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      /** Constructor. */
46      private XMLObjectHelper() { }
47      
48      /**
49       * Clone an XMLObject by brute force:
50       * 
51       * <p>
52       * 1) Marshall the original object if necessary
53       * 2) Clone the resulting DOM Element
54       * 3) Unmarshall a new XMLObject tree around it.
55       * </p>
56       * 
57       * <p>
58       * This method variant is equivalent to <code>cloneXMLObject(originalXMLObject, false).</code>
59       * </p>
60       * 
61       * 
62       * @param originalXMLObject the object to be cloned
63       * @return a clone of the original object
64       * 
65       * @throws MarshallingException if original object can not be marshalled
66       * @throws UnmarshallingException if cloned object tree can not be unmarshalled
67       * 
68       * @param <T> the type of object being cloned
69       */
70      public static <T extends XMLObject> T cloneXMLObject(T originalXMLObject)
71              throws MarshallingException, UnmarshallingException {
72          return cloneXMLObject(originalXMLObject, false);
73      }
74      
75      /**
76       * Clone an XMLObject by brute force:
77       * 
78       * <p>
79       * 1) Marshall the original object if necessary
80       * 2) Clone the resulting DOM Element
81       * 3) Unmarshall a new XMLObject tree around it.
82       * </p>
83       * 
84       * @param originalXMLObject the object to be cloned
85       * @param rootInNewDocument if true the cloned object's cached DOM will be rooted
86       *          in a new Document; if false, the original object's underlying DOM is cloned,
87       *          but the cloned copy remains unrooted and owned by the original Document
88       * @return a clone of the original object
89       * 
90       * @throws MarshallingException if original object can not be marshalled
91       * @throws UnmarshallingException if cloned object tree can not be unmarshalled
92       * 
93       * @param <T> the type of object being cloned
94       */
95      public static <T extends XMLObject> T cloneXMLObject(T originalXMLObject, boolean rootInNewDocument)
96              throws MarshallingException, UnmarshallingException {
97          
98          if (originalXMLObject == null) {
99              return null;
100         }
101         
102         Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(originalXMLObject);
103         Element origElement = marshaller.marshall(originalXMLObject);
104         
105         Element clonedElement = null;
106         
107         if (rootInNewDocument) {
108             try {
109                 Document newDocument = Configuration.getParserPool().newDocument();
110                 // Note: importNode copies the node tree and does not modify the source document
111                 clonedElement = (Element) newDocument.importNode(origElement, true);
112                 newDocument.appendChild(clonedElement);
113             } catch (XMLParserException e) {
114                 throw new XMLRuntimeException("Error obtaining new Document from parser pool", e);
115             }
116         } else {
117             clonedElement = (Element) origElement.cloneNode(true);
118         }
119         
120         Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(clonedElement);
121         T clonedXMLObject = (T) unmarshaller.unmarshall(clonedElement);
122         
123         return clonedXMLObject;
124     }
125     
126     /**
127      * Unmarshall a Document from an InputSteam.
128      * 
129      * @param parserPool the ParserPool instance to use
130      * @param inputStream the InputStream to unmarshall
131      * @return the unmarshalled XMLObject
132      * @throws XMLParserException if there is a problem parsing the input data
133      * @throws UnmarshallingException if there is a problem unmarshalling the parsed DOM
134      */
135     public static XMLObject unmarshallFromInputStream(ParserPool parserPool, InputStream inputStream)
136             throws XMLParserException, UnmarshallingException {
137         Logger log = getLogger();
138         log.debug("Parsing InputStream into DOM document");
139 
140         Document messageDoc = parserPool.parse(inputStream);
141         Element messageElem = messageDoc.getDocumentElement();
142 
143         if (log.isTraceEnabled()) {
144             log.trace("Resultant DOM message was:");
145             log.trace(XMLHelper.nodeToString(messageElem));
146         }
147 
148         log.debug("Unmarshalling DOM parsed from InputStream");
149         Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(messageElem);
150         if (unmarshaller == null) {
151             log.error("Unable to unmarshall InputStream, no unmarshaller registered for element "
152                     + XMLHelper.getNodeQName(messageElem));
153             throw new UnmarshallingException(
154                     "Unable to unmarshall InputStream, no unmarshaller registered for element "
155                             + XMLHelper.getNodeQName(messageElem));
156         }
157 
158         XMLObject message = unmarshaller.unmarshall(messageElem);
159 
160         log.debug("InputStream succesfully unmarshalled");
161         return message;
162     }
163     
164     /**
165      * Unmarshall a Document from a Reader.
166      * 
167      * @param parserPool the ParserPool instance to use
168      * @param reader the Reader to unmarshall
169      * @return the unmarshalled XMLObject
170      * @throws XMLParserException if there is a problem parsing the input data
171      * @throws UnmarshallingException if there is a problem unmarshalling the parsed DOM
172      */
173     public static XMLObject unmarshallFromReader(ParserPool parserPool, Reader reader)
174             throws XMLParserException, UnmarshallingException {
175         Logger log = getLogger();
176         log.debug("Parsing Reader into DOM document");
177         
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         Logger log = getLogger();
213         log.debug("Marshalling XMLObject");
214         
215         if (xmlObject.getDOM() != null) {
216             log.debug("XMLObject already had cached DOM, returning that element");
217             return xmlObject.getDOM();
218         }
219 
220         Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(xmlObject);
221         if (marshaller == null) {
222             log.error("Unable to marshall XMLOBject, no marshaller registered for object: "
223                     + xmlObject.getElementQName());
224         }
225         
226         Element messageElem = marshaller.marshall(xmlObject);
227         
228         if (log.isTraceEnabled()) {
229             log.trace("Marshalled XMLObject into DOM:");
230             log.trace(XMLHelper.nodeToString(messageElem));
231         }
232         
233         return messageElem;
234     }
235     
236     /**
237      * Marshall an XMLObject to an OutputStream.
238      * 
239      * @param xmlObject the XMLObject to marshall
240      * @param outputStream the OutputStream to which to marshall
241      * @throws MarshallingException if there is a problem marshalling the object
242      */
243     public static void marshallToOutputStream(XMLObject xmlObject, OutputStream outputStream) 
244             throws MarshallingException {
245         Element element = marshall(xmlObject);
246         XMLHelper.writeNode(element, outputStream);
247     }
248     
249     /**
250      * Marshall an XMLObject to a Writer.
251      * 
252      * @param xmlObject the XMLObject to marshall
253      * @param writer the Writer to which to marshall
254      * @throws MarshallingException if there is a problem marshalling the object
255      */
256     public static void marshallToWriter(XMLObject xmlObject, Writer writer) throws MarshallingException {
257         Element element = marshall(xmlObject);
258         XMLHelper.writeNode(element, writer);
259     }
260     
261     /**
262      * Get the namespace URI bound to the specified prefix within the scope of the specified
263      * XMLObject.
264      *
265      * @param xmlObject the XMLObject from which to search
266      * @param prefix the prefix to search
267      * @return the namespace URI bound to the prefix, or none if not found
268      */
269     public static String lookupNamespaceURI(XMLObject xmlObject, String prefix) {
270         XMLObject current = xmlObject;
271         
272         while (current != null) {
273             for (Namespace ns : current.getNamespaces()) {
274                 if (DatatypeHelper.safeEquals(ns.getNamespacePrefix(), prefix)) {
275                     return ns.getNamespaceURI();
276                 }
277             }
278             current = current.getParent();
279         }
280         
281         return null;
282     }
283     
284     /**
285      * Get the prefix bound to the specified namespace URI within the scope of the specified
286      * XMLObject.
287      *
288      * @param xmlObject the XMLObject from which to search
289      * @param namespaceURI the namespace URI to search
290      * @return the prefix bound to the namespace URI, or none if not found
291      */
292     public static String lookupNamespacePrefix(XMLObject xmlObject, String namespaceURI) {
293         XMLObject current = xmlObject;
294         
295         while (current != null) {
296             for (Namespace ns : current.getNamespaces()) {
297                 if (DatatypeHelper.safeEquals(ns.getNamespaceURI(), namespaceURI)) {
298                     return ns.getNamespacePrefix();
299                 }
300             }
301             current = current.getParent();
302         }
303         
304         return null;
305     }
306     
307     /**
308      * Get an SLF4J Logger.
309      * 
310      * @return a Logger instance
311      */
312     private static Logger getLogger() {
313         return LoggerFactory.getLogger(XMLObjectHelper.class);
314     }
315     
316 }