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 }