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