001    package org.apache.archiva.xml;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *  http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import org.apache.commons.io.IOUtils;
023    import org.apache.commons.lang.StringUtils;
024    import org.dom4j.Attribute;
025    import org.dom4j.Document;
026    import org.dom4j.DocumentException;
027    import org.dom4j.Element;
028    import org.dom4j.Namespace;
029    import org.dom4j.Node;
030    import org.dom4j.QName;
031    import org.dom4j.XPath;
032    import org.dom4j.io.SAXReader;
033    
034    import java.io.File;
035    import java.io.IOException;
036    import java.io.InputStream;
037    import java.io.InputStreamReader;
038    import java.net.MalformedURLException;
039    import java.net.URL;
040    import java.util.ArrayList;
041    import java.util.HashMap;
042    import java.util.Iterator;
043    import java.util.List;
044    import java.util.Map;
045    
046    /**
047     * XMLReader - a set of common xml utility methods for reading content out of an xml file. 
048     *
049     *
050     */
051    public class XMLReader
052    {
053        private URL xmlUrl;
054    
055        private String documentType;
056    
057        private Document document;
058    
059        private Map<String, String> namespaceMap = new HashMap<String, String>();
060    
061        public XMLReader( String type, File file )
062            throws XMLException
063        {
064            if ( !file.exists() )
065            {
066                throw new XMLException( "file does not exist: " + file.getAbsolutePath() );
067            }
068    
069            if ( !file.isFile() )
070            {
071                throw new XMLException( "path is not a file: " + file.getAbsolutePath() );
072            }
073    
074            if ( !file.canRead() )
075            {
076                throw new XMLException( "Cannot read xml file due to permissions: " + file.getAbsolutePath() );
077            }
078    
079            try
080            {
081                init( type, file.toURL() );
082            }
083            catch ( MalformedURLException e )
084            {
085                throw new XMLException( "Unable to translate file " + file + " to URL: " + e.getMessage(), e );
086            }
087        }
088    
089        public XMLReader( String type, URL url )
090            throws XMLException
091        {
092            init( type, url );
093        }
094    
095        private void init( String type, URL url )
096            throws XMLException
097        {
098            this.documentType = type;
099            this.xmlUrl = url;
100    
101            InputStream in = null;
102            SAXReader reader = new SAXReader();
103            
104            try
105            {
106                in = url.openStream();
107                InputStreamReader inReader = new InputStreamReader( in, "UTF-8" );
108                LatinEntityResolutionReader latinReader = new LatinEntityResolutionReader( inReader );
109                this.document = reader.read( latinReader );
110            }
111            catch ( DocumentException e )
112            {
113                throw new XMLException( "Unable to parse " + documentType + " xml " + xmlUrl + ": " + e.getMessage(), e );
114            }
115            catch ( IOException e )
116            {
117                throw new XMLException( "Unable to open stream to " + url + ": " + e.getMessage(), e );
118            }
119            finally
120            {
121                IOUtils.closeQuietly( in );
122            }
123    
124            Element root = this.document.getRootElement();
125            if ( root == null )
126            {
127                throw new XMLException( "Invalid " + documentType + " xml: root element is null." );
128            }
129    
130            if ( !StringUtils.equals( root.getName(), documentType ) )
131            {
132                throw new XMLException( "Invalid " + documentType + " xml: Unexpected root element <" + root.getName()
133                    + ">, expected <" + documentType + ">" );
134            }
135        }
136    
137        public String getDefaultNamespaceURI()
138        {
139            Namespace namespace = this.document.getRootElement().getNamespace();
140            return namespace.getURI();
141        }
142    
143        public void addNamespaceMapping( String elementName, String uri )
144        {
145            this.namespaceMap.put( elementName, uri );
146        }
147    
148        public Element getElement( String xpathExpr )
149            throws XMLException
150        {
151            XPath xpath = createXPath( xpathExpr );
152            Object evaluated = xpath.selectSingleNode( document );
153    
154            if ( evaluated == null )
155            {
156                return null;
157            }
158    
159            if ( evaluated instanceof Element )
160            {
161                return (Element) evaluated;
162            }
163            else
164            {
165                // Unknown evaluated type.
166                throw new XMLException( ".getElement( Expr: " + xpathExpr + " ) resulted in non-Element type -> ("
167                    + evaluated.getClass().getName() + ") " + evaluated );
168            }
169        }
170    
171        private XPath createXPath( String xpathExpr )
172        {
173            XPath xpath = document.createXPath( xpathExpr );
174            if ( !this.namespaceMap.isEmpty() )
175            {
176                xpath.setNamespaceURIs( this.namespaceMap );
177            }
178            return xpath;
179        }
180    
181        public boolean hasElement( String xpathExpr )
182            throws XMLException
183        {
184            XPath xpath = createXPath( xpathExpr );
185            Object evaluated = xpath.selectSingleNode( document );
186    
187            if ( evaluated == null )
188            {
189                return false;
190            }
191    
192            return true;
193        }
194    
195        /**
196         * Remove namespaces from entire document.
197         */
198        public void removeNamespaces()
199        {
200            removeNamespaces( this.document.getRootElement() );
201        }
202    
203        /**
204         * Remove namespaces from element recursively.
205         */
206        @SuppressWarnings("unchecked")
207        public void removeNamespaces( Element elem )
208        {
209            elem.setQName( QName.get( elem.getName(), Namespace.NO_NAMESPACE, elem.getQualifiedName() ) );
210    
211            Node n;
212    
213            Iterator<Node> it = elem.elementIterator();
214            while ( it.hasNext() )
215            {
216                n = it.next();
217    
218                switch ( n.getNodeType() )
219                {
220                    case Node.ATTRIBUTE_NODE:
221                        ( (Attribute) n ).setNamespace( Namespace.NO_NAMESPACE );
222                        break;
223                    case Node.ELEMENT_NODE:
224                        removeNamespaces( (Element) n );
225                        break;
226                }
227            }
228        }
229    
230        public String getElementText( Node context, String xpathExpr )
231            throws XMLException
232        {
233            XPath xpath = createXPath( xpathExpr );
234            Object evaluated = xpath.selectSingleNode( context );
235    
236            if ( evaluated == null )
237            {
238                return null;
239            }
240    
241            if ( evaluated instanceof Element )
242            {
243                Element evalElem = (Element) evaluated;
244                return evalElem.getTextTrim();
245            }
246            else
247            {
248                // Unknown evaluated type.
249                throw new XMLException( ".getElementText( Node, Expr: " + xpathExpr
250                    + " ) resulted in non-Element type -> (" + evaluated.getClass().getName() + ") " + evaluated );
251            }
252        }
253    
254        public String getElementText( String xpathExpr )
255            throws XMLException
256        {
257            XPath xpath = createXPath( xpathExpr );
258            Object evaluated = xpath.selectSingleNode( document );
259    
260            if ( evaluated == null )
261            {
262                return null;
263            }
264    
265            if ( evaluated instanceof Element )
266            {
267                Element evalElem = (Element) evaluated;
268                return evalElem.getTextTrim();
269            }
270            else
271            {
272                // Unknown evaluated type.
273                throw new XMLException( ".getElementText( Expr: " + xpathExpr + " ) resulted in non-Element type -> ("
274                    + evaluated.getClass().getName() + ") " + evaluated );
275            }
276        }
277    
278        @SuppressWarnings("unchecked")
279        public List<Element> getElementList( String xpathExpr )
280            throws XMLException
281        {
282            XPath xpath = createXPath( xpathExpr );
283            Object evaluated = xpath.evaluate( document );
284    
285            if ( evaluated == null )
286            {
287                return null;
288            }
289    
290            /* The xpath.evaluate(Context) method can return:
291             *   1) A Collection or List of dom4j Nodes. 
292             *   2) A single dom4j Node.
293             */
294    
295            if ( evaluated instanceof List )
296            {
297                return (List<Element>) evaluated;
298            }
299            else if ( evaluated instanceof Node )
300            {
301                List<Element> ret = new ArrayList<Element>();
302                ret.add( (Element) evaluated );
303                return ret;
304            }
305            else
306            {
307                // Unknown evaluated type.
308                throw new XMLException( ".getElementList( Expr: " + xpathExpr + " ) resulted in non-List type -> ("
309                    + evaluated.getClass().getName() + ") " + evaluated );
310            }
311        }
312    
313        public List<String> getElementListText( String xpathExpr )
314            throws XMLException
315        {
316            List<Element> elemList = getElementList( xpathExpr );
317            if ( elemList == null )
318            {
319                return null;
320            }
321    
322            List<String> ret = new ArrayList<String>();
323            for ( Iterator<Element> iter = elemList.iterator(); iter.hasNext(); )
324            {
325                Element listelem = iter.next();
326                ret.add( listelem.getTextTrim() );
327            }
328            return ret;
329        }
330    
331    }