KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > XConfToolTask


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 import java.io.File JavaDoc;
18 import java.io.IOException JavaDoc;
19 import java.net.UnknownHostException JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Iterator JavaDoc;
22
23 import javax.xml.transform.TransformerException JavaDoc;
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 JavaDoc;
32 import org.w3c.dom.DOMException JavaDoc;
33 import org.w3c.dom.Document JavaDoc;
34 import org.w3c.dom.Element JavaDoc;
35 import org.w3c.dom.NamedNodeMap JavaDoc;
36 import org.w3c.dom.Node JavaDoc;
37 import org.w3c.dom.NodeList JavaDoc;
38 import org.xml.sax.SAXException JavaDoc;
39
40 /**
41  * Ant task to patch xmlfiles.
42  *
43  *
44  * replace-properties no|false,anything else
45  * xpath: xpath expression for context node
46  * unless-path: xpath expression that must return empty node set
47  * unless: (deprecated) xpath expression that must return empty node set
48  * if-prop: use path file only when project property is set
49  * remove: xpath expression to remove before adding nodes
50  * add-comments: if specified, overrides the ant task value
51  * add-attribute: name of attribute to add to context node (requires value)
52  * add-attribute-<i>name</i>: add attribute <i>name</i> with the specified value
53  * value: value of attribute to add to context node (requires add-attribute)
54  * insert-before: xpath expression, add new nodes before
55  * insert-after: xpath expression, add new nodes after
56  *
57  * @author <a HREF="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
58  * @author <a HREF="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
59  * @author <a HREF="mailto:crafterm@fztig938.bank.dresdner.net">Marcus Crafter</a>
60  * @author <a HREF="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a>
61  * @author <a HREF="mailto:stephan@apache.org">Stephan Michels</a>
62  * @version CVS $Id: XConfToolTask.java 289417 2005-09-16 07:52:09Z sylvain $
63  */

