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