View Javadoc

1   /*
2    * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.xml.util;
18  
19  import java.io.InputStream;
20  import java.io.OutputStream;
21  import java.io.Reader;
22  import java.io.Writer;
23  
24  import org.opensaml.xml.Configuration;
25  import org.opensaml.xml.Namespace;
26  import org.opensaml.xml.XMLObject;
27  import org.opensaml.xml.XMLRuntimeException;
28  import org.opensaml.xml.io.Marshaller;
29  import org.opensaml.xml.io.MarshallingException;
30  import org.opensaml.xml.io.Unmarshaller;
31  import org.opensaml.xml.io.UnmarshallingException;
32  import org.opensaml.xml.parse.ParserPool;
33  import org.opensaml.xml.parse.XMLParserException;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  import org.w3c.dom.Document;
37  import org.w3c.dom.Element;
38  
39  
40  /**
41   * A helper class for working with XMLObjects.
42   */
43  public final class XMLObjectHelper {
44      
45      /** Class logger. */
46      private static final Logger LOG = LoggerFactory.getLogger(XMLObjectHelper.class);
47  
48      /** Constructor. */
49      private XMLObjectHelper() { }
50      
51      /**
52       * Clone an XMLObject by brute force:
53       * 
54       * <p>
55       * 1) Marshall the original object if necessary
56       * 2) Clone the resulting DOM Element
57       * 3) Unmarshall a new XMLObject tree around it.
58       * </p>
59       * 
60       * <p>
61       * This method variant is equivalent to <code>cloneXMLObject(originalXMLObject, false).</code>
62       * </p>
63       * 
64       * 
65       * @param originalXMLObject the object to be cloned
66       * @return a clone of the original object
67       * 
68       * @throws MarshallingException if original object can not be marshalled
69       * @throws UnmarshallingException if cloned object tree can not be unmarshalled
70       * 
71       * @param <T> the type of object being cloned
72       */
73      public static <T extends XMLObject> T cloneXMLObject(T originalXMLObject)
74              throws MarshallingException, UnmarshallingException {
75          return cloneXMLObject(originalXMLObject, false);
76      }
77      
78      /**
79       * Clone an XMLObject by brute force:
80       * 
81       * <p>
82       * 1) Marshall the original object if necessary
83       * 2) Clone the resulting DOM Element
84       * 3) Unmarshall a new XMLObject tree around it.
85       * </p>
86       * 
87       * @param originalXMLObject the object to be cloned
88       * @param rootInNewDocument if true the cloned object's cached DOM will be rooted
89       *          in a new Document; if false, the original object's underlying DOM is cloned,
90       *          but the cloned copy remains unrooted and owned by the original Document
91       * @return a clone of the original object
92       * 
93       * @throws MarshallingException if original object can not be marshalled
94       * @throws UnmarshallingException if cloned object tree can not be unmarshalled
95       * 
96       * @param <T> the type of object being cloned
97       */
98      public static <T extends XMLObject> T cloneXMLObject(T originalXMLObject, boolean rootInNewDocument)
99              throws MarshallingException, UnmarshallingException {
100         
101         if (originalXMLObject == null) {
102             return null;
103         }
104         
105         Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(originalXMLObject);
106         Element origElement = marshaller.marshall(originalXMLObject);
107         
108         Element clonedElement = null;
109         
110         if (rootInNewDocument) {
111             try {
112                 Document newDocument = Configuration.getParserPool().newDocument();
113                 // Note: importNode copies the node tree and does not modify the source document
114                 clonedElement = (Element) newDocument.importNode(origElement, true);
115                 newDocument.appendChild(clonedElement);
116             } catch (XMLParserException e) {
117                 throw new XMLRuntimeException("Error obtaining new Document from parser pool", e);
118             }
119         } else {
120             clonedElement = (Element) origElement.cloneNode(true);
121         }
122         
123         Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(clonedElement);
124         T clonedXMLObject = (T) unmarshaller.unmarshall(clonedElement);
125         
126         return clonedXMLObject;
127     }
128     
129     /**
130      * Unmarshall a Document from an InputSteam.
131      * 
132      * @param parserPool the ParserPool instance to use
133      * @param inputStream the InputStream to unmarshall
134      * @return the unmarshalled XMLObject
135      * @throws XMLParserException if there is a problem parsing the input data
136      * @throws UnmarshallingException if there is a problem unmarshalling the parsed DOM
137      */
138     public static XMLObject unmarshallFromInputStream(ParserPool parserPool, InputStream inputStream)
139             throws XMLParserException, UnmarshallingException {
140         LOG.debug("Parsing InputStream into DOM document");
141 
142         Document messageDoc = parserPool.parse(inputStream);
143         Element messageElem = messageDoc.getDocumentElement();
144 
145         if (LOG.isTraceEnabled()) {
146             LOG.trace("Resultant DOM message was:");
147             LOG.trace(XMLHelper.nodeToString(messageElem));
148         }
149 
150         LOG.debug("Unmarshalling DOM parsed from InputStream");
151         Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(messageElem);
152         if (unmarshaller == null) {
153             LOG.error("Unable to unmarshall InputStream, no unmarshaller registered for element "
154                     + XMLHelper.getNodeQName(messageElem));
155             throw new UnmarshallingException(
156                     "Unable to unmarshall InputStream, no unmarshaller registered for element "
157                             + XMLHelper.getNodeQName(messageElem));
158         }
159 
160         XMLObject message = unmarshaller.unmarshall(messageElem);
161 
162         LOG.debug("InputStream succesfully unmarshalled");
163         return message;
164     }
165     
166     /**
167      * Unmarshall a Document from a Reader.
168      * 
169      * @param parserPool the ParserPool instance to use
170      * @param reader the Reader to unmarshall
171      * @return the unmarshalled XMLObject
172      * @throws XMLParserException if there is a problem parsing the input data
173      * @throws UnmarshallingException if there is a problem unmarshalling the parsed DOM
174      */
175     public static XMLObject unmarshallFromReader(ParserPool parserPool, Reader reader)
176             throws XMLParserException, UnmarshallingException {
177         LOG.debug("Parsing Reader into DOM document");
178 
179         Document messageDoc = parserPool.parse(reader);
180         Element messageElem = messageDoc.getDocumentElement();
181 
182         if (LOG.isTraceEnabled()) {
183             LOG.trace("Resultant DOM message was:");
184             LOG.trace(XMLHelper.nodeToString(messageElem));
185         }
186 
187         LOG.debug("Unmarshalling DOM parsed from Reader");
188         Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(messageElem);
189         if (unmarshaller == null) {
190             LOG.error("Unable to unmarshall Reader, no unmarshaller registered for element "
191                     + XMLHelper.getNodeQName(messageElem));
192             throw new UnmarshallingException(
193                     "Unable to unmarshall Reader, no unmarshaller registered for element "
194                             + XMLHelper.getNodeQName(messageElem));
195         }
196 
197         XMLObject message = unmarshaller.unmarshall(messageElem);
198 
199         LOG.debug("Reader succesfully unmarshalled");
200         return message;
201     }
202 
203     /**
204      * Marshall an XMLObject.  If the XMLObject already has a cached DOM via {@link XMLObject#getDOM()},
205      * that Element will be returned.  Otherwise the object will be fully marshalled and that Element returned.
206      * 
207      * @param xmlObject the XMLObject to marshall
208      * @return the marshalled Element
209      * @throws MarshallingException if there is a problem marshalling the XMLObject
210      */
211     public static Element marshall(XMLObject xmlObject) throws MarshallingException {
212         LOG.debug("Marshalling XMLObject");
213         
214         if (xmlObject.getDOM() != null) {
215             LOG.debug("XMLObject already had cached DOM, returning that element");
216             return xmlObject.getDOM();
217         }
218 
219         Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(xmlObject);
220         if (marshaller == null) {
221             LOG.error("Unable to marshall XMLOBject, no marshaller registered for object: "
222                     + xmlObject.getElementQName());
223         }
224         
225         Element messageElem = marshaller.marshall(xmlObject);
226         
227         if (LOG.isTraceEnabled()) {
228             LOG.trace("Marshalled XMLObject into DOM:");
229             LOG.trace(XMLHelper.nodeToString(messageElem));
230         }
231         
232         return messageElem;
233     }
234     
235     /**
236      * Marshall an XMLObject to an OutputStream.
237      * 
238      * @param xmlObject the XMLObject to marshall
239      * @param outputStream the OutputStream to which to marshall
240      * @throws MarshallingException if there is a problem marshalling the object
241      */
242     public static void marshallToOutputStream(XMLObject xmlObject, OutputStream outputStream) 
243             throws MarshallingException {
244         Element element = marshall(xmlObject);
245         XMLHelper.writeNode(element, outputStream);
246     }
247     
248     /**
249      * Marshall an XMLObject to a Writer.
250      * 
251      * @param xmlObject the XMLObject to marshall
252      * @param writer the Writer to which to marshall
253      * @throws MarshallingException if there is a problem marshalling the object
254      */
255     public static void marshallToWriter(XMLObject xmlObject, Writer writer) throws MarshallingException {
256         Element element = marshall(xmlObject);
257         XMLHelper.writeNode(element, writer);
258     }
259     
260     /**
261      * Get the namespace URI bound to the specified prefix within the scope of the specified
262      * XMLObject.
263      *
264      * @param xmlObject the XMLObject from which to search
265      * @param prefix the prefix to search
266      * @return the namespace URI bound to the prefix, or none if not found
267      */
268     public static String lookupNamespaceURI(XMLObject xmlObject, String prefix) {
269         XMLObject current = xmlObject;
270         
271         while (current != null) {
272             for (Namespace ns : current.getNamespaces()) {
273                 if (DatatypeHelper.safeEquals(ns.getNamespacePrefix(), prefix)) {
274                     return ns.getNamespaceURI();
275                 }
276             }
277             current = current.getParent();
278         }
279         
280         return null;
281     }
282     
283     /**
284      * Get the prefix bound to the specified namespace URI within the scope of the specified
285      * XMLObject.
286      *
287      * @param xmlObject the XMLObject from which to search
288      * @param namespaceURI the namespace URI to search
289      * @return the prefix bound to the namespace URI, or none if not found
290      */
291     public static String lookupNamespacePrefix(XMLObject xmlObject, String namespaceURI) {
292         XMLObject current = xmlObject;
293         
294         while (current != null) {
295             for (Namespace ns : current.getNamespaces()) {
296                 if (DatatypeHelper.safeEquals(ns.getNamespaceURI(), namespaceURI)) {
297                     return ns.getNamespacePrefix();
298                 }
299             }
300             current = current.getParent();
301         }
302         
303         return null;
304     }
305     
306 }