001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: Extension.java,v 1.2 2008/06/25 05:46:46 qcheng Exp $
026 * Portions Copyrighted 2014 ForgeRock AS
027 */
028
029package com.sun.identity.federation.message.common;
030
031import static org.forgerock.http.util.Uris.urlEncodeQueryParameterNameOrValue;
032
033import com.sun.identity.federation.common.FSUtils;
034import com.sun.identity.federation.common.IFSConstants;
035import com.sun.identity.shared.xml.XMLUtils;
036import java.util.ArrayList;
037import java.util.Enumeration;
038import java.util.HashMap;
039import java.util.Iterator;
040import java.util.List;
041import java.util.Map;
042import javax.servlet.http.HttpServletRequest;
043import org.w3c.dom.Document;
044import org.w3c.dom.Element;
045import org.w3c.dom.Node;
046import org.w3c.dom.NodeList;
047
048/**
049 * The class <code>Extension</code> is used to create , parse
050 * <code>Extension</code> object.
051 *
052 * @supported.all.api
053 * @deprecated since 12.0.0
054 */
055@Deprecated
056public class Extension {
057    private List children = null;
058    private Map avpairs = null;
059    private int minorVersion = 0;
060
061    /**
062     * Constructor to create <code>Extension</code> object.
063     *
064     * @param children a list of XML <code>String</code> object.
065     * @throws FSMsgException on error.
066     */
067    public Extension(List children) throws FSMsgException {
068        validateChildren(children);
069        this.children = children;
070    }
071
072    /**
073     * Constructor to create <code>Extension</code> object.
074     *
075     * @param element the <code>Extension</code> Element object.
076     * @throws FSMsgException on error.
077     */
078    public Extension(Element element) throws FSMsgException {
079        if (element == null) {
080            FSUtils.debug.error("Extension.Extension: null input.");
081            throw new FSMsgException("nullInput", null);
082        }
083        String nodeName = element.getLocalName();
084        if (!IFSConstants.EXTENSION.equals(nodeName)) {
085            FSUtils.debug.error("Extension.Extension: wrong input");
086            throw new FSMsgException("wrongInput", null);
087        }
088             
089        NodeList childNodes = element.getChildNodes();
090        int length = childNodes.getLength();
091        for(int i = 0; i < length; i++) {
092            Node child = childNodes.item(i);
093            if (child.getNodeType() == Node.ELEMENT_NODE) {
094                if (children == null) {
095                    children = new ArrayList();
096                }
097                children.add(XMLUtils.print(child));
098                addToAvpairs((Element)child);
099            }
100        }
101    }
102
103    /**
104     * Constructor to create <code>Extension</code> object. Each attribute
105     * value pair will be converted to a XML string. The converted XML string
106     * has only one element. The local name of the element will be the key of
107     * the map entry and the value of the element will be the value of the map
108     * entry. Both key and value of the map entry should be a
109     * <code>String</code> object. 
110     *
111     * @param avpairs attribute value pairs.
112     * @throws FSMsgException on error.
113     */
114    public Extension(Map avpairs) throws FSMsgException {
115        setAttributeMap(avpairs);
116    }
117
118    /**
119     * Returns a list of XML <code>String</code> objects.
120     *
121     * @return a list of XML <code>String</code> objects.
122     * @see #setChildren(List)
123     */
124    public List getChildren() {
125        return children;
126    }
127
128    /**
129     * Sets a list of XML <code>String</code> object.
130     *
131     * @param children a list of XML <code>String</code> object.
132     * @see #getChildren()
133     */
134    public void setChildren(List children) throws FSMsgException {
135        validateChildren(children);
136        this.children = children;
137    }
138
139    /*
140     * Gets attribute value pairs. Each attribute value pair is converted from
141     * a XML string. The XML string can have only one element. The element
142     * element can't have namespace and must have a simple content. The local
143     * name of the element will be the key of the map entry and the value of
144     * the element will be the value of the map entry. Both key and value of
145     * the map entry will be a <code>String</code> object. If a XML string
146     * can't be converted, it will not be added to the map.
147     * 
148     * @return an attribute value pairs.
149     */
150    public Map getAttributeMap() {
151        if ((children == null) || (children.isEmpty())) {
152            return null;
153        }
154
155        if (avpairs != null) {
156            return avpairs;
157        }
158        for(Iterator iter = children.iterator(); iter.hasNext(); ) {
159            String xml = (String)iter.next();
160
161            Document doc = XMLUtils.toDOMDocument(xml, FSUtils.debug);
162            if (doc == null) {
163                continue;
164            }
165            Element element = doc.getDocumentElement();
166            addToAvpairs(element);
167        }
168
169        return avpairs;
170    }
171
172    /**
173     * Converts attribute value pairs to a list of XML <code>String</code>
174     * objects. Each attribute value pair will be converted to a XML string.
175     * The converted XML string has only one element. The local name of the
176     * element will be the key of the map entry and the value of the element
177     * will be the value of the map entry. Both key and value of the map entry
178     * should be a <code>String</code> object. 
179     *
180     * @param avpairs attribute value pairs.
181     * @throws FSMsgException on error.
182     */
183    public void setAttributeMap(Map avpairs) throws FSMsgException {
184        this.avpairs = avpairs;
185        if ((avpairs != null) && (!avpairs.isEmpty())) {
186            for(Iterator iter = avpairs.keySet().iterator(); iter.hasNext();) {
187                String key = (String)iter.next();
188                String value = (String)avpairs.get(key);
189                String xml = IFSConstants.LEFT_ANGLE + key +
190                             IFSConstants.RIGHT_ANGLE +
191                             XMLUtils.escapeSpecialCharacters(value) +
192                             IFSConstants.START_END_ELEMENT + key +
193                             IFSConstants.RIGHT_ANGLE;
194                if (children == null) {
195                    children = new ArrayList();
196                }
197                children.add(xml);
198            }
199        }
200    }
201
202    /**
203     * Returns the <code>MinorVersion</code>.
204     *
205     * @return the <code>MinorVersion</code>.
206     * @see #setMinorVersion(int)
207     */
208    public int getMinorVersion() {
209       return minorVersion;
210    }
211
212    /**
213     * Sets the <code>MinorVersion</code>.
214     *
215     * @param minorVersion the <code>MinorVersion</code>.
216     * @see #getMinorVersion()
217     */
218    public void setMinorVersion(int minorVersion) {
219        this.minorVersion = minorVersion;
220    }
221
222    /**
223     * Returns a String representation of the <Code>Extension</Code> element.
224     *
225     * @return a string containing the valid XML for this element
226     * @throws FSMsgException if there is an error converting this object to
227     *     a string.
228     */
229    public String toXMLString() throws FSMsgException {
230        return this.toXMLString(true, false);
231    }
232
233    /**
234     * Creates a String representation of the <Code>Extension</Code> element.
235     *
236     * @param includeNS : Determines whether or not the namespace qualifier
237     *     is prepended to the Element when converted
238     * @param declareNS : Determines whether or not the namespace is declared
239     *     within the Element.
240     * @return string containing the valid XML for this element.
241     * @throws FSMsgException if there is an error.
242     */
243    public String toXMLString(boolean includeNS, boolean declareNS)
244        throws FSMsgException {
245
246        String prefix = "";
247        String uri = "";
248
249        StringBuffer xml = new StringBuffer();
250        if (includeNS) {
251            prefix = IFSConstants.LIB_PREFIX;
252        }
253        if (declareNS) {
254            if(minorVersion == IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) {
255                uri = IFSConstants.LIB_12_NAMESPACE_STRING;
256            } else {
257                uri = IFSConstants.LIB_NAMESPACE_STRING;
258            }
259        }
260        xml.append(IFSConstants.LEFT_ANGLE)
261           .append(prefix)
262           .append(IFSConstants.EXTENSION)
263           .append(uri)
264           .append(IFSConstants.RIGHT_ANGLE);
265        if ((children != null) && (!children.isEmpty())) {
266            for(Iterator iter = children.iterator(); iter.hasNext();) {
267                String child = (String)iter.next();
268                xml.append(child);
269            }
270        }
271        xml.append(IFSConstants.START_END_ELEMENT)
272           .append(prefix)
273           .append(IFSConstants.EXTENSION)
274           .append(IFSConstants.RIGHT_ANGLE);
275
276        return xml.toString();
277    }
278
279    /**
280     * Returns <code>Extension</code> object. The object is creating by
281     * parsing the <code>HttpServletRequest</code> object.
282     *
283     * @param request the <code>HttpServletRequest</code> object.
284     * @param prefix the string that is prepended to the key of query
285     *     string.
286     * @param minorVersion the <code>MinorVersion</code>.
287     * @return <code><Extension/code> object.
288     * @throws FSMsgException if there is an error creating
289     *     <code>Extension</code> object.
290     */
291    public static Extension parseURLEncodedRequest(HttpServletRequest request,
292        String prefix, int minorVersion) throws FSMsgException {
293
294        Map attrMap = null;
295        for(Enumeration e=request.getParameterNames(); e.hasMoreElements();) {
296            String paraName = (String)e.nextElement();
297            if (paraName.startsWith(prefix)) {
298                String key = paraName.substring(prefix.length());
299                String value = request.getParameter(paraName);
300                if (attrMap == null) {
301                    attrMap = new HashMap();
302                }
303                attrMap.put(key, value);
304            }
305        }
306
307        if (attrMap == null) {
308            return null;
309        }
310
311        Extension extension = new Extension(attrMap);
312        extension.setMinorVersion(minorVersion);
313        return extension;        
314    }
315      
316    /**
317     * Returns an URL Encoded String.
318     *
319     * @param prefix the string that will be prepended to the key of query
320     *     string.
321     * @return a url encoded query string.
322     * @throws FSMsgException if there is an error.
323     */
324    public String toURLEncodedQueryString(String prefix)
325        throws FSMsgException {
326
327        Map attrMap = getAttributeMap();
328        if ((attrMap == null) || (attrMap.isEmpty())) {
329            return "";
330        }
331
332        StringBuffer queryString = new StringBuffer();
333        for(Iterator iter = attrMap.keySet().iterator();iter.hasNext();) {
334            String key = (String)iter.next();
335            String value = urlEncodeQueryParameterNameOrValue((String)attrMap.get(key));
336            key = urlEncodeQueryParameterNameOrValue(prefix + key);
337            if (queryString.length() > 0) {
338                queryString.append(IFSConstants.AMPERSAND);
339            }
340            queryString.append(key).append(IFSConstants.EQUAL_TO)
341                       .append(value);
342        }
343
344        return queryString.toString();
345    }
346
347    private void addToAvpairs(Element element) {
348        String ns = element.getNamespaceURI();
349        if ((ns == null) && (!XMLUtils.hasElementChild(element))) {
350            String key = element.getLocalName();
351            String value = XMLUtils.getElementValue(element);
352            if (avpairs == null) {
353                avpairs = new HashMap();
354            }
355            avpairs.put(key, value);
356       }
357    }
358
359    private static void validateChildren(List children) throws FSMsgException {
360        if ((children != null) && (!children.isEmpty())) {
361            for(Iterator iter = children.iterator(); iter.hasNext(); ) {
362                String xml = (String)iter.next();
363                Document doc = XMLUtils.toDOMDocument(xml, FSUtils.debug);
364                if (doc == null) {
365                    FSUtils.debug.error("Extension.validateChildren: Error "
366                        + "while parsing input xml string");
367                    throw new FSMsgException("parseError", null);
368                }
369            }
370        }
371    }
372}