64 public final class XConfToolTask extends MatchingTask {
65
66     private static final String JavaDoc NL=System.getProperty("line.separator");
67     private static final String JavaDoc FSEP=System.getProperty("file.separator");
68     
69     private File JavaDoc file;
70     //private File directory;
71
private File JavaDoc srcdir;
72     private boolean addComments;
73     /** for resolving entities such as dtds */
74     private XMLCatalog xmlCatalog = new XMLCatalog();
75
76     /**
77      * Set file, which should be patched.
78      *
79      * @param file File, which should be patched.
80      */

81     public void setFile(File JavaDoc file) {
82         this.file = file;
83     }
84
85     /**
86      * Set base directory for the patch files.
87      *
88      * @param srcdir Base directory for the patch files.
89      */

90     public void setSrcdir(File JavaDoc srcdir) {
91         this.srcdir = srcdir;
92     }
93
94     /**
95      * Add the catalog to our internal catalog
96      *
97      * @param xmlCatalog the XMLCatalog instance to use to look up DTDs
98      */

99     public void addConfiguredXMLCatalog(XMLCatalog newXMLCatalog) {
100         this.xmlCatalog.addConfiguredXMLCatalog(newXMLCatalog);
101     }
102
103     /**
104      * Whether to add a comment indicating where this block of code comes
105      * from.
106      */

107     public void setAddComments(Boolean JavaDoc addComments) {
108         this.addComments = addComments.booleanValue();
109     }
110
111     /**
112      * Initialize internal instance of XMLCatalog
113      */

114     public void init() throws BuildException {
115         super.init();
116         xmlCatalog.setProject(this.getProject());
117     }
118
119     /**
120      * Execute task.
121      */

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 JavaDoc 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 JavaDoc[] list = scanner.getIncludedFiles();
135             boolean modified = false;
136             // process recursive
137
File JavaDoc patchfile;
138             ArrayList JavaDoc suspended = new ArrayList JavaDoc();
139             boolean hasChanged = false;
140             for (int i = 0; i < list.length; i++) {
141                 patchfile = new File JavaDoc(this.srcdir, list[i]);
142                 try {
143                     // Adds configuration snippet from the file to the configuration
144
boolean changed = patch(document, patchfile);
145                     hasChanged |= changed;
146                     if (!changed) {
147                         suspended.add(patchfile);
148                     }
149                 } catch (SAXException JavaDoc 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 JavaDoc newSuspended = new ArrayList JavaDoc();
160             while (hasChanged && !suspended.isEmpty()) {
161                 hasChanged = false;
162                 for(Iterator JavaDoc i=suspended.iterator(); i.hasNext();) {
163                     patchfile = (File JavaDoc)i.next();
164                     try {
165                          // Adds configuration snippet from the file to the configuration
166
boolean changed = patch(document, patchfile);
167                         hasChanged |= changed;
168                         if (!changed) {
169                             newSuspended.add(patchfile);
170                         }
171                     } catch (SAXException JavaDoc e) {
172                         log("Ignoring: "+patchfile+"\n(not a valid XML)");
173                     }
174                 }
175                 suspended = newSuspended;
176                 newSuspended = new ArrayList JavaDoc();
177             }
178
179             if (!suspended.isEmpty()) {
180                 for(Iterator JavaDoc i=suspended.iterator(); i.hasNext();) {
181                     patchfile = (File JavaDoc)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 JavaDoc e) {
193             throw new BuildException("TransformerException: "+e);
194         } catch (SAXException JavaDoc e) {
195             throw new BuildException("SAXException:" +e);
196         } catch (DOMException JavaDoc e) {
197             throw new BuildException("DOMException:" +e);
198         } catch (UnknownHostException JavaDoc 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 JavaDoc ioe) {
204             throw new BuildException("IOException: "+ioe);
205         }
206     }
207
208     /**
209      * Patch XML document with a given patch file.
210      *
211      * @param configuration Orginal document
212      * @param component Patch document
213      * @param patchFile Patch file
214      *
215      * @return True, if the document was successfully patched
216      */

217     private boolean patch(final Document JavaDoc configuration,
218                            final File JavaDoc patchFile)
219                            throws TransformerException JavaDoc, IOException JavaDoc, DOMException JavaDoc, SAXException JavaDoc {
220
221         Document JavaDoc component = DocumentCache.getDocument(patchFile, this);
222         String JavaDoc filename = patchFile.toString();
223                             
224         // Check to see if Document is an xconf-tool document
225
Element JavaDoc elem = component.getDocumentElement();
226
227         String JavaDoc extension = filename.lastIndexOf(".")>0?filename.substring(filename.lastIndexOf(".")+1):"";
228         String JavaDoc basename = basename(filename);
229
230         if (!elem.getTagName().equals(extension)) {
231         throw new BuildException("Not a valid xpatch file: "+filename);
232         }
233
234         String JavaDoc replacePropertiesStr = elem.getAttribute("replace-properties");
235
236         boolean replaceProperties = !("no".equalsIgnoreCase(replacePropertiesStr) ||
237                                       "false".equalsIgnoreCase(replacePropertiesStr));
238
239         // Get 'root' node were 'component' will be inserted into
240
String JavaDoc xpath = getAttribute(elem, "xpath", replaceProperties);
241         if ( xpath == null ) {
242             throw new IOException JavaDoc("Attribute 'xpath' is required.");
243         }
244         NodeList JavaDoc nodes = XPathAPI.selectNodeList(configuration, xpath);
245
246         // Suspend, because the xpath returned not one node
247
if (nodes.getLength() !=1 ) {
248             log("Suspending: "+filename, Project.MSG_DEBUG);
249             return false;
250         }
251         Node JavaDoc root = nodes.item(0);
252
253         // Test that 'root' node satisfies 'component' insertion criteria
254
String JavaDoc testPath = getAttribute(elem, "unless-path", replaceProperties);
255         if (testPath == null || testPath.length() == 0) {
256             // only look for old "unless" attr if unless-path is not present
257
testPath = getAttribute(elem, "unless", replaceProperties);
258         }
259         // Is if-path needed?
260
String JavaDoc 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             // Test if component wants us to remove a list of nodes first
275
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 JavaDoc node = nodes.item(i);
282                     Node JavaDoc parent = node.getParentNode();
283
284                     parent.removeChild(node);
285                 }
286             }
287
288             // Test for an attribute that needs to be added to an element
289
String JavaDoc name = getAttribute(elem, "add-attribute", replaceProperties);
290             String JavaDoc value = getAttribute(elem, "value", replaceProperties);
291
292             if (name != null && name.length() > 0) {
293                 if (value == null) {
294                     throw new IOException JavaDoc("No attribute value specified for 'add-attribute' "+
295                                           xpath);
296                 }
297                 if (root instanceof Element JavaDoc) {
298                     ((Element JavaDoc) root).setAttribute(name, value);
299                 }
300             }
301  
302             // Override addComments from ant task if specified as an attribute
303
String JavaDoc addCommentsAttr = getAttribute(elem, "add-comments", replaceProperties);
304             if ((addCommentsAttr!=null) && (addCommentsAttr.length()>0)) {
305                 setAddComments(new Boolean JavaDoc(addCommentsAttr));
306             }
307
308             // Allow multiple attributes to be added or modified
309
if (root instanceof Element JavaDoc) {
310                 NamedNodeMap JavaDoc attrMap = elem.getAttributes();
311                 for (int i=0; i<attrMap.getLength(); ++i){
312                     Attr JavaDoc attr = (Attr JavaDoc)attrMap.item(i);
313                     final String JavaDoc addAttr = "add-attribute-";
314                     if (attr.getName().startsWith(addAttr)) {
315                         String JavaDoc key = attr.getName().substring(addAttr.length());
316                         ((Element JavaDoc) root).setAttribute(key, attr.getValue());
317                     }
318                 }
319             }
320
321             // Test if 'component' provides desired insertion point
322
xpath = getAttribute(elem, "insert-before", replaceProperties);
323             Node JavaDoc 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 JavaDoc("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 JavaDoc("XPath ("+xpath+") zero nodes.");
339                     }
340                     before = nodes.item(nodes.getLength()-1).getNextSibling();
341                 }
342             }
343
344             // Add 'component' data into 'root' node
345
log("Processing: "+filename);
346             NodeList JavaDoc 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 JavaDoc 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 JavaDoc getAttribute(Element JavaDoc elem, String JavaDoc attrName, boolean replaceProperties) {
374         String JavaDoc 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 JavaDoc n) throws DOMException JavaDoc {
385         NamedNodeMap JavaDoc attrs = n.getAttributes();
386         if (attrs != null) {
387             for (int i = 0; i < attrs.getLength(); i++) {
388                 Node JavaDoc 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 JavaDoc child = n.getFirstChild();
403                 while (child != null) {
404                     replaceProperties(child);
405                     child = child.getNextSibling();
406                 }
407                 break;
408             }
409             default: {
410                 // ignore all other node types
411
}
412         }
413     }
414
415     /** Returns the file name (excluding directories and extension). */
416     private String JavaDoc basename(String JavaDoc fileName) {
417         int start = fileName.lastIndexOf(FSEP)+1; // last '/'
418
int end = fileName.lastIndexOf("."); // last '.'
419

420         if (end == 0) {
421             end = fileName.length();
422         }
423         return fileName.substring(start, end);
424     }
425 }
426
Popular Tags