View Javadoc
1   package org.apache.archiva.xml;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.lang.StringUtils;
23  import org.dom4j.Attribute;
24  import org.dom4j.Document;
25  import org.dom4j.DocumentException;
26  import org.dom4j.Element;
27  import org.dom4j.Namespace;
28  import org.dom4j.Node;
29  import org.dom4j.QName;
30  import org.dom4j.XPath;
31  import org.dom4j.io.SAXReader;
32  
33  import java.io.File;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.InputStreamReader;
37  import java.net.MalformedURLException;
38  import java.net.URL;
39  import java.nio.charset.Charset;
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.util.Iterator;
43  import java.util.List;
44  import java.util.Map;
45  
46  /**
47   * XMLReader - a set of common xml utility methods for reading content out of an xml file.
48   */
49  public class XMLReader
50  {
51      private URL xmlUrl;
52  
53      private String documentType;
54  
55      private Document document;
56  
57      private Map<String, String> namespaceMap = new HashMap<>();
58  
59      public XMLReader( String type, File file )
60          throws XMLException
61      {
62          if ( !file.exists() )
63          {
64              throw new XMLException( "file does not exist: " + file.getAbsolutePath() );
65          }
66  
67          if ( !file.isFile() )
68          {
69              throw new XMLException( "path is not a file: " + file.getAbsolutePath() );
70          }
71  
72          if ( !file.canRead() )
73          {
74              throw new XMLException( "Cannot read xml file due to permissions: " + file.getAbsolutePath() );
75          }
76  
77          try
78          {
79              init( type, file.toURL() );
80          }
81          catch ( MalformedURLException e )
82          {
83              throw new XMLException( "Unable to translate file " + file + " to URL: " + e.getMessage(), e );
84          }
85      }
86  
87      public XMLReader( String type, URL url )
88          throws XMLException
89      {
90          init( type, url );
91      }
92  
93      private void init( String type, URL url )
94          throws XMLException
95      {
96          this.documentType = type;
97          this.xmlUrl = url;
98  
99          SAXReader reader = new SAXReader();
100 
101         try (InputStream in = url.openStream())
102         {
103             InputStreamReader inReader = new InputStreamReader( in, Charset.forName( "UTF-8" ) );
104             LatinEntityResolutionReader latinReader = new LatinEntityResolutionReader( inReader );
105             this.document = reader.read( latinReader );
106         }
107         catch ( DocumentException e )
108         {
109             throw new XMLException( "Unable to parse " + documentType + " xml " + xmlUrl + ": " + e.getMessage(), e );
110         }
111         catch ( IOException e )
112         {
113             throw new XMLException( "Unable to open stream to " + url + ": " + e.getMessage(), e );
114         }
115 
116         Element root = this.document.getRootElement();
117         if ( root == null )
118         {
119             throw new XMLException( "Invalid " + documentType + " xml: root element is null." );
120         }
121 
122         if ( !StringUtils.equals( root.getName(), documentType ) )
123         {
124             throw new XMLException(
125                 "Invalid " + documentType + " xml: Unexpected root element <" + root.getName() + ">, expected <"
126                     + documentType + ">" );
127         }
128     }
129 
130     public String getDefaultNamespaceURI()
131     {
132         Namespace namespace = this.document.getRootElement().getNamespace();
133         return namespace.getURI();
134     }
135 
136     public void addNamespaceMapping( String elementName, String uri )
137     {
138         this.namespaceMap.put( elementName, uri );
139     }
140 
141     public Element getElement( String xpathExpr )
142         throws XMLException
143     {
144         XPath xpath = createXPath( xpathExpr );
145         Object evaluated = xpath.selectSingleNode( document );
146 
147         if ( evaluated == null )
148         {
149             return null;
150         }
151 
152         if ( evaluated instanceof Element )
153         {
154             return (Element) evaluated;
155         }
156         else
157         {
158             // Unknown evaluated type.
159             throw new XMLException( ".getElement( Expr: " + xpathExpr + " ) resulted in non-Element type -> ("
160                                         + evaluated.getClass().getName() + ") " + evaluated );
161         }
162     }
163 
164     private XPath createXPath( String xpathExpr )
165     {
166         XPath xpath = document.createXPath( xpathExpr );
167         if ( !this.namespaceMap.isEmpty() )
168         {
169             xpath.setNamespaceURIs( this.namespaceMap );
170         }
171         return xpath;
172     }
173 
174     public boolean hasElement( String xpathExpr )
175         throws XMLException
176     {
177         XPath xpath = createXPath( xpathExpr );
178         Object evaluated = xpath.selectSingleNode( document );
179 
180         if ( evaluated == null )
181         {
182             return false;
183         }
184 
185         return true;
186     }
187 
188     /**
189      * Remove namespaces from entire document.
190      */
191     public void removeNamespaces()
192     {
193         removeNamespaces( this.document.getRootElement() );
194     }
195 
196     /**
197      * Remove namespaces from element recursively.
198      */
199     @SuppressWarnings("unchecked")
200     public void removeNamespaces( Element elem )
201     {
202         elem.setQName( QName.get( elem.getName(), Namespace.NO_NAMESPACE, elem.getQualifiedName() ) );
203 
204         Node n;
205 
206         Iterator<Node> it = elem.elementIterator();
207         while ( it.hasNext() )
208         {
209             n = it.next();
210 
211             switch ( n.getNodeType() )
212             {
213                 case Node.ATTRIBUTE_NODE:
214                     ( (Attribute) n ).setNamespace( Namespace.NO_NAMESPACE );
215                     break;
216                 case Node.ELEMENT_NODE:
217                     removeNamespaces( (Element) n );
218                     break;
219             }
220         }
221     }
222 
223     public String getElementText( Node context, String xpathExpr )
224         throws XMLException
225     {
226         XPath xpath = createXPath( xpathExpr );
227         Object evaluated = xpath.selectSingleNode( context );
228 
229         if ( evaluated == null )
230         {
231             return null;
232         }
233 
234         if ( evaluated instanceof Element )
235         {
236             Element evalElem = (Element) evaluated;
237             return evalElem.getTextTrim();
238         }
239         else
240         {
241             // Unknown evaluated type.
242             throw new XMLException( ".getElementText( Node, Expr: " + xpathExpr + " ) resulted in non-Element type -> ("
243                                         + evaluated.getClass().getName() + ") " + evaluated );
244         }
245     }
246 
247     public String getElementText( String xpathExpr )
248         throws XMLException
249     {
250         XPath xpath = createXPath( xpathExpr );
251         Object evaluated = xpath.selectSingleNode( document );
252 
253         if ( evaluated == null )
254         {
255             return null;
256         }
257 
258         if ( evaluated instanceof Element )
259         {
260             Element evalElem = (Element) evaluated;
261             return evalElem.getTextTrim();
262         }
263         else
264         {
265             // Unknown evaluated type.
266             throw new XMLException( ".getElementText( Expr: " + xpathExpr + " ) resulted in non-Element type -> ("
267                                         + evaluated.getClass().getName() + ") " + evaluated );
268         }
269     }
270 
271     @SuppressWarnings("unchecked")
272     public List<Element> getElementList( String xpathExpr )
273         throws XMLException
274     {
275         XPath xpath = createXPath( xpathExpr );
276         Object evaluated = xpath.evaluate( document );
277 
278         if ( evaluated == null )
279         {
280             return null;
281         }
282 
283         /* The xpath.evaluate(Context) method can return:
284          *   1) A Collection or List of dom4j Nodes. 
285          *   2) A single dom4j Node.
286          */
287 
288         if ( evaluated instanceof List )
289         {
290             return (List<Element>) evaluated;
291         }
292         else if ( evaluated instanceof Node )
293         {
294             List<Element> ret = new ArrayList<>();
295             ret.add( (Element) evaluated );
296             return ret;
297         }
298         else
299         {
300             // Unknown evaluated type.
301             throw new XMLException( ".getElementList( Expr: " + xpathExpr + " ) resulted in non-List type -> ("
302                                         + evaluated.getClass().getName() + ") " + evaluated );
303         }
304     }
305 
306     public List<String> getElementListText( String xpathExpr )
307         throws XMLException
308     {
309         List<Element> elemList = getElementList( xpathExpr );
310         if ( elemList == null )
311         {
312             return null;
313         }
314 
315         List<String> ret = new ArrayList<>();
316         for ( Iterator<Element> iter = elemList.iterator(); iter.hasNext(); )
317         {
318             Element listelem = iter.next();
319             ret.add( listelem.getTextTrim() );
320         }
321         return ret;
322     }
323 
324 }