001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.xml;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
020    import com.liferay.portal.kernel.xml.Attribute;
021    import com.liferay.portal.kernel.xml.Document;
022    import com.liferay.portal.kernel.xml.DocumentException;
023    import com.liferay.portal.kernel.xml.Element;
024    import com.liferay.portal.kernel.xml.Entity;
025    import com.liferay.portal.kernel.xml.Namespace;
026    import com.liferay.portal.kernel.xml.Node;
027    import com.liferay.portal.kernel.xml.ProcessingInstruction;
028    import com.liferay.portal.kernel.xml.QName;
029    import com.liferay.portal.kernel.xml.SAXReader;
030    import com.liferay.portal.kernel.xml.Text;
031    import com.liferay.portal.kernel.xml.XMLSchema;
032    import com.liferay.portal.kernel.xml.XPath;
033    import com.liferay.portal.security.xml.SecureXMLFactoryProvider;
034    import com.liferay.portal.security.xml.SecureXMLFactoryProviderImpl;
035    import com.liferay.portal.util.ClassLoaderUtil;
036    import com.liferay.portal.util.EntityResolver;
037    import com.liferay.portal.util.PropsValues;
038    import com.liferay.util.xml.XMLSafeReader;
039    
040    import java.io.File;
041    import java.io.InputStream;
042    import java.io.Reader;
043    
044    import java.net.MalformedURLException;
045    import java.net.URL;
046    
047    import java.util.ArrayList;
048    import java.util.HashMap;
049    import java.util.List;
050    import java.util.Map;
051    
052    import org.dom4j.DocumentFactory;
053    import org.dom4j.DocumentHelper;
054    
055    /**
056     * @author Brian Wing Shun Chan
057     */
058    @DoPrivileged
059    public class SAXReaderImpl implements SAXReader {
060    
061            /**
062             * @deprecated As of 6.2.0, with no direct replacement
063             */
064            public static SAXReaderImpl getInstance() {
065                    return new SAXReaderImpl();
066            }
067    
068            public static List<Attribute> toNewAttributes(
069                    List<org.dom4j.Attribute> oldAttributes) {
070    
071                    List<Attribute> newAttributes = new ArrayList<Attribute>(
072                            oldAttributes.size());
073    
074                    for (org.dom4j.Attribute oldAttribute : oldAttributes) {
075                            newAttributes.add(new AttributeImpl(oldAttribute));
076                    }
077    
078                    return new NodeList<Attribute, org.dom4j.Attribute>(
079                            newAttributes, oldAttributes);
080            }
081    
082            public static List<Element> toNewElements(
083                    List<org.dom4j.Element> oldElements) {
084    
085                    List<Element> newElements = new ArrayList<Element>(oldElements.size());
086    
087                    for (org.dom4j.Element oldElement : oldElements) {
088                            newElements.add(new ElementImpl(oldElement));
089                    }
090    
091                    return new NodeList<Element, org.dom4j.Element>(
092                            newElements, oldElements);
093            }
094    
095            public static List<Namespace> toNewNamespaces(
096                    List<org.dom4j.Namespace> oldNamespaces) {
097    
098                    List<Namespace> newNamespaces = new ArrayList<Namespace>(
099                            oldNamespaces.size());
100    
101                    for (org.dom4j.Namespace oldNamespace : oldNamespaces) {
102                            newNamespaces.add(new NamespaceImpl(oldNamespace));
103                    }
104    
105                    return new NodeList<Namespace, org.dom4j.Namespace>(
106                            newNamespaces, oldNamespaces);
107            }
108    
109            public static List<Node> toNewNodes(List<org.dom4j.Node> oldNodes) {
110                    List<Node> newNodes = new ArrayList<Node>(oldNodes.size());
111    
112                    for (org.dom4j.Node oldNode : oldNodes) {
113                            if (oldNode instanceof org.dom4j.Element) {
114                                    newNodes.add(new ElementImpl((org.dom4j.Element)oldNode));
115                            }
116                            else {
117                                    newNodes.add(new NodeImpl(oldNode));
118                            }
119                    }
120    
121                    return new NodeList<Node, org.dom4j.Node>(newNodes, oldNodes);
122            }
123    
124            public static List<ProcessingInstruction> toNewProcessingInstructions(
125                    List<org.dom4j.ProcessingInstruction> oldProcessingInstructions) {
126    
127                    List<ProcessingInstruction> newProcessingInstructions =
128                            new ArrayList<ProcessingInstruction>(
129                                    oldProcessingInstructions.size());
130    
131                    for (org.dom4j.ProcessingInstruction oldProcessingInstruction :
132                                    oldProcessingInstructions) {
133    
134                            newProcessingInstructions.add(
135                                    new ProcessingInstructionImpl(oldProcessingInstruction));
136                    }
137    
138                    return new NodeList
139                            <ProcessingInstruction, org.dom4j.ProcessingInstruction>(
140                                    newProcessingInstructions, oldProcessingInstructions);
141            }
142    
143            public static List<org.dom4j.Attribute> toOldAttributes(
144                    List<Attribute> newAttributes) {
145    
146                    List<org.dom4j.Attribute> oldAttributes =
147                            new ArrayList<org.dom4j.Attribute>(newAttributes.size());
148    
149                    for (Attribute newAttribute : newAttributes) {
150                            AttributeImpl newAttributeImpl = (AttributeImpl)newAttribute;
151    
152                            oldAttributes.add(newAttributeImpl.getWrappedAttribute());
153                    }
154    
155                    return oldAttributes;
156            }
157    
158            public static List<org.dom4j.Node> toOldNodes(List<Node> newNodes) {
159                    List<org.dom4j.Node> oldNodes = new ArrayList<org.dom4j.Node>(
160                            newNodes.size());
161    
162                    for (Node newNode : newNodes) {
163                            NodeImpl newNodeImpl = (NodeImpl)newNode;
164    
165                            oldNodes.add(newNodeImpl.getWrappedNode());
166                    }
167    
168                    return oldNodes;
169            }
170    
171            public static List<org.dom4j.ProcessingInstruction>
172                    toOldProcessingInstructions(
173                            List<ProcessingInstruction> newProcessingInstructions) {
174    
175                    List<org.dom4j.ProcessingInstruction> oldProcessingInstructions =
176                            new ArrayList<org.dom4j.ProcessingInstruction>(
177                                    newProcessingInstructions.size());
178    
179                    for (ProcessingInstruction newProcessingInstruction :
180                                    newProcessingInstructions) {
181    
182                            ProcessingInstructionImpl newProcessingInstructionImpl =
183                                    (ProcessingInstructionImpl)newProcessingInstruction;
184    
185                            oldProcessingInstructions.add(
186                                    newProcessingInstructionImpl.getWrappedProcessingInstruction());
187                    }
188    
189                    return oldProcessingInstructions;
190            }
191    
192            @Override
193            public Attribute createAttribute(
194                    Element element, QName qName, String value) {
195    
196                    ElementImpl elementImpl = (ElementImpl)element;
197                    QNameImpl qNameImpl = (QNameImpl)qName;
198    
199                    DocumentFactory documentFactory = DocumentFactory.getInstance();
200    
201                    return new AttributeImpl(
202                            documentFactory.createAttribute(
203                                    elementImpl.getWrappedElement(), qNameImpl.getWrappedQName(),
204                                    value));
205            }
206    
207            @Override
208            public Attribute createAttribute(
209                    Element element, String name, String value) {
210    
211                    ElementImpl elementImpl = (ElementImpl)element;
212    
213                    DocumentFactory documentFactory = DocumentFactory.getInstance();
214    
215                    return new AttributeImpl(
216                            documentFactory.createAttribute(
217                                    elementImpl.getWrappedElement(), name, value));
218            }
219    
220            @Override
221            public Document createDocument() {
222                    return new DocumentImpl(DocumentHelper.createDocument());
223            }
224    
225            @Override
226            public Document createDocument(Element rootElement) {
227                    ElementImpl rootElementImpl = (ElementImpl)rootElement;
228    
229                    return new DocumentImpl(
230                            DocumentHelper.createDocument(rootElementImpl.getWrappedElement()));
231            }
232    
233            @Override
234            public Document createDocument(String encoding) {
235                    DocumentFactory documentFactory = DocumentFactory.getInstance();
236    
237                    return new DocumentImpl(documentFactory.createDocument(encoding));
238            }
239    
240            @Override
241            public Element createElement(QName qName) {
242                    QNameImpl qNameImpl = (QNameImpl)qName;
243    
244                    return new ElementImpl(
245                            DocumentHelper.createElement(qNameImpl.getWrappedQName()));
246            }
247    
248            @Override
249            public Element createElement(String name) {
250                    return new ElementImpl(DocumentHelper.createElement(name));
251            }
252    
253            @Override
254            public Entity createEntity(String name, String text) {
255                    return new EntityImpl(DocumentHelper.createEntity(name, text));
256            }
257    
258            @Override
259            public Namespace createNamespace(String uri) {
260                    return new NamespaceImpl(org.dom4j.Namespace.get(uri));
261            }
262    
263            @Override
264            public Namespace createNamespace(String prefix, String uri) {
265                    return new NamespaceImpl(DocumentHelper.createNamespace(prefix, uri));
266            }
267    
268            @Override
269            public ProcessingInstruction createProcessingInstruction(
270                    String target, Map<String, String> data) {
271    
272                    org.dom4j.ProcessingInstruction processingInstruction =
273                            DocumentHelper.createProcessingInstruction(target, data);
274    
275                    if (processingInstruction == null) {
276                            return null;
277                    }
278                    else {
279                            return new ProcessingInstructionImpl(processingInstruction);
280                    }
281            }
282    
283            @Override
284            public ProcessingInstruction createProcessingInstruction(
285                    String target, String data) {
286    
287                    org.dom4j.ProcessingInstruction processingInstruction =
288                            DocumentHelper.createProcessingInstruction(target, data);
289    
290                    if (processingInstruction == null) {
291                            return null;
292                    }
293                    else {
294                            return new ProcessingInstructionImpl(processingInstruction);
295                    }
296            }
297    
298            @Override
299            public QName createQName(String localName) {
300                    return new QNameImpl(DocumentHelper.createQName(localName));
301            }
302    
303            @Override
304            public QName createQName(String localName, Namespace namespace) {
305                    NamespaceImpl namespaceImpl = (NamespaceImpl)namespace;
306    
307                    return new QNameImpl(
308                            DocumentHelper.createQName(
309                                    localName, namespaceImpl.getWrappedNamespace()));
310            }
311    
312            @Override
313            public Text createText(String text) {
314                    return new TextImpl(DocumentHelper.createText(text));
315            }
316    
317            @Override
318            public XPath createXPath(String xPathExpression) {
319                    return createXPath(xPathExpression, null);
320            }
321    
322            @Override
323            public XPath createXPath(
324                    String xPathExpression, Map<String, String> namespaceContextMap) {
325    
326                    return new XPathImpl(
327                            DocumentHelper.createXPath(xPathExpression), namespaceContextMap);
328            }
329    
330            @Override
331            public XPath createXPath(
332                    String xPathExpression, String prefix, String namespace) {
333    
334                    Map<String, String> namespaceContextMap = new HashMap<String, String>();
335    
336                    namespaceContextMap.put(prefix, namespace);
337    
338                    return createXPath(xPathExpression, namespaceContextMap);
339            }
340    
341            @Override
342            public Document read(File file) throws DocumentException {
343                    return read(file, false);
344            }
345    
346            @Override
347            public Document read(File file, boolean validate) throws DocumentException {
348                    Class<?> clazz = getClass();
349    
350                    ClassLoader classLoader = clazz.getClassLoader();
351    
352                    ClassLoader contextClassLoader =
353                            ClassLoaderUtil.getContextClassLoader();
354    
355                    try {
356                            if (classLoader != contextClassLoader) {
357                                    ClassLoaderUtil.setContextClassLoader(classLoader);
358                            }
359    
360                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
361    
362                            return new DocumentImpl(saxReader.read(file));
363                    }
364                    catch (org.dom4j.DocumentException de) {
365                            throw new DocumentException(de.getMessage(), de);
366                    }
367                    finally {
368                            if (classLoader != contextClassLoader) {
369                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
370                            }
371                    }
372            }
373    
374            @Override
375            public Document read(InputStream is) throws DocumentException {
376                    return read(is, false);
377            }
378    
379            @Override
380            public Document read(InputStream is, boolean validate)
381                    throws DocumentException {
382    
383                    Class<?> clazz = getClass();
384    
385                    ClassLoader classLoader = clazz.getClassLoader();
386    
387                    ClassLoader contextClassLoader =
388                            ClassLoaderUtil.getContextClassLoader();
389    
390                    try {
391                            if (classLoader != contextClassLoader) {
392                                    ClassLoaderUtil.setContextClassLoader(classLoader);
393                            }
394    
395                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
396    
397                            return new DocumentImpl(saxReader.read(is));
398                    }
399                    catch (org.dom4j.DocumentException de) {
400                            throw new DocumentException(de.getMessage(), de);
401                    }
402                    finally {
403                            if (classLoader != contextClassLoader) {
404                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
405                            }
406                    }
407            }
408    
409            @Override
410            public Document read(Reader reader) throws DocumentException {
411                    return read(reader, false);
412            }
413    
414            @Override
415            public Document read(Reader reader, boolean validate)
416                    throws DocumentException {
417    
418                    Class<?> clazz = getClass();
419    
420                    ClassLoader classLoader = clazz.getClassLoader();
421    
422                    ClassLoader contextClassLoader =
423                            ClassLoaderUtil.getContextClassLoader();
424    
425                    try {
426                            if (classLoader != contextClassLoader) {
427                                    ClassLoaderUtil.setContextClassLoader(classLoader);
428                            }
429    
430                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
431    
432                            return new DocumentImpl(saxReader.read(reader));
433                    }
434                    catch (org.dom4j.DocumentException de) {
435                            throw new DocumentException(de.getMessage(), de);
436                    }
437                    finally {
438                            if (classLoader != contextClassLoader) {
439                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
440                            }
441                    }
442            }
443    
444            @Override
445            public Document read(String xml) throws DocumentException {
446                    return read(new XMLSafeReader(xml));
447            }
448    
449            @Override
450            public Document read(String xml, boolean validate)
451                    throws DocumentException {
452    
453                    return read(new XMLSafeReader(xml), validate);
454            }
455    
456            @Override
457            public Document read(String xml, XMLSchema xmlSchema)
458                    throws DocumentException {
459    
460                    Class<?> clazz = getClass();
461    
462                    ClassLoader classLoader = clazz.getClassLoader();
463    
464                    ClassLoader contextClassLoader =
465                            ClassLoaderUtil.getContextClassLoader();
466    
467                    try {
468                            if (classLoader != contextClassLoader) {
469                                    ClassLoaderUtil.setContextClassLoader(classLoader);
470                            }
471    
472                            org.dom4j.io.SAXReader saxReader = getSAXReader(xmlSchema);
473    
474                            Reader reader = new XMLSafeReader(xml);
475    
476                            return new DocumentImpl(saxReader.read(reader));
477                    }
478                    catch (org.dom4j.DocumentException de) {
479                            throw new DocumentException(de.getMessage(), de);
480                    }
481                    finally {
482                            if (classLoader != contextClassLoader) {
483                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
484                            }
485                    }
486            }
487    
488            @Override
489            public Document read(URL url) throws DocumentException {
490                    return read(url, false);
491            }
492    
493            @Override
494            public Document read(URL url, boolean validate) throws DocumentException {
495                    Class<?> clazz = getClass();
496    
497                    ClassLoader classLoader = clazz.getClassLoader();
498    
499                    ClassLoader contextClassLoader =
500                            ClassLoaderUtil.getContextClassLoader();
501    
502                    try {
503                            if (classLoader != contextClassLoader) {
504                                    ClassLoaderUtil.setContextClassLoader(classLoader);
505                            }
506    
507                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
508    
509                            return new DocumentImpl(saxReader.read(url));
510                    }
511                    catch (org.dom4j.DocumentException de) {
512                            throw new DocumentException(de.getMessage(), de);
513                    }
514                    finally {
515                            if (classLoader != contextClassLoader) {
516                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
517                            }
518                    }
519            }
520    
521            @Override
522            public Document readURL(String url)
523                    throws DocumentException, MalformedURLException {
524    
525                    return read(new URL(url), false);
526            }
527    
528            @Override
529            public Document readURL(String url, boolean validate)
530                    throws DocumentException, MalformedURLException {
531    
532                    return read(new URL(url), validate);
533            }
534    
535            @Override
536            public List<Node> selectNodes(
537                    String xPathFilterExpression, List<Node> nodes) {
538    
539                    return toNewNodes(
540                            DocumentHelper.selectNodes(
541                                    xPathFilterExpression, toOldNodes(nodes)));
542            }
543    
544            @Override
545            public List<Node> selectNodes(String xPathFilterExpression, Node node) {
546                    NodeImpl nodeImpl = (NodeImpl)node;
547    
548                    return toNewNodes(
549                            DocumentHelper.selectNodes(
550                                    xPathFilterExpression, nodeImpl.getWrappedNode()));
551            }
552    
553            public void setSecure(boolean secure) {
554                    _secure = secure;
555            }
556    
557            public void setSecureXMLFactoryProvider(
558                    SecureXMLFactoryProvider secureXMLFactoryProvider) {
559    
560                    _secureXMLFactoryProvider = secureXMLFactoryProvider;
561            }
562    
563            @Override
564            public void sort(List<Node> nodes, String xPathExpression) {
565                    DocumentHelper.sort(toOldNodes(nodes), xPathExpression);
566            }
567    
568            @Override
569            public void sort(
570                    List<Node> nodes, String xPathExpression, boolean distinct) {
571    
572                    DocumentHelper.sort(toOldNodes(nodes), xPathExpression, distinct);
573            }
574    
575            protected org.dom4j.io.SAXReader getSAXReader(boolean validate) {
576                    org.dom4j.io.SAXReader reader = null;
577    
578                    if (!PropsValues.XML_VALIDATION_ENABLED) {
579                            validate = false;
580                    }
581    
582                    try {
583                            reader = new org.dom4j.io.SAXReader(
584                                    _secureXMLFactoryProvider.newXMLReader(), validate);
585    
586                            reader.setEntityResolver(new EntityResolver());
587                            reader.setFeature(_FEATURES_DYNAMIC, validate);
588                            reader.setFeature(_FEATURES_VALIDATION, validate);
589                            reader.setFeature(_FEATURES_VALIDATION_SCHEMA, validate);
590                            reader.setFeature(
591                                    _FEATURES_VALIDATION_SCHEMA_FULL_CHECKING, validate);
592    
593                            if (!_secure) {
594                                    reader.setFeature(_FEATURES_DISALLOW_DOCTYPE_DECL, false);
595                                    reader.setFeature(_FEATURES_LOAD_DTD_GRAMMAR, validate);
596                                    reader.setFeature(_FEATURES_LOAD_EXTERNAL_DTD, validate);
597                            }
598                    }
599                    catch (Exception e) {
600                            if (_log.isWarnEnabled()) {
601                                    _log.warn(
602                                            "XSD validation is disabled because " + e.getMessage());
603                            }
604    
605                            reader = new org.dom4j.io.SAXReader(
606                                    _secureXMLFactoryProvider.newXMLReader(), false);
607    
608                            reader.setEntityResolver(new EntityResolver());
609                    }
610    
611                    return reader;
612            }
613    
614            protected org.dom4j.io.SAXReader getSAXReader(XMLSchema xmlSchema) {
615                    boolean validate = true;
616    
617                    if (!PropsValues.XML_VALIDATION_ENABLED) {
618                            validate = false;
619                    }
620    
621                    org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
622    
623                    if ((xmlSchema == null) || (validate == false)) {
624                            return saxReader;
625                    }
626    
627                    try {
628                            saxReader.setProperty(
629                                    _PROPERTY_SCHEMA_LANGUAGE, xmlSchema.getSchemaLanguage());
630                            saxReader.setProperty(
631                                    _PROPERTY_SCHEMA_SOURCE, xmlSchema.getSchemaSource());
632                    }
633                    catch (Exception e) {
634                            if (_log.isWarnEnabled()) {
635                                    _log.warn(
636                                            "XSD validation is disabled because " + e.getMessage());
637                            }
638                    }
639    
640                    return saxReader;
641            }
642    
643            private static final String _FEATURES_DISALLOW_DOCTYPE_DECL =
644                    "http://apache.org/xml/features/disallow-doctype-decl";
645    
646            private static final String _FEATURES_DYNAMIC =
647                    "http://apache.org/xml/features/validation/dynamic";
648    
649            private static final String _FEATURES_LOAD_DTD_GRAMMAR =
650                    "http://apache.org/xml/features/nonvalidating/load-dtd-grammar";
651    
652            private static final String _FEATURES_LOAD_EXTERNAL_DTD =
653                    "http://apache.org/xml/features/nonvalidating/load-external-dtd";
654    
655            private static final String _FEATURES_VALIDATION =
656                    "http://xml.org/sax/features/validation";
657    
658            private static final String _FEATURES_VALIDATION_SCHEMA =
659                    "http://apache.org/xml/features/validation/schema";
660    
661            private static final String _FEATURES_VALIDATION_SCHEMA_FULL_CHECKING =
662                    "http://apache.org/xml/features/validation/schema-full-checking";
663    
664            private static final String _PROPERTY_SCHEMA_LANGUAGE =
665                    "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
666    
667            private static final String _PROPERTY_SCHEMA_SOURCE =
668                    "http://java.sun.com/xml/jaxp/properties/schemaSource";
669    
670            private static Log _log = LogFactoryUtil.getLog(SAXReaderImpl.class);
671    
672            private boolean _secure;
673            private SecureXMLFactoryProvider _secureXMLFactoryProvider =
674                    new SecureXMLFactoryProviderImpl();
675    
676    }