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.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    import org.dom4j.DocumentHelper;
054    
055    /**
056     * @author Brian Wing Shun Chan
057     */
058    @DoPrivileged
059    public class SAXReaderImpl implements SAXReader {
060    
061            public static SAXReaderImpl getInstance() {
062                    return _instance;
063            }
064    
065            public static List<Attribute> toNewAttributes(
066                    List<org.dom4j.Attribute> oldAttributes) {
067    
068                    List<Attribute> newAttributes = new ArrayList<Attribute>(
069                            oldAttributes.size());
070    
071                    for (org.dom4j.Attribute oldAttribute : oldAttributes) {
072                            newAttributes.add(new AttributeImpl(oldAttribute));
073                    }
074    
075                    return new NodeList<Attribute, org.dom4j.Attribute>(
076                            newAttributes, oldAttributes);
077            }
078    
079            public static List<Element> toNewElements(
080                    List<org.dom4j.Element> oldElements) {
081    
082                    List<Element> newElements = new ArrayList<Element>(oldElements.size());
083    
084                    for (org.dom4j.Element oldElement : oldElements) {
085                            newElements.add(new ElementImpl(oldElement));
086                    }
087    
088                    return new NodeList<Element, org.dom4j.Element>(
089                            newElements, oldElements);
090            }
091    
092            public static List<Namespace> toNewNamespaces(
093                    List<org.dom4j.Namespace> oldNamespaces) {
094    
095                    List<Namespace> newNamespaces = new ArrayList<Namespace>(
096                            oldNamespaces.size());
097    
098                    for (org.dom4j.Namespace oldNamespace : oldNamespaces) {
099                            newNamespaces.add(new NamespaceImpl(oldNamespace));
100                    }
101    
102                    return new NodeList<Namespace, org.dom4j.Namespace>(
103                            newNamespaces, oldNamespaces);
104            }
105    
106            public static List<Node> toNewNodes(List<org.dom4j.Node> oldNodes) {
107                    List<Node> newNodes = new ArrayList<Node>(oldNodes.size());
108    
109                    for (org.dom4j.Node oldNode : oldNodes) {
110                            if (oldNode instanceof org.dom4j.Element) {
111                                    newNodes.add(new ElementImpl((org.dom4j.Element)oldNode));
112                            }
113                            else {
114                                    newNodes.add(new NodeImpl(oldNode));
115                            }
116                    }
117    
118                    return new NodeList<Node, org.dom4j.Node>(newNodes, oldNodes);
119            }
120    
121            public static List<ProcessingInstruction> toNewProcessingInstructions(
122                    List<org.dom4j.ProcessingInstruction> oldProcessingInstructions) {
123    
124                    List<ProcessingInstruction> newProcessingInstructions =
125                            new ArrayList<ProcessingInstruction>(
126                                    oldProcessingInstructions.size());
127    
128                    for (org.dom4j.ProcessingInstruction oldProcessingInstruction :
129                                    oldProcessingInstructions) {
130    
131                            newProcessingInstructions.add(
132                                    new ProcessingInstructionImpl(oldProcessingInstruction));
133                    }
134    
135                    return new NodeList
136                            <ProcessingInstruction, org.dom4j.ProcessingInstruction>(
137                                    newProcessingInstructions, oldProcessingInstructions);
138            }
139    
140            public static List<org.dom4j.Attribute> toOldAttributes(
141                    List<Attribute> newAttributes) {
142    
143                    List<org.dom4j.Attribute> oldAttributes =
144                            new ArrayList<org.dom4j.Attribute>(newAttributes.size());
145    
146                    for (Attribute newAttribute : newAttributes) {
147                            AttributeImpl newAttributeImpl = (AttributeImpl)newAttribute;
148    
149                            oldAttributes.add(newAttributeImpl.getWrappedAttribute());
150                    }
151    
152                    return oldAttributes;
153            }
154    
155            public static List<org.dom4j.Node> toOldNodes(List<Node> newNodes) {
156                    List<org.dom4j.Node> oldNodes = new ArrayList<org.dom4j.Node>(
157                            newNodes.size());
158    
159                    for (Node newNode : newNodes) {
160                            NodeImpl newNodeImpl = (NodeImpl)newNode;
161    
162                            oldNodes.add(newNodeImpl.getWrappedNode());
163                    }
164    
165                    return oldNodes;
166            }
167    
168            public static List<org.dom4j.ProcessingInstruction>
169                    toOldProcessingInstructions(
170                            List<ProcessingInstruction> newProcessingInstructions) {
171    
172                    List<org.dom4j.ProcessingInstruction> oldProcessingInstructions =
173                            new ArrayList<org.dom4j.ProcessingInstruction>(
174                                    newProcessingInstructions.size());
175    
176                    for (ProcessingInstruction newProcessingInstruction :
177                                    newProcessingInstructions) {
178    
179                            ProcessingInstructionImpl newProcessingInstructionImpl =
180                                    (ProcessingInstructionImpl)newProcessingInstruction;
181    
182                            oldProcessingInstructions.add(
183                                    newProcessingInstructionImpl.getWrappedProcessingInstruction());
184                    }
185    
186                    return oldProcessingInstructions;
187            }
188    
189            @Override
190            public Attribute createAttribute(
191                    Element element, QName qName, String value) {
192    
193                    ElementImpl elementImpl = (ElementImpl)element;
194                    QNameImpl qNameImpl = (QNameImpl)qName;
195    
196                    DocumentFactory documentFactory = DocumentFactory.getInstance();
197    
198                    return new AttributeImpl(
199                            documentFactory.createAttribute(
200                                    elementImpl.getWrappedElement(), qNameImpl.getWrappedQName(),
201                                    value));
202            }
203    
204            @Override
205            public Attribute createAttribute(
206                    Element element, String name, String value) {
207    
208                    ElementImpl elementImpl = (ElementImpl)element;
209    
210                    DocumentFactory documentFactory = DocumentFactory.getInstance();
211    
212                    return new AttributeImpl(
213                            documentFactory.createAttribute(
214                                    elementImpl.getWrappedElement(), name, value));
215            }
216    
217            @Override
218            public Document createDocument() {
219                    return new DocumentImpl(DocumentHelper.createDocument());
220            }
221    
222            @Override
223            public Document createDocument(Element rootElement) {
224                    ElementImpl rootElementImpl = (ElementImpl)rootElement;
225    
226                    return new DocumentImpl(
227                            DocumentHelper.createDocument(rootElementImpl.getWrappedElement()));
228            }
229    
230            @Override
231            public Document createDocument(String encoding) {
232                    DocumentFactory documentFactory = DocumentFactory.getInstance();
233    
234                    return new DocumentImpl(documentFactory.createDocument(encoding));
235            }
236    
237            @Override
238            public Element createElement(QName qName) {
239                    QNameImpl qNameImpl = (QNameImpl)qName;
240    
241                    return new ElementImpl(
242                            DocumentHelper.createElement(qNameImpl.getWrappedQName()));
243            }
244    
245            @Override
246            public Element createElement(String name) {
247                    return new ElementImpl(DocumentHelper.createElement(name));
248            }
249    
250            @Override
251            public Entity createEntity(String name, String text) {
252                    return new EntityImpl(DocumentHelper.createEntity(name, text));
253            }
254    
255            @Override
256            public Namespace createNamespace(String uri) {
257                    return new NamespaceImpl(org.dom4j.Namespace.get(uri));
258            }
259    
260            @Override
261            public Namespace createNamespace(String prefix, String uri) {
262                    return new NamespaceImpl(DocumentHelper.createNamespace(prefix, uri));
263            }
264    
265            @Override
266            public ProcessingInstruction createProcessingInstruction(
267                    String target, Map<String, String> data) {
268    
269                    org.dom4j.ProcessingInstruction processingInstruction =
270                            DocumentHelper.createProcessingInstruction(target, data);
271    
272                    if (processingInstruction == null) {
273                            return null;
274                    }
275                    else {
276                            return new ProcessingInstructionImpl(processingInstruction);
277                    }
278            }
279    
280            @Override
281            public ProcessingInstruction createProcessingInstruction(
282                    String target, String data) {
283    
284                    org.dom4j.ProcessingInstruction processingInstruction =
285                            DocumentHelper.createProcessingInstruction(target, data);
286    
287                    if (processingInstruction == null) {
288                            return null;
289                    }
290                    else {
291                            return new ProcessingInstructionImpl(processingInstruction);
292                    }
293            }
294    
295            @Override
296            public QName createQName(String localName) {
297                    return new QNameImpl(DocumentHelper.createQName(localName));
298            }
299    
300            @Override
301            public QName createQName(String localName, Namespace namespace) {
302                    NamespaceImpl namespaceImpl = (NamespaceImpl)namespace;
303    
304                    return new QNameImpl(
305                            DocumentHelper.createQName(
306                                    localName, namespaceImpl.getWrappedNamespace()));
307            }
308    
309            @Override
310            public Text createText(String text) {
311                    return new TextImpl(DocumentHelper.createText(text));
312            }
313    
314            @Override
315            public XPath createXPath(String xPathExpression) {
316                    return createXPath(xPathExpression, null);
317            }
318    
319            @Override
320            public XPath createXPath(
321                    String xPathExpression, Map<String, String> namespaceContextMap) {
322    
323                    return new XPathImpl(
324                            DocumentHelper.createXPath(xPathExpression), namespaceContextMap);
325            }
326    
327            @Override
328            public XPath createXPath(
329                    String xPathExpression, String prefix, String namespace) {
330    
331                    Map<String, String> namespaceContextMap = new HashMap<String, String>();
332    
333                    namespaceContextMap.put(prefix, namespace);
334    
335                    return createXPath(xPathExpression, namespaceContextMap);
336            }
337    
338            @Override
339            public Document read(File file) throws DocumentException {
340                    return read(file, false);
341            }
342    
343            @Override
344            public Document read(File file, boolean validate) throws DocumentException {
345                    ClassLoader classLoader = getClass().getClassLoader();
346    
347                    ClassLoader contextClassLoader =
348                            ClassLoaderUtil.getContextClassLoader();
349    
350                    try {
351                            if (contextClassLoader != classLoader) {
352                                    ClassLoaderUtil.setContextClassLoader(classLoader);
353                            }
354    
355                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
356    
357                            return new DocumentImpl(saxReader.read(file));
358                    }
359                    catch (org.dom4j.DocumentException de) {
360                            throw new DocumentException(de.getMessage(), de);
361                    }
362                    finally {
363                            if (contextClassLoader != classLoader) {
364                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
365                            }
366                    }
367            }
368    
369            @Override
370            public Document read(InputStream is) throws DocumentException {
371                    return read(is, false);
372            }
373    
374            @Override
375            public Document read(InputStream is, boolean validate)
376                    throws DocumentException {
377    
378                    ClassLoader classLoader = getClass().getClassLoader();
379    
380                    ClassLoader contextClassLoader =
381                            ClassLoaderUtil.getContextClassLoader();
382    
383                    try {
384                            if (contextClassLoader != classLoader) {
385                                    ClassLoaderUtil.setContextClassLoader(classLoader);
386                            }
387    
388                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
389    
390                            return new DocumentImpl(saxReader.read(is));
391                    }
392                    catch (org.dom4j.DocumentException de) {
393                            throw new DocumentException(de.getMessage(), de);
394                    }
395                    finally {
396                            if (contextClassLoader != classLoader) {
397                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
398                            }
399                    }
400            }
401    
402            @Override
403            public Document read(Reader reader) throws DocumentException {
404                    return read(reader, false);
405            }
406    
407            @Override
408            public Document read(Reader reader, boolean validate)
409                    throws DocumentException {
410    
411                    ClassLoader classLoader = getClass().getClassLoader();
412    
413                    ClassLoader contextClassLoader =
414                            ClassLoaderUtil.getContextClassLoader();
415    
416                    try {
417                            if (contextClassLoader != classLoader) {
418                                    ClassLoaderUtil.setContextClassLoader(classLoader);
419                            }
420    
421                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
422    
423                            return new DocumentImpl(saxReader.read(reader));
424                    }
425                    catch (org.dom4j.DocumentException de) {
426                            throw new DocumentException(de.getMessage(), de);
427                    }
428                    finally {
429                            if (contextClassLoader != classLoader) {
430                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
431                            }
432                    }
433            }
434    
435            @Override
436            public Document read(String xml) throws DocumentException {
437                    return read(new XMLSafeReader(xml));
438            }
439    
440            @Override
441            public Document read(String xml, boolean validate)
442                    throws DocumentException {
443    
444                    return read(new XMLSafeReader(xml), validate);
445            }
446    
447            @Override
448            public Document read(String xml, XMLSchema xmlSchema)
449                    throws DocumentException {
450    
451                    ClassLoader classLoader = getClass().getClassLoader();
452    
453                    ClassLoader contextClassLoader =
454                            ClassLoaderUtil.getContextClassLoader();
455    
456                    try {
457                            if (contextClassLoader != classLoader) {
458                                    ClassLoaderUtil.setContextClassLoader(classLoader);
459                            }
460    
461                            org.dom4j.io.SAXReader saxReader = getSAXReader(xmlSchema);
462    
463                            Reader reader = new XMLSafeReader(xml);
464    
465                            return new DocumentImpl(saxReader.read(reader));
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 read(URL url) throws DocumentException {
479                    return read(url, false);
480            }
481    
482            @Override
483            public Document read(URL url, boolean validate) throws DocumentException {
484                    ClassLoader classLoader = getClass().getClassLoader();
485    
486                    ClassLoader contextClassLoader =
487                            ClassLoaderUtil.getContextClassLoader();
488    
489                    try {
490                            if (contextClassLoader != classLoader) {
491                                    ClassLoaderUtil.setContextClassLoader(classLoader);
492                            }
493    
494                            org.dom4j.io.SAXReader saxReader = getSAXReader(validate);
495    
496                            return new DocumentImpl(saxReader.read(url));
497                    }
498                    catch (org.dom4j.DocumentException de) {
499                            throw new DocumentException(de.getMessage(), de);
500                    }
501                    finally {
502                            if (contextClassLoader != classLoader) {
503                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
504                            }
505                    }
506            }
507    
508            @Override
509            public Document readURL(String url)
510                    throws DocumentException, MalformedURLException {
511    
512                    return read(new URL(url), false);
513            }
514    
515            @Override
516            public Document readURL(String url, boolean validate)
517                    throws DocumentException, MalformedURLException {
518    
519                    return read(new URL(url), validate);
520            }
521    
522            @Override
523            public List<Node> selectNodes(
524                    String xPathFilterExpression, List<Node> nodes) {
525    
526                    return toNewNodes(
527                            DocumentHelper.selectNodes(
528                                    xPathFilterExpression, toOldNodes(nodes)));
529            }
530    
531            @Override
532            public List<Node> selectNodes(String xPathFilterExpression, Node node) {
533                    NodeImpl nodeImpl = (NodeImpl)node;
534    
535                    return toNewNodes(
536                            DocumentHelper.selectNodes(
537                                    xPathFilterExpression, nodeImpl.getWrappedNode()));
538            }
539    
540            @Override
541            public void sort(List<Node> nodes, String xPathExpression) {
542                    DocumentHelper.sort(toOldNodes(nodes), xPathExpression);
543            }
544    
545            @Override
546            public void sort(
547                    List<Node> nodes, String xPathExpression, boolean distinct) {
548    
549                    DocumentHelper.sort(toOldNodes(nodes), xPathExpression, 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 Log _log = LogFactoryUtil.getLog(SAXReaderImpl.class);
644    
645            private static SAXReaderImpl _instance = new SAXReaderImpl();
646    
647    }