KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > AntStructure


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

18
19 package org.apache.tools.ant.taskdefs;
20
21 import java.io.File JavaDoc;
22 import java.io.FileOutputStream JavaDoc;
23 import java.io.FileWriter JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.OutputStreamWriter JavaDoc;
26 import java.io.PrintWriter JavaDoc;
27 import java.io.UnsupportedEncodingException JavaDoc;
28 import java.util.Enumeration JavaDoc;
29 import java.util.Hashtable JavaDoc;
30 import java.util.Vector JavaDoc;
31 import org.apache.tools.ant.BuildException;
32 import org.apache.tools.ant.IntrospectionHelper;
33 import org.apache.tools.ant.Project;
34 import org.apache.tools.ant.Task;
35 import org.apache.tools.ant.TaskContainer;
36 import org.apache.tools.ant.types.EnumeratedAttribute;
37 import org.apache.tools.ant.types.Reference;
38
39 /**
40  * Creates a partial DTD for Ant from the currently known tasks.
41  *
42  *
43  * @since Ant 1.1
44  *
45  * @ant.task category="xml"
46  */

47 public class AntStructure extends Task {
48
49     private static final String JavaDoc LINE_SEP
50         = System.getProperty("line.separator");
51
52     private File JavaDoc output;
53     private StructurePrinter printer = new DTDPrinter();
54
55     /**
56      * The output file.
57      * @param output the output file
58      */

59     public void setOutput(File JavaDoc output) {
60         this.output = output;
61     }
62
63     /**
64      * The StructurePrinter to use.
65      * @param p the printer to use.
66      * @since Ant 1.7
67      */

68     public void add(StructurePrinter p) {
69         printer = p;
70     }
71
72     /**
73      * Build the antstructure DTD.
74      *
75      * @exception BuildException if the DTD cannot be written.
76      */

77     public void execute() throws BuildException {
78
79         if (output == null) {
80             throw new BuildException("output attribute is required", getLocation());
81         }
82
83         PrintWriter JavaDoc out = null;
84         try {
85             try {
86                 out = new PrintWriter JavaDoc(new OutputStreamWriter JavaDoc(new FileOutputStream JavaDoc(output), "UTF8"));
87             } catch (UnsupportedEncodingException JavaDoc ue) {
88                 /*
89                  * Plain impossible with UTF8, see
90                  * http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html
91                  *
92                  * fallback to platform specific anyway.
93                  */

94                 out = new PrintWriter JavaDoc(new FileWriter JavaDoc(output));
95             }
96
97             printer.printHead(out, getProject(),
98                               getProject().getTaskDefinitions(),
99                               getProject().getDataTypeDefinitions());
100
101             printer.printTargetDecl(out);
102
103             Enumeration JavaDoc dataTypes = getProject().getDataTypeDefinitions().keys();
104             while (dataTypes.hasMoreElements()) {
105                 String JavaDoc typeName = (String JavaDoc) dataTypes.nextElement();
106                 printer.printElementDecl(
107                     out, getProject(), typeName,
108                     (Class JavaDoc) getProject().getDataTypeDefinitions().get(typeName));
109             }
110
111             Enumeration JavaDoc tasks = getProject().getTaskDefinitions().keys();
112             while (tasks.hasMoreElements()) {
113                 String JavaDoc tName = (String JavaDoc) tasks.nextElement();
114                 printer.printElementDecl(out, getProject(), tName,
115                                          (Class JavaDoc) getProject().getTaskDefinitions().get(tName));
116             }
117
118             printer.printTail(out);
119
120         } catch (IOException JavaDoc ioe) {
121             throw new BuildException("Error writing "
122                                      + output.getAbsolutePath(), ioe, getLocation());
123         } finally {
124             if (out != null) {
125                 out.close();
126             }
127         }
128     }
129
130     /**
131      * Writes the actual structure information.
132      *
133      * <p>{@link #printHead}, {@link #printTargetDecl} and {@link #printTail}
134      * are called exactly once, {@link #printElementDecl} once for
135      * each declared task and type.</p>
136      */

137     public static interface StructurePrinter {
138         /**
139          * Prints the header of the generated output.
140          *
141          * @param out PrintWriter to write to.
142          * @param p Project instance for the current task
143          * @param tasks map (name to implementing class)
144          * @param types map (name to implementing class)
145          * data types.
146          */

147         void printHead(PrintWriter JavaDoc out, Project p, Hashtable JavaDoc tasks,
148                        Hashtable JavaDoc types);
149
150         /**
151          * Prints the definition for the target element.
152          * @param out PrintWriter to write to.
153          */

154         void printTargetDecl(PrintWriter JavaDoc out);
155
156         /**
157          * Print the definition for a given element.
158          *
159          * @param out PrintWriter to write to.
160          * @param p Project instance for the current task
161          * @param name element name.
162          * @param element class of the defined element.
163          */

164         void printElementDecl(PrintWriter JavaDoc out, Project p, String JavaDoc name,
165                               Class JavaDoc element);
166
167         /**
168          * Prints the trailer.
169          * @param out PrintWriter to write to.
170          */

171         void printTail(PrintWriter JavaDoc out);
172     }
173
174     private static class DTDPrinter implements StructurePrinter {
175
176         private static final String JavaDoc BOOLEAN = "%boolean;";
177         private static final String JavaDoc TASKS = "%tasks;";
178         private static final String JavaDoc TYPES = "%types;";
179
180         private Hashtable JavaDoc visited = new Hashtable JavaDoc();
181
182         public void printTail(PrintWriter JavaDoc out) {
183             visited.clear();
184         }
185
186         public void printHead(PrintWriter JavaDoc out, Project p, Hashtable JavaDoc tasks, Hashtable JavaDoc types) {
187             printHead(out, tasks.keys(), types.keys());
188         }
189
190
191         /**
192          * Prints the header of the generated output.
193          *
194          * <p>Basically this prints the XML declaration, defines some
195          * entities and the project element.</p>
196          */

197         private void printHead(PrintWriter JavaDoc out, Enumeration JavaDoc tasks,
198                                Enumeration JavaDoc types) {
199             out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
200             out.println("<!ENTITY % boolean \"(true|false|on|off|yes|no)\">");
201             out.print("<!ENTITY % tasks \"");
202             boolean first = true;
203             while (tasks.hasMoreElements()) {
204                 String JavaDoc tName = (String JavaDoc) tasks.nextElement();
205                 if (!first) {
206                     out.print(" | ");
207                 } else {
208                     first = false;
209                 }
210                 out.print(tName);
211             }
212             out.println("\">");
213             out.print("<!ENTITY % types \"");
214             first = true;
215             while (types.hasMoreElements()) {
216                 String JavaDoc typeName = (String JavaDoc) types.nextElement();
217                 if (!first) {
218                     out.print(" | ");
219                 } else {
220                     first = false;
221                 }
222                 out.print(typeName);
223             }
224             out.println("\">");
225
226             out.println("");
227
228             out.print("<!ELEMENT project (target | ");
229             out.print(TASKS);
230             out.print(" | ");
231             out.print(TYPES);
232             out.println(")*>");
233             out.println("<!ATTLIST project");
234             out.println(" name CDATA #IMPLIED");
235             out.println(" default CDATA #IMPLIED");
236             out.println(" basedir CDATA #IMPLIED>");
237             out.println("");
238         }
239
240         /**
241          * Prints the definition for the target element.
242          */

243         public void printTargetDecl(PrintWriter JavaDoc out) {
244             out.print("<!ELEMENT target (");
245             out.print(TASKS);
246             out.print(" | ");
247             out.print(TYPES);
248             out.println(")*>");
249             out.println("");
250
251             out.println("<!ATTLIST target");
252             out.println(" id ID #IMPLIED");
253             out.println(" name CDATA #REQUIRED");
254             out.println(" if CDATA #IMPLIED");
255             out.println(" unless CDATA #IMPLIED");
256             out.println(" depends CDATA #IMPLIED");
257             out.println(" description CDATA #IMPLIED>");
258             out.println("");
259         }
260
261         /**
262          * Print the definition for a given element.
263          */

264         public void printElementDecl(PrintWriter JavaDoc out, Project p,
265                                      String JavaDoc name, Class JavaDoc element) {
266
267             if (visited.containsKey(name)) {
268                 return;
269             }
270             visited.put(name, "");
271
272             IntrospectionHelper ih = null;
273             try {
274                 ih = IntrospectionHelper.getHelper(p, element);
275             } catch (Throwable JavaDoc t) {
276                 /*
277                  * XXX - failed to load the class properly.
278                  *
279                  * should we print a warning here?
280                  */

281                 return;
282             }
283
284             StringBuffer JavaDoc sb = new StringBuffer JavaDoc("<!ELEMENT ");
285             sb.append(name).append(" ");
286
287             if (org.apache.tools.ant.types.Reference.class.equals(element)) {
288                 sb.append("EMPTY>").append(LINE_SEP);
289                 sb.append("<!ATTLIST ").append(name);
290                 sb.append(LINE_SEP).append(" id ID #IMPLIED");
291                 sb.append(LINE_SEP).append(" refid IDREF #IMPLIED");
292                 sb.append(">").append(LINE_SEP);
293                 out.println(sb);
294                 return;
295             }
296
297             Vector JavaDoc v = new Vector JavaDoc();
298             if (ih.supportsCharacters()) {
299                 v.addElement("#PCDATA");
300             }
301
302             if (TaskContainer.class.isAssignableFrom(element)) {
303                 v.addElement(TASKS);
304             }
305
306             Enumeration JavaDoc e = ih.getNestedElements();
307             while (e.hasMoreElements()) {
308                 v.addElement(e.nextElement());
309             }
310
311             if (v.isEmpty()) {
312                 sb.append("EMPTY");
313             } else {
314                 sb.append("(");
315                 final int count = v.size();
316                 for (int i = 0; i < count; i++) {
317                     if (i != 0) {
318                         sb.append(" | ");
319                     }
320                     sb.append(v.elementAt(i));
321                 }
322                 sb.append(")");
323                 if (count > 1 || !v.elementAt(0).equals("#PCDATA")) {
324                     sb.append("*");
325                 }
326             }
327             sb.append(">");
328             out.println(sb);
329
330             sb = new StringBuffer JavaDoc("<!ATTLIST ");
331             sb.append(name);
332             sb.append(LINE_SEP).append(" id ID #IMPLIED");
333
334             e = ih.getAttributes();
335             while (e.hasMoreElements()) {
336                 String JavaDoc attrName = (String JavaDoc) e.nextElement();
337                 if ("id".equals(attrName)) {
338                     continue;
339                 }
340
341                 sb.append(LINE_SEP).append(" ")
342                     .append(attrName).append(" ");
343                 Class JavaDoc type = ih.getAttributeType(attrName);
344                 if (type.equals(java.lang.Boolean JavaDoc.class)
345                     || type.equals(java.lang.Boolean.TYPE)) {
346                     sb.append(BOOLEAN).append(" ");
347                 } else if (Reference.class.isAssignableFrom(type)) {
348                     sb.append("IDREF ");
349                 } else if (EnumeratedAttribute.class.isAssignableFrom(type)) {
350                     try {
351                         EnumeratedAttribute ea =
352                             (EnumeratedAttribute) type.newInstance();
353                         String JavaDoc[] values = ea.getValues();
354                         if (values == null
355                             || values.length == 0
356                             || !areNmtokens(values)) {
357                             sb.append("CDATA ");
358                         } else {
359                             sb.append("(");
360                             for (int i = 0; i < values.length; i++) {
361                                 if (i != 0) {
362                                     sb.append(" | ");
363                                 }
364                                 sb.append(values[i]);
365                             }
366                             sb.append(") ");
367                         }
368                     } catch (InstantiationException JavaDoc ie) {
369                         sb.append("CDATA ");
370                     } catch (IllegalAccessException JavaDoc ie) {
371                         sb.append("CDATA ");
372                     }
373                 } else if (type.getSuperclass() != null
374                            && type.getSuperclass().getName().equals("java.lang.Enum")) {
375                     try {
376                         Object JavaDoc[] values = (Object JavaDoc[]) type.getMethod("values", (Class JavaDoc[]) null)
377                             .invoke(null, (Object JavaDoc[]) null);
378                         if (values.length == 0) {
379                             sb.append("CDATA ");
380                         } else {
381                             sb.append('(');
382                             for (int i = 0; i < values.length; i++) {
383                                 if (i != 0) {
384                                     sb.append(" | ");
385                                 }
386                                 sb.append(type.getMethod("name", (Class JavaDoc[]) null)
387                                           .invoke(values[i], (Object JavaDoc[]) null));
388                             }
389                             sb.append(") ");
390                         }
391                     } catch (Exception JavaDoc x) {
392                         sb.append("CDATA ");
393                     }
394                 } else {
395                     sb.append("CDATA ");
396                 }
397                 sb.append("#IMPLIED");
398             }
399             sb.append(">").append(LINE_SEP);
400             out.println(sb);
401
402             final int count = v.size();
403             for (int i = 0; i < count; i++) {
404                 String JavaDoc nestedName = (String JavaDoc) v.elementAt(i);
405                 if (!"#PCDATA".equals(nestedName)
406                     && !TASKS.equals(nestedName)
407                     && !TYPES.equals(nestedName)) {
408                     printElementDecl(out, p, nestedName, ih.getElementType(nestedName));
409                 }
410             }
411         }
412
413         /**
414          * Does this String match the XML-NMTOKEN production?
415          * @param s the string to test
416          * @return true if the string matches the XML-NMTOKEN
417          */

418         public static final boolean isNmtoken(String JavaDoc s) {
419             final int length = s.length();
420             for (int i = 0; i < length; i++) {
421                 char c = s.charAt(i);
422                 // XXX - we are committing CombiningChar and Extender here
423
if (!Character.isLetterOrDigit(c)
424                     && c != '.' && c != '-' && c != '_' && c != ':') {
425                     return false;
426                 }
427             }
428             return true;
429         }
430
431         /**
432          * Do the Strings all match the XML-NMTOKEN production?
433          *
434          * <p>Otherwise they are not suitable as an enumerated attribute,
435          * for example.</p>
436          * @param s the array of string to test
437          * @return true if all the strings in the array math XML-NMTOKEN
438          */

439         public static final boolean areNmtokens(String JavaDoc[] s) {
440             for (int i = 0; i < s.length; i++) {
441                 if (!isNmtoken(s[i])) {
442                     return false;
443                 }
444             }
445             return true;
446         }
447     }
448
449     /**
450      * Does this String match the XML-NMTOKEN production?
451      * @param s the string to test
452      * @return true if the string matches the XML-NMTOKEN
453      */

454     protected boolean isNmtoken(String JavaDoc s) {
455         return DTDPrinter.isNmtoken(s);
456     }
457
458     /**
459      * Do the Strings all match the XML-NMTOKEN production?
460      *
461      * <p>Otherwise they are not suitable as an enumerated attribute,
462      * for example.</p>
463      * @param s the array of string to test
464      * @return true if all the strings in the array math XML-NMTOKEN
465      */

466     protected boolean areNmtokens(String JavaDoc[] s) {
467         return DTDPrinter.areNmtokens(s);
468     }
469 }
470
Popular Tags