1 16 17 import java.io.File ; 18 import java.io.IOException ; 19 import java.net.UnknownHostException ; 20 import java.util.ArrayList ; 21 import java.util.Iterator ; 22 23 import javax.xml.transform.TransformerException ; 24 25 import org.apache.tools.ant.BuildException; 26 import org.apache.tools.ant.DirectoryScanner; 27 import org.apache.tools.ant.Project; 28 import org.apache.tools.ant.taskdefs.MatchingTask; 29 import org.apache.tools.ant.types.XMLCatalog; 30 import org.apache.xpath.XPathAPI; 31 import org.w3c.dom.Attr ; 32 import org.w3c.dom.DOMException ; 33 import org.w3c.dom.Document ; 34 import org.w3c.dom.Element ; 35 import org.w3c.dom.NamedNodeMap ; 36 import org.w3c.dom.Node ; 37 import org.w3c.dom.NodeList ; 38 import org.xml.sax.SAXException ; 39 40 64 public final class XConfToolTask extends MatchingTask { 65 66 private static final String NL=System.getProperty("line.separator"); 67 private static final String FSEP=System.getProperty("file.separator"); 68 69 private File file; 70 private File srcdir; 72 private boolean addComments; 73 74 private XMLCatalog xmlCatalog = new XMLCatalog(); 75 76 81 public void setFile(File file) { 82 this.file = file; 83 } 84 85 90 public void setSrcdir(File srcdir) { 91 this.srcdir = srcdir; 92 } 93 94 99 public void addConfiguredXMLCatalog(XMLCatalog newXMLCatalog) { 100 this.xmlCatalog.addConfiguredXMLCatalog(newXMLCatalog); 101 } 102 103 107 public void setAddComments(Boolean addComments) { 108 this.addComments = addComments.booleanValue(); 109 } 110 111 114 public void init() throws BuildException { 115 super.init(); 116 xmlCatalog.setProject(this.getProject()); 117 } 118 119 122 public void execute() throws BuildException { 123 if (this.file == null) { 124 throw new BuildException("file attribute is required", this.getLocation()); 125 } 126 try { 127 Document document = DocumentCache.getDocument(this.file, this); 128 129 if (this.srcdir == null) { 130 this.srcdir = this.getProject().resolveFile("."); 131 } 132 133 DirectoryScanner scanner = getDirectoryScanner(this.srcdir); 134 String [] list = scanner.getIncludedFiles(); 135 boolean modified = false; 136 File patchfile; 138 ArrayList suspended = new ArrayList (); 139 boolean hasChanged = false; 140 for (int i = 0; i < list.length; i++) { 141 patchfile = new File (this.srcdir, list[i]); 142 try { 143 boolean changed = patch(document, patchfile); 145 hasChanged |= changed; 146 if (!changed) { 147 suspended.add(patchfile); 148 } 149 } catch (SAXException e) { 150 log("Ignoring: "+patchfile+"\n(not a valid XML)"); 151 } 152 } 153 modified = hasChanged; 154 155 if (hasChanged && !suspended.isEmpty()) { 156 log("Try to apply suspended patch files", Project.MSG_DEBUG); 157 } 158 159 ArrayList newSuspended = new ArrayList (); 160 while (hasChanged && !suspended.isEmpty()) { 161 hasChanged = false; 162 for(Iterator i=suspended.iterator(); i.hasNext();) { 163 patchfile = (File )i.next(); 164 try { 165 boolean changed = patch(document, patchfile); 167 hasChanged |= changed; 168 if (!changed) { 169 newSuspended.add(patchfile); 170 } 171 } catch (SAXException e) { 172 log("Ignoring: "+patchfile+"\n(not a valid XML)"); 173 } 174 } 175 suspended = newSuspended; 176 newSuspended = new ArrayList (); 177 } 178 179 if (!suspended.isEmpty()) { 180 for(Iterator i=suspended.iterator(); i.hasNext();) { 181 patchfile = (File )i.next(); 182 log("Dismiss: "+patchfile.toString(), Project.MSG_DEBUG); 183 } 184 } 185 186 if (modified) { 187 DocumentCache.writeDocument(this.file, document, this); 188 } else { 189 log("No Changes: " + this.file, Project.MSG_DEBUG); 190 } 191 DocumentCache.storeDocument(this.file, document, this); 192 } catch (TransformerException e) { 193 throw new BuildException("TransformerException: "+e); 194 } catch (SAXException e) { 195 throw new BuildException("SAXException:" +e); 196 } catch (DOMException e) { 197 throw new BuildException("DOMException:" +e); 198 } catch (UnknownHostException e) { 199 throw new BuildException("UnknownHostException. Probable cause: The parser is " + 200 "trying to resolve a dtd from the internet and no connection exists.\n" + 201 "You can either connect to the internet during the build, or patch \n" + 202 "XConfToolTask.java to ignore DTD declarations when your parser is in use."); 203 } catch (IOException ioe) { 204 throw new BuildException("IOException: "+ioe); 205 } 206 } 207 208 217 private boolean patch(final Document configuration, 218 final File patchFile) 219 throws TransformerException , IOException , DOMException , SAXException { 220 221 Document component = DocumentCache.getDocument(patchFile, this); 222 String filename = patchFile.toString(); 223 224 Element elem = component.getDocumentElement(); 226 227 String extension = filename.lastIndexOf(".")>0?filename.substring(filename.lastIndexOf(".")+1):""; 228 String basename = basename(filename); 229 230 if (!elem.getTagName().equals(extension)) { 231 throw new BuildException("Not a valid xpatch file: "+filename); 232 } 233 234 String replacePropertiesStr = elem.getAttribute("replace-properties"); 235 236 boolean replaceProperties = !("no".equalsIgnoreCase(replacePropertiesStr) || 237 "false".equalsIgnoreCase(replacePropertiesStr)); 238 239 String xpath = getAttribute(elem, "xpath", replaceProperties); 241 if ( xpath == null ) { 242 throw new IOException ("Attribute 'xpath' is required."); 243 } 244 NodeList nodes = XPathAPI.selectNodeList(configuration, xpath); 245 246 if (nodes.getLength() !=1 ) { 248 log("Suspending: "+filename, Project.MSG_DEBUG); 249 return false; 250 } 251 Node root = nodes.item(0); 252 253 String testPath = getAttribute(elem, "unless-path", replaceProperties); 255 if (testPath == null || testPath.length() == 0) { 256 testPath = getAttribute(elem, "unless", replaceProperties); 258 } 259 String ifProp = getAttribute(elem, "if-prop", replaceProperties); 261 boolean ifValue = false; 262 if (ifProp != null && !ifProp.equals("")) { 263 ifValue = Boolean.valueOf(this.getProject().getProperty(ifProp)).booleanValue(); 264 } 265 266 if (ifProp != null && ifProp.length() > 0 && !ifValue ) { 267 log("Skipping: " + filename, Project.MSG_DEBUG); 268 return false; 269 } else if (testPath != null && testPath.length() > 0 && 270 XPathAPI.eval(root, testPath).bool()) { 271 log("Skipping: " + filename, Project.MSG_DEBUG); 272 return false; 273 } else { 274 xpath = getAttribute(elem, "remove", replaceProperties); 276 277 if (xpath != null && xpath.length() > 0) { 278 nodes = XPathAPI.selectNodeList(configuration, xpath); 279 280 for (int i = 0, length = nodes.getLength(); i<length; i++) { 281 Node node = nodes.item(i); 282 Node parent = node.getParentNode(); 283 284 parent.removeChild(node); 285 } 286 } 287 288 String name = getAttribute(elem, "add-attribute", replaceProperties); 290 String value = getAttribute(elem, "value", replaceProperties); 291 292 if (name != null && name.length() > 0) { 293 if (value == null) { 294 throw new IOException ("No attribute value specified for 'add-attribute' "+ 295 xpath); 296 } 297 if (root instanceof Element ) { 298 ((Element ) root).setAttribute(name, value); 299 } 300 } 301 302 String addCommentsAttr = getAttribute(elem, "add-comments", replaceProperties); 304 if ((addCommentsAttr!=null) && (addCommentsAttr.length()>0)) { 305 setAddComments(new Boolean (addCommentsAttr)); 306 } 307 308 if (root instanceof Element ) { 310 NamedNodeMap attrMap = elem.getAttributes(); 311 for (int i=0; i<attrMap.getLength(); ++i){ 312 Attr attr = (Attr )attrMap.item(i); 313 final String addAttr = "add-attribute-"; 314 if (attr.getName().startsWith(addAttr)) { 315 String key = attr.getName().substring(addAttr.length()); 316 ((Element ) root).setAttribute(key, attr.getValue()); 317 } 318 } 319 } 320 321 xpath = getAttribute(elem, "insert-before", replaceProperties); 323 Node before = null; 324 325 if (xpath != null && xpath.length() > 0) { 326 nodes = XPathAPI.selectNodeList(root, xpath); 327 if (nodes.getLength() == 0) { 328 log("Error in: "+filename); 329 throw new IOException ("XPath ("+xpath+") returned zero nodes"); 330 } 331 before = nodes.item(0); 332 } else { 333 xpath = getAttribute(elem, "insert-after", replaceProperties); 334 if (xpath != null && xpath.length() > 0) { 335 nodes = XPathAPI.selectNodeList(root, xpath); 336 if (nodes.getLength() == 0) { 337 log("Error in: "+filename); 338 throw new IOException ("XPath ("+xpath+") zero nodes."); 339 } 340 before = nodes.item(nodes.getLength()-1).getNextSibling(); 341 } 342 } 343 344 log("Processing: "+filename); 346 NodeList componentNodes = component.getDocumentElement().getChildNodes(); 347 348 if (this.addComments) { 349 root.appendChild(configuration.createComment("..... Start configuration from '"+basename+"' ")); 350 root.appendChild(configuration.createTextNode(NL)); 351 } 352 for (int i = 0; i<componentNodes.getLength(); i++) { 353 Node node = configuration.importNode(componentNodes.item(i), 354 true); 355 356 if (replaceProperties) { 357 replaceProperties(node); 358 } 359 if (before==null) { 360 root.appendChild(node); 361 } else { 362 root.insertBefore(node, before); 363 } 364 } 365 if (this.addComments) { 366 root.appendChild(configuration.createComment("..... End configuration from '"+basename+"' ")); 367 root.appendChild(configuration.createTextNode(NL)); 368 } 369 return true; 370 } 371 } 372 373 private String getAttribute(Element elem, String attrName, boolean replaceProperties) { 374 String attr = elem.getAttribute(attrName); 375 if (attr == null) { 376 return null; 377 } else if (replaceProperties) { 378 return getProject().replaceProperties(attr); 379 } else { 380 return attr; 381 } 382 } 383 384 private void replaceProperties(Node n) throws DOMException { 385 NamedNodeMap attrs = n.getAttributes(); 386 if (attrs != null) { 387 for (int i = 0; i < attrs.getLength(); i++) { 388 Node attr = attrs.item(i); 389 attr.setNodeValue(getProject().replaceProperties(attr.getNodeValue())); 390 } 391 } 392 switch (n.getNodeType()) { 393 case Node.ATTRIBUTE_NODE: 394 case Node.CDATA_SECTION_NODE: 395 case Node.TEXT_NODE: { 396 n.setNodeValue(getProject().replaceProperties(n.getNodeValue())); 397 break; 398 } 399 case Node.DOCUMENT_NODE: 400 case Node.DOCUMENT_FRAGMENT_NODE: 401 case Node.ELEMENT_NODE: { 402 Node child = n.getFirstChild(); 403 while (child != null) { 404 replaceProperties(child); 405 child = child.getNextSibling(); 406 } 407 break; 408 } 409 default: { 410 } 412 } 413 } 414 415 416 private String basename(String fileName) { 417 int start = fileName.lastIndexOf(FSEP)+1; int end = fileName.lastIndexOf("."); 420 if (end == 0) { 421 end = fileName.length(); 422 } 423 return fileName.substring(start, end); 424 } 425 } 426 | Popular Tags |