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.XPath;
032    import com.liferay.portal.util.ClassLoaderUtil;
033    import com.liferay.portal.util.EntityResolver;
034    import com.liferay.portal.util.PropsValues;
035    import com.liferay.util.xml.XMLSafeReader;
036    
037    import java.io.File;
038    import java.io.InputStream;
039    import java.io.Reader;
040    
041    import java.net.MalformedURLException;
042    import java.net.URL;
043    
044    import java.util.ArrayList;
045    import java.util.HashMap;
046    import java.util.List;
047    import java.util.Map;
048    
049    import org.apache.xerces.parsers.SAXParser;
050    
051    import org.dom4j.DocumentFactory;
052    import org.dom4j.DocumentHelper;
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                    DocumentFactory documentFactory = DocumentFactory.getInstance();
196    
197                    return new AttributeImpl(
198                            documentFactory.createAttribute(
199                                    elementImpl.getWrappedElement(), qNameImpl.getWrappedQName(),
200                                    value));
201            }
202    
203            @Override
204            public Attribute createAttribute(
205                    Element element, String name, String value) {
206    
207                    ElementImpl elementImpl = (ElementImpl)element;
208    
209                    DocumentFactory documentFactory = DocumentFactory.getInstance();
210    
211                    return new AttributeImpl(
212                            documentFactory.createAttribute(
213                                    elementImpl.getWrappedElement(), name, value));
214            }
215    
216            @Override
217            public Document createDocument() {
218                    return new DocumentImpl(DocumentHelper.createDocument());
219            }
220    
221            @Override
222            public Document createDocument(Element rootElement) {
223                    ElementImpl rootElementImpl = (ElementImpl)rootElement;
224    
225                    return new DocumentImpl(
226                            DocumentHelper.createDocument(rootElementImpl.getWrappedElement()));
227            }
228    
229            @Override
230            public Document createDocument(String encoding) {
231                    DocumentFactory documentFactory = DocumentFactory.getInstance();
232    
233                    return new DocumentImpl(documentFactory.createDocument(encoding));
234            }
235    
236            @Override
237            public Element createElement(QName qName) {
238                    QNameImpl qNameImpl = (QNameImpl)qName;
239    
240                    return new ElementImpl(
241                            DocumentHelper.createElement(qNameImpl.getWrappedQName()));
242            }
243    
244            @Override
245            public Element createElement(String name) {
246                    return new ElementImpl(DocumentHelper.createElement(name));
247            }
248    
249            @Override
250            public Entity createEntity(String name, String text) {
251                    return new EntityImpl(DocumentHelper.createEntity(name, text));
252            }
253    
254            @Override
255            public Namespace createNamespace(String uri) {
256                    return new NamespaceImpl(org.dom4j.Namespace.get(uri));
257            }
258    
259            @Override
260            public Namespace createNamespace(String prefix, String uri) {
261                    return new NamespaceImpl(DocumentHelper.createNamespace(prefix, uri));
262            }
263    
264            @Override
265            public ProcessingInstruction createProcessingInstruction(
266                    String target, Map<String, String> data) {
267    
268                    org.dom4j.ProcessingInstruction processingInstruction =
269                            DocumentHelper.createProcessingInstruction(target, data);
270    
271                    if (processingInstruction == null) {
272                            return null;
273                    }
274                    else {
275                            return new ProcessingInstructionImpl(processingInstruction);
276                    }
277            }
278    
279            @Override
280            public ProcessingInstruction createProcessingInstruction(
281                    String target, String data) {
282    
283                    org.dom4j.ProcessingInstruction processingInstruction =
284                            DocumentHelper.createProcessingInstruction(target, data);
285    
286                    if (processingInstruction == null) {
287                            return null;
288                    }
289                    else {
290                            return new ProcessingInstructionImpl(processingInstruction);
291                    }
292            }
293    
294            @Override
295            public QName createQName(String localName) {
296                    return new QNameImpl(DocumentHelper.createQName(localName));
297            }
298    
299            @Override
300            public QName createQName(String localName, Namespace namespace) {
301                    NamespaceImpl namespaceImpl = (NamespaceImpl)namespace;
302    
303                    return new QNameImpl(
304                            DocumentHelper.createQName(
305                                    localName, namespaceImpl.getWrappedNamespace()));
306            }
307    
308            @Override
309            public Text createText(String text) {
310                    return new TextImpl(DocumentHelper.createText(text));
311            }
312    
313            @Override
314            public XPath createXPath(String xPathExpression) {
315                    return createXPath(xPathExpression, null);
316            }
317    
318            @Override
319            public XPath createXPath(
320                    String xPathExpression, Map<String, String> namespaceContextMap) {
321    
322                    return new XPathImpl(
323                            DocumentHelper.createXPath(xPathExpression), namespaceContextMap);
324            }
325    
326            @Override
327            public XPath createXPath(
328                    String xPathExpression, String prefix, String namespace) {
329    
330                    Map<String, String> namespaceContextMap = new HashMap<String, String>();
331    
332                    namespaceContextMap.put(prefix, namespace);
333    
334                    return createXPath(xPathExpression, namespaceContextMap);
335            }
336    
337            @Override
338            public Document read(File file) throws DocumentException {
339                    return read(file, false);
340            }
341    
342            @Override
343            public Document read(File file, boolean validate) throws DocumentException {
344                    ClassLoader classLoader = getClass().getClassLoader();
345    
346                    ClassLoader contextClassLoader =
347                            ClassLoaderUtil.getContextClassLoader();
348    
349                    try {
350                            if (contextClassLoader != classLoader) {
351                                    ClassLoaderUtil.setContextClassLoader(classLoader);
352                            }
353    
354                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
355    
356                            return new DocumentImpl(saxReader.read(file));
357                    }
358                    catch (org.dom4j.DocumentException de) {
359                            throw new DocumentException(de.getMessage(), de);
360                    }
361                    finally {
362                            if (contextClassLoader != classLoader) {
363                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
364                            }
365                    }
366            }
367    
368            @Override
369            public Document read(InputStream is) throws DocumentException {
370                    return read(is, false);
371            }
372    
373            @Override
374            public Document read(InputStream is, boolean validate)
375                    throws DocumentException {
376    
377                    ClassLoader classLoader = getClass().getClassLoader();
378    
379                    ClassLoader contextClassLoader =
380                            ClassLoaderUtil.getContextClassLoader();
381    
382                    try {
383                            if (contextClassLoader != classLoader) {
384                                    ClassLoaderUtil.setContextClassLoader(classLoader);
385                            }
386    
387                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
388    
389                            return new DocumentImpl(saxReader.read(is));
390                    }
391                    catch (org.dom4j.DocumentException de) {
392                            throw new DocumentException(de.getMessage(), de);
393                    }
394                    finally {
395                            if (contextClassLoader != classLoader) {
396                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
397                            }
398                    }
399            }
400    
401            @Override
402            public Document read(Reader reader) throws DocumentException {
403                    return read(reader, false);
404            }
405    
406            @Override
407            public Document read(Reader reader, boolean validate)
408                    throws DocumentException {
409    
410                    ClassLoader classLoader = getClass().getClassLoader();
411    
412                    ClassLoader contextClassLoader =
413                            ClassLoaderUtil.getContextClassLoader();
414    
415                    try {
416                            if (contextClassLoader != classLoader) {
417                                    ClassLoaderUtil.setContextClassLoader(classLoader);
418                            }
419    
420                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
421    
422                            return new DocumentImpl(saxReader.read(reader));
423                    }
424                    catch (org.dom4j.DocumentException de) {
425                            throw new DocumentException(de.getMessage(), de);
426                    }
427                    finally {
428                            if (contextClassLoader != classLoader) {
429                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
430                            }
431                    }
432            }
433    
434            @Override
435            public Document read(String xml) throws DocumentException {
436                    return read(new XMLSafeReader(xml));
437            }
438    
439            @Override
440            public Document read(String xml, boolean validate)
441                    throws DocumentException {
442    
443                    return read(new XMLSafeReader(xml), validate);
444            }
445    
446            @Override
447            public Document read(URL url) throws DocumentException {
448                    return read(url, false);
449            }
450    
451            @Override
452            public Document read(URL url, boolean validate) throws DocumentException {
453                    ClassLoader classLoader = getClass().getClassLoader();
454    
455                    ClassLoader contextClassLoader =
456                            ClassLoaderUtil.getContextClassLoader();
457    
458                    try {
459                            if (contextClassLoader != classLoader) {
460                                    ClassLoaderUtil.setContextClassLoader(classLoader);
461                            }
462    
463                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
464    
465                            return new DocumentImpl(saxReader.read(url));
466                    }
467                    catch (org.dom4j.DocumentException de) {
468                            throw new DocumentException(de.getMessage(), de);
469                    }
470                    finally {
471                            if (contextClassLoader != classLoader) {
472                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
473                            }
474                    }
475            }
476    
477            @Override
478            public Document readURL(String url)
479                    throws DocumentException, MalformedURLException {
480    
481                    return read(new URL(url), false);
482            }
483    
484            @Override
485            public Document readURL(String url, boolean validate)
486                    throws DocumentException, MalformedURLException {
487    
488                    return read(new URL(url), validate);
489            }
490    
491            @Override
492            public List<Node> selectNodes(
493                    String xPathFilterExpression, List<Node> nodes) {
494    
495                    return toNewNodes(
496                            DocumentHelper.selectNodes(
497                                    xPathFilterExpression, toOldNodes(nodes)));
498            }
499    
500            @Override
501            public List<Node> selectNodes(String xPathFilterExpression, Node node) {
502                    NodeImpl nodeImpl = (NodeImpl)node;
503    
504                    return toNewNodes(
505                            DocumentHelper.selectNodes(
506                                    xPathFilterExpression, nodeImpl.getWrappedNode()));
507            }
508    
509            @Override
510            public void sort(List<Node> nodes, String xPathExpression) {
511                    DocumentHelper.sort(toOldNodes(nodes), xPathExpression);
512            }
513    
514            @Override
515            public void sort(
516                    List<Node> nodes, String xPathExpression, boolean distinct) {
517    
518                    DocumentHelper.sort(toOldNodes(nodes), xPathExpression, distinct);
519            }
520    
521            protected org.dom4j.io.SAXReader getSAXReader(boolean validate) {
522                    org.dom4j.io.SAXReader reader = null;
523    
524                    if (!PropsValues.XML_VALIDATION_ENABLED) {
525                            validate = false;
526                    }
527    
528                    try {
529                            reader = new org.dom4j.io.SAXReader(new SAXParser(), validate);
530    
531                            reader.setEntityResolver(new EntityResolver());
532    
533                            reader.setFeature(_FEATURES_DYNAMIC, validate);
534                            reader.setFeature(_FEATURES_EXTERNAL_GENERAL_ENTITIES, validate);
535                            reader.setFeature(_FEATURES_LOAD_DTD_GRAMMAR, validate);
536                            reader.setFeature(_FEATURES_LOAD_EXTERNAL_DTD, validate);
537                            reader.setFeature(_FEATURES_VALIDATION, validate);
538                            reader.setFeature(_FEATURES_VALIDATION_SCHEMA, validate);
539                            reader.setFeature(
540                                    _FEATURES_VALIDATION_SCHEMA_FULL_CHECKING, validate);
541                    }
542                    catch (Exception e) {
543                            if (_log.isWarnEnabled()) {
544                                    _log.warn(
545                                            "XSD validation is disabled because " + e.getMessage());
546                            }
547    
548                            reader = new org.dom4j.io.SAXReader(false);
549    
550                            reader.setEntityResolver(new EntityResolver());
551                    }
552    
553                    return reader;
554            }
555    
556            private static final String _FEATURES_DYNAMIC =
557                    "http://apache.org/xml/features/validation/dynamic";
558    
559            private static final String _FEATURES_EXTERNAL_GENERAL_ENTITIES =
560                    "http://xml.org/sax/features/external-general-entities";
561    
562            private static final String _FEATURES_LOAD_DTD_GRAMMAR =
563                    "http://apache.org/xml/features/nonvalidating/load-dtd-grammar";
564    
565            private static final String _FEATURES_LOAD_EXTERNAL_DTD =
566                    "http://apache.org/xml/features/nonvalidating/load-external-dtd";
567    
568            private static final String _FEATURES_VALIDATION =
569                    "http://xml.org/sax/features/validation";
570    
571            private static final String _FEATURES_VALIDATION_SCHEMA =
572                    "http://apache.org/xml/features/validation/schema";
573    
574            private static final String _FEATURES_VALIDATION_SCHEMA_FULL_CHECKING =
575                    "http://apache.org/xml/features/validation/schema-full-checking";
576    
577            private static Log _log = LogFactoryUtil.getLog(SAXReaderImpl.class);
578    
579            private static SAXReaderImpl _instance = new SAXReaderImpl();
580    
581    }