001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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                    ClassLoader classLoader = getClass().getClassLoader();
349    
350                    ClassLoader contextClassLoader =
351                            ClassLoaderUtil.getContextClassLoader();
352    
353                    try {
354                            if (contextClassLoader != classLoader) {
355                                    ClassLoaderUtil.setContextClassLoader(classLoader);
356                            }
357    
358                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
359    
360                            return new DocumentImpl(saxReader.read(file));
361                    }
362                    catch (org.dom4j.DocumentException de) {
363                            throw new DocumentException(de.getMessage(), de);
364                    }
365                    finally {
366                            if (contextClassLoader != classLoader) {
367                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
368                            }
369                    }
370            }
371    
372            @Override
373            public Document read(InputStream is) throws DocumentException {
374                    return read(is, false);
375            }
376    
377            @Override
378            public Document read(InputStream is, boolean validate)
379                    throws DocumentException {
380    
381                    ClassLoader classLoader = getClass().getClassLoader();
382    
383                    ClassLoader contextClassLoader =
384                            ClassLoaderUtil.getContextClassLoader();
385    
386                    try {
387                            if (contextClassLoader != classLoader) {
388                                    ClassLoaderUtil.setContextClassLoader(classLoader);
389                            }
390    
391                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
392    
393                            return new DocumentImpl(saxReader.read(is));
394                    }
395                    catch (org.dom4j.DocumentException de) {
396                            throw new DocumentException(de.getMessage(), de);
397                    }
398                    finally {
399                            if (contextClassLoader != classLoader) {
400                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
401                            }
402                    }
403            }
404    
405            @Override
406            public Document read(Reader reader) throws DocumentException {
407                    return read(reader, false);
408            }
409    
410            @Override
411            public Document read(Reader reader, boolean validate)
412                    throws DocumentException {
413    
414                    ClassLoader classLoader = getClass().getClassLoader();
415    
416                    ClassLoader contextClassLoader =
417                            ClassLoaderUtil.getContextClassLoader();
418    
419                    try {
420                            if (contextClassLoader != classLoader) {
421                                    ClassLoaderUtil.setContextClassLoader(classLoader);
422                            }
423    
424                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
425    
426                            return new DocumentImpl(saxReader.read(reader));
427                    }
428                    catch (org.dom4j.DocumentException de) {
429                            throw new DocumentException(de.getMessage(), de);
430                    }
431                    finally {
432                            if (contextClassLoader != classLoader) {
433                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
434                            }
435                    }
436            }
437    
438            @Override
439            public Document read(String xml) throws DocumentException {
440                    return read(new XMLSafeReader(xml));
441            }
442    
443            @Override
444            public Document read(String xml, boolean validate)
445                    throws DocumentException {
446    
447                    return read(new XMLSafeReader(xml), validate);
448            }
449    
450            @Override
451            public Document read(String xml, XMLSchema xmlSchema)
452                    throws DocumentException {
453    
454                    ClassLoader classLoader = getClass().getClassLoader();
455    
456                    ClassLoader contextClassLoader =
457                            ClassLoaderUtil.getContextClassLoader();
458    
459                    try {
460                            if (contextClassLoader != classLoader) {
461                                    ClassLoaderUtil.setContextClassLoader(classLoader);
462                            }
463    
464                            org.dom4j.io.SAXReader saxReader = getSAXReader(xmlSchema);
465    
466                            Reader reader = new XMLSafeReader(xml);
467    
468                            return new DocumentImpl(saxReader.read(reader));
469                    }
470                    catch (org.dom4j.DocumentException de) {
471                            throw new DocumentException(de.getMessage(), de);
472                    }
473                    finally {
474                            if (contextClassLoader != classLoader) {
475                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
476                            }
477                    }
478            }
479    
480            @Override
481            public Document read(URL url) throws DocumentException {
482                    return read(url, false);
483            }
484    
485            @Override
486            public Document read(URL url, boolean validate) throws DocumentException {
487                    ClassLoader classLoader = getClass().getClassLoader();
488    
489                    ClassLoader contextClassLoader =
490                            ClassLoaderUtil.getContextClassLoader();
491    
492                    try {
493                            if (contextClassLoader != classLoader) {
494                                    ClassLoaderUtil.setContextClassLoader(classLoader);
495                            }
496    
497                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
498    
499                            return new DocumentImpl(saxReader.read(url));
500                    }
501                    catch (org.dom4j.DocumentException de) {
502                            throw new DocumentException(de.getMessage(), de);
503                    }
504                    finally {
505                            if (contextClassLoader != classLoader) {
506                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
507                            }
508                    }
509            }
510    
511            @Override
512            public Document readURL(String url)
513                    throws DocumentException, MalformedURLException {
514    
515                    return read(new URL(url), false);
516            }
517    
518            @Override
519            public Document readURL(String url, boolean validate)
520                    throws DocumentException, MalformedURLException {
521    
522                    return read(new URL(url), validate);
523            }
524    
525            @Override
526            public List<Node> selectNodes(
527                    String xPathFilterExpression, List<Node> nodes) {
528    
529                    return toNewNodes(
530                            DocumentHelper.selectNodes(
531                                    xPathFilterExpression, toOldNodes(nodes)));
532            }
533    
534            @Override
535            public List<Node> selectNodes(String xPathFilterExpression, Node node) {
536                    NodeImpl nodeImpl = (NodeImpl)node;
537    
538                    return toNewNodes(
539                            DocumentHelper.selectNodes(
540                                    xPathFilterExpression, nodeImpl.getWrappedNode()));
541            }
542    
543            public void setSecure(boolean secure) {
544                    _secure = secure;
545            }
546    
547            public void setSecureXMLFactoryProvider(
548                    SecureXMLFactoryProvider secureXMLFactoryProvider) {
549    
550                    _secureXMLFactoryProvider = secureXMLFactoryProvider;
551            }
552    
553            @Override
554            public void sort(List<Node> nodes, String xPathExpression) {
555                    DocumentHelper.sort(toOldNodes(nodes), xPathExpression);
556            }
557    
558            @Override
559            public void sort(
560                    List<Node> nodes, String xPathExpression, boolean distinct) {
561    
562                    DocumentHelper.sort(toOldNodes(nodes), xPathExpression, distinct);
563            }
564    
565            protected org.dom4j.io.SAXReader getSAXReader(boolean validate) {
566                    org.dom4j.io.SAXReader reader = null;
567    
568                    if (!PropsValues.XML_VALIDATION_ENABLED) {
569                            validate = false;
570                    }
571    
572                    try {
573                            reader = new org.dom4j.io.SAXReader(
574                                    _secureXMLFactoryProvider.newXMLReader(), validate);
575    
576                            reader.setEntityResolver(new EntityResolver());
577                            reader.setFeature(_FEATURES_DYNAMIC, validate);
578                            reader.setFeature(_FEATURES_VALIDATION, validate);
579                            reader.setFeature(_FEATURES_VALIDATION_SCHEMA, validate);
580                            reader.setFeature(
581                                    _FEATURES_VALIDATION_SCHEMA_FULL_CHECKING, validate);
582    
583                            if (!_secure) {
584                                    reader.setFeature(_FEATURES_DISALLOW_DOCTYPE_DECL, false);
585                                    reader.setFeature(_FEATURES_LOAD_DTD_GRAMMAR, validate);
586                                    reader.setFeature(_FEATURES_LOAD_EXTERNAL_DTD, validate);
587                            }
588                    }
589                    catch (Exception e) {
590                            if (_log.isWarnEnabled()) {
591                                    _log.warn(
592                                            "XSD validation is disabled because " + e.getMessage());
593                            }
594    
595                            reader = new org.dom4j.io.SAXReader(
596                                    _secureXMLFactoryProvider.newXMLReader(), false);
597    
598                            reader.setEntityResolver(new EntityResolver());
599                    }
600    
601                    return reader;
602            }
603    
604            protected org.dom4j.io.SAXReader getSAXReader(XMLSchema xmlSchema) {
605                    boolean validate = true;
606    
607                    if (!PropsValues.XML_VALIDATION_ENABLED) {
608                            validate = false;
609                    }
610    
611                    org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
612    
613                    if ((xmlSchema == null) || (validate == false)) {
614                            return saxReader;
615                    }
616    
617                    try {
618                            saxReader.setProperty(
619                                    _PROPERTY_SCHEMA_LANGUAGE, xmlSchema.getSchemaLanguage());
620                            saxReader.setProperty(
621                                    _PROPERTY_SCHEMA_SOURCE, xmlSchema.getSchemaSource());
622                    }
623                    catch (Exception e) {
624                            if (_log.isWarnEnabled()) {
625                                    _log.warn(
626                                            "XSD validation is disabled because " + e.getMessage());
627                            }
628                    }
629    
630                    return saxReader;
631            }
632    
633            private static final String _FEATURES_DISALLOW_DOCTYPE_DECL =
634                    "http://apache.org/xml/features/disallow-doctype-decl";
635    
636            private static final String _FEATURES_DYNAMIC =
637                    "http://apache.org/xml/features/validation/dynamic";
638    
639            private static final String _FEATURES_LOAD_DTD_GRAMMAR =
640                    "http://apache.org/xml/features/nonvalidating/load-dtd-grammar";
641    
642            private static final String _FEATURES_LOAD_EXTERNAL_DTD =
643                    "http://apache.org/xml/features/nonvalidating/load-external-dtd";
644    
645            private static final String _FEATURES_VALIDATION =
646                    "http://xml.org/sax/features/validation";
647    
648            private static final String _FEATURES_VALIDATION_SCHEMA =
649                    "http://apache.org/xml/features/validation/schema";
650    
651            private static final String _FEATURES_VALIDATION_SCHEMA_FULL_CHECKING =
652                    "http://apache.org/xml/features/validation/schema-full-checking";
653    
654            private static final String _PROPERTY_SCHEMA_LANGUAGE =
655                    "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
656    
657            private static final String _PROPERTY_SCHEMA_SOURCE =
658                    "http://java.sun.com/xml/jaxp/properties/schemaSource";
659    
660            private static Log _log = LogFactoryUtil.getLog(SAXReaderImpl.class);
661    
662            private boolean _secure;
663            private SecureXMLFactoryProvider _secureXMLFactoryProvider =
664                    new SecureXMLFactoryProviderImpl();
665    
666    }