1 /*
2
3 */
4 package attrib4j.ant;
5
6 import java.io.File;
7 import java.io.FileWriter;
8 import java.io.FilenameFilter;
9 import java.io.IOException;
10 import java.io.PrintWriter;
11 import java.util.Enumeration;
12 import java.util.StringTokenizer;
13 import java.util.Vector;
14
15 import org.apache.tools.ant.BuildException;
16 import org.apache.tools.ant.DirectoryScanner;
17 import org.apache.tools.ant.Project;
18 import org.apache.tools.ant.Task;
19 import org.apache.tools.ant.taskdefs.Execute;
20 import org.apache.tools.ant.taskdefs.Javac;
21 import org.apache.tools.ant.taskdefs.LogOutputStream;
22 import org.apache.tools.ant.taskdefs.PumpStreamHandler;
23 import org.apache.tools.ant.types.Commandline;
24 import org.apache.tools.ant.types.DirSet;
25 import org.apache.tools.ant.types.FileSet;
26 import org.apache.tools.ant.types.Path;
27 import org.apache.tools.ant.types.PatternSet;
28 import org.apache.tools.ant.types.Reference;
29 import org.apache.tools.ant.util.FileUtils;
30 import org.apache.tools.ant.util.JavaEnvUtils;
31
32 /***
33 * A custom Ant task to run Attrib4 that supports the following syntax.
34 * <attrib4j sourcepath="${src}/main"
35 * classpath="${build}/classes"
36 * destdir="${dist}"
37 * loglevel="info"
38 * attributepackages="attrib4j.examples.attributes">
39 *
40 * It also is a helper class to the Attrib4jCompilerAdapter
41 *
42 * It will eventually support running both the Sun JDK javadoc engine
43 * and alternative javadoc engines. Now heavily influenced from the
44 * Ant javadoc task.
45 *
46 *
47 * @author Mark.Pollack
48 */
49 public class Attrib4jTask extends Task {
50
51 //Stuff specifc for using Sun JDK javadoc engine.
52
53 /***
54 * Used to track info about the packages to be javadoc'd
55 */
56 public static class PackageName {
57 /*** The package name *//package-summary/html">color="#AA0000">* The package name *//package-summary.html">/*** The package name *//package-summary.html">color="#AA0000">* The package name */
58 private String name;
59
60 /***
61 * Set the name of the package
62 *
63 * @param name the package name.
64 */
65 public void setName(String name) {
66 this.name = name.trim();
67 }
68
69 /***
70 * Get the package name.
71 *
72 * @return the package's name.
73 */
74 public String getName() {
75 return name;
76 }
77
78 /***
79 * @see java.lang.Object#toString
80 */
81 public String toString() {
82 return getName();
83 }
84 }
85
86 /***
87 * This class is used to manage the source files to be processed.
88 */
89 public static class SourceFile {
90 /*** The source file */
91 private File file;
92
93 public SourceFile() {
94 }
95 public SourceFile(File file) {
96 this.file = file;
97 }
98
99 /***
100 * Set the source file.
101 *
102 * @param file the source file.
103 */
104 public void setFile(File file) {
105 this.file = file;
106 }
107
108 /***
109 * Get the source file.
110 *
111 * @return the source file.
112 */
113 public File getFile() {
114 return file;
115 }
116
117 public String toString() {
118 return file.toString();
119 }
120 }
121
122 /*** The command line built to execute Javadoc. */
123 private Commandline cmd = new Commandline();
124
125 /*** Flag which indicates if javadoc from JDK 1.1 is to be used. */
126 //TODO: There are not doclets in 1.1. Think about removing.
127 private static boolean javadoc1 =
128 JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1);
129
130 /*** Flag which indicates if javadoc from JDK 1.4 is available */
131 private static boolean javadoc4 =
132 (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)
133 && !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)
134 && !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3));
135
136 /***
137 * Flag which indicates if the task should fail if there is a
138 * javadoc error.
139 */
140 private boolean failOnError = false;
141 private Path sourcePath = null;
142
143 /***
144 * The packages that contain attribute. Used to prepend to
145 * the classname given as an attribute so as not to require
146 * using the fully qualified classname.
147 */
148 private String attributePackages = null;
149
150 /***
151 * Logging level for attrib4j.
152 */
153 private String loglevel = "INFO";
154
155 private File destDir = null;
156 private Vector sourceFiles = new Vector();
157 private Vector packageNames = new Vector(5);
158 private Vector excludePackageNames = new Vector(1);
159 private boolean author = true;
160 private boolean version = true;
161 private Path classpath = null;
162 private Path bootclasspath = null;
163
164 private Path docletPath = null;
165
166 private String packageList = null;
167 private Vector tags = new Vector(5);
168 private boolean useDefaultExcludes = true;
169 private boolean useExternalFile = false;
170 private FileUtils fileUtils = FileUtils.newFileUtils();
171 private String source = null;
172
173 private Vector fileSets = new Vector();
174 private Vector packageSets = new Vector();
175
176 /***
177 * Work around command line length limit by using an external file
178 * for the sourcefiles.
179 *
180 * @param b true if an external file is to be used.
181 */
182 public void setUseExternalFile(boolean b) {
183 if (!javadoc1) {
184 useExternalFile = b;
185 }
186 }
187
188 /***
189 * Set the maximum memory to be used by the javadoc process
190 *
191 * @param max a string indicating the maximum memory according to the
192 * JVM conventions (e.g. 128m is 128 Megabytes)
193 */
194 public void setMaxmemory(String max) {
195 if (javadoc1) {
196 cmd.createArgument().setValue("-J-mx" + max);
197 } else {
198 cmd.createArgument().setValue("-J-Xmx" + max);
199 }
200 }
201
202 /***
203 * Specify where to find source file
204 *
205 * @param src a Path instance containing the various source directories.
206 */
207 public void setSourcepath(Path src) {
208 if (sourcePath == null) {
209 sourcePath = src;
210 } else {
211 sourcePath.append(src);
212 }
213 }
214
215 public void setAttributePackages(String packages) {
216 attributePackages = packages;
217 }
218
219 public void setLoglevel(String level) {
220 loglevel = level;
221 }
222
223 /***
224 * Create a path to be configured with the locations of the source
225 * files.
226 *
227 * @return a new Path instance to be configured by the Ant core.
228 */
229 public Path createSourcepath() {
230 if (sourcePath == null) {
231 sourcePath = new Path(project);
232 }
233 return sourcePath.createPath();
234 }
235
236 /***
237 * Adds a reference to a CLASSPATH defined elsewhere.
238 *
239 * @param r the reference containing the source path definition.
240 */
241 public void setSourcepathRef(Reference r) {
242 createSourcepath().setRefid(r);
243 }
244
245 /***
246 * Set the directory where the Javadoc output will be generated.
247 *
248 * @param dir the destination directory.
249 */
250 public void setDestdir(File dir) {
251 destDir = dir;
252 }
253
254 /***
255 * Set the list of source files to process.
256 *
257 * @param src a comma separated list of source files.
258 */
259 public void setSourcefiles(String src) {
260 StringTokenizer tok = new StringTokenizer(src, ",");
261 while (tok.hasMoreTokens()) {
262 String f = tok.nextToken();
263 SourceFile sf = new SourceFile();
264 sf.setFile(project.resolveFile(f));
265 addSource(sf);
266 }
267 }
268
269 /***
270 * Add a single source file.
271 *
272 * @param sf the source file to be processed.
273 */
274 public void addSource(SourceFile sf) {
275 sourceFiles.addElement(sf);
276 }
277
278 /***
279 * Set the package names to be processed.
280 *
281 * @param packages a comma separated list of packages specs
282 * (may be wildcarded).
283 *
284 * @see #addPackage for wildcard information.
285 */
286 public void setPackagenames(String packages) {
287 StringTokenizer tok = new StringTokenizer(packages, ",");
288 while (tok.hasMoreTokens()) {
289 String p = tok.nextToken();
290 PackageName pn = new PackageName();
291 pn.setName(p);
292 addPackage(pn);
293 }
294 }
295
296 /***
297 * Add a single package to be processed.
298 *
299 * If the package name ends with ".*" the Javadoc task
300 * will find and process all subpackages.
301 *
302 * @param pn the package name, possibly wildcarded.
303 */
304 public void addPackage(PackageName pn) {
305 packageNames.addElement(pn);
306 }
307
308 /***
309 * Set the list of packages to be excluded.
310 *
311 * @param packages a comma separated list of packages to be excluded.
312 * This may not include wildcards.
313 */
314 public void setExcludePackageNames(String packages) {
315 StringTokenizer tok = new StringTokenizer(packages, ",");
316 while (tok.hasMoreTokens()) {
317 String p = tok.nextToken();
318 PackageName pn = new PackageName();
319 pn.setName(p);
320 addExcludePackage(pn);
321 }
322 }
323
324 /***
325 * Add a package to be excluded from the javadoc run.
326 *
327 * @param pn the name of the package (wildcards are not permitted).
328 */
329 public void addExcludePackage(PackageName pn) {
330 excludePackageNames.addElement(pn);
331 }
332
333 /***
334 * Adds a fileset.
335 *
336 * <p>All included files will be added as sourcefiles. The task
337 * will automatically add
338 * <code>includes="**/*.java"</code> to the
339 * fileset.</p>
340 *
341 * @since 1.5
342 */
343 public void addFileset(FileSet fs) {
344 fileSets.addElement(fs);
345 }
346
347 /***
348 * Set the classpath to be used for this javadoc run.
349 *
350 * @param path an Ant Path object containing the compilation
351 * classpath.
352 */
353 public void setClasspath(Path path) {
354 if (classpath == null) {
355 classpath = path;
356 } else {
357 classpath.append(path);
358 }
359 }
360
361 /***
362 * Create a Path to be configured with the classpath to use
363 *
364 * @return a new Path instance to be configured with the classpath.
365 */
366 public Path createClasspath() {
367 if (classpath == null) {
368 classpath = new Path(project);
369 }
370 return classpath.createPath();
371 }
372
373 /***
374 * Adds a reference to a CLASSPATH defined elsewhere.
375 *
376 * @param r the reference to an instance defining the classpath.
377 */
378 public void setClasspathRef(Reference r) {
379 createClasspath().setRefid(r);
380 }
381
382 /***
383 * Set the classpath used to find the doclet class.
384 *
385 * @param docletPath the doclet classpath.
386 */
387 public void setDocletPath(Path docletPath) {
388 this.docletPath = docletPath;
389 }
390
391
392 public String setupAttrib4j(Javac javac) {
393 if (null == javac) {
394 return "null javac";
395 }
396 setProject(javac.getProject());
397 setLocation(javac.getLocation());
398 setTaskName("javac-attrib4j");
399
400 setSourcepath(javac.getSourcepath());
401 setClasspath(javac.getClasspath());
402
403 //Some wierd stuff to set the classpath
404 Path destPath = new Path(javac.getProject());
405 destPath.setLocation(javac.getDestdir());
406 setClasspath(destPath);
407
408 //list of files to compile from javac task...don't have a fileset anymore like
409 //in the javadoc task.
410 File[] javacSourceFiles = javac.getFileList();
411 if (javacSourceFiles != null) {
412 for (int i = 0; i < javacSourceFiles.length; i++) {
413 sourceFiles.add(new SourceFile(javacSourceFiles[i]));
414 }
415 }
416
417 String attribDestdir = javac.getProject().getProperty("attrib4j.destdir");
418 File f = new File(attribDestdir);
419 setDestdir(f);
420
421 setAttributePackages(javac.getProject().getProperty("attrib4j.packages"));
422
423
424 return null;
425 }
426
427 public void execute() throws BuildException {
428
429 log("*************************************", Project.MSG_INFO);
430 log("*Compiling Attributes using Javadoc *", Project.MSG_INFO);
431
432
433 Vector packagesToDoc = new Vector();
434 Path sourceDirs = new Path(getProject());
435
436 log("* sourceDirs = " + sourceDirs);
437 <b>if (packageList != null && sourcePath == null) {
438 String msg =
439 "sourcePath attribute must be set when "
440 + "specifying packagelist.";
441 throw new BuildException(msg);
442 }
443
444 if (sourcePath != null) {
445 sourceDirs.addExisting(sourcePath);
446 }
447
448 parsePackages(packagesToDoc, sourceDirs);
449
450 <b>if (packagesToDoc.size() != 0 && sourceDirs.size() == 0) {
451 String msg =
452 "sourcePath attribute must be set when "
453 + "specifying package names.";
454 throw new BuildException(msg);
455 }
456
457 Vector sourceFilesToDoc = (Vector) sourceFiles.clone();
458 addFileSets(sourceFilesToDoc);
459
460 log("* packageList = " + packageList, Project.MSG_INFO);
461 log("* packagesToDoc = " + packagesToDoc, Project.MSG_INFO);
462 log("* sourceFilesToDoc = " + sourceFilesToDoc, Project.MSG_INFO);
463 <b>if (packageList == null
464 && packagesToDoc.size() == 0
465 && sourceFilesToDoc.size() == 0) {
466 throw new BuildException(
467 "No source files and no packages have " + "been specified.");
468 }
469
470
471 Commandline toExecute = (Commandline) cmd.clone();
472 toExecute.setExecutable(JavaEnvUtils.getJdkExecutable("javadoc"));
473
474 //need access to private, protected and package delcarations.
475 toExecute.createArgument().setValue("-private");
476
477 if (classpath == null) {
478 classpath = Path.systemClasspath;
479 } else {
480 //Why doesn't this work with "ignore" as in the original ant task from which
481 //this was copied?
482 //classpath = classpath.concatSystemClasspath("ignore");
483 classpath = classpath.concatSystemClasspath();
484 }
485 log("*Classpath = " + classpath, Project.MSG_INFO);
486 log("* *", Project.MSG_INFO);
487 log("*************************************", Project.MSG_INFO);
488 //When running from Ant, if attrib4j.jar and bcel.jar are in ANT_HOME/lib
489 //then these jars will be present on the Path.systemClasspath.
490 //Set the doclet classpath equal to the same thing.
491 docletPath = classpath;
492
493 if (!javadoc1) {
494 if (classpath.size() > 0) {
495 toExecute.createArgument().setValue("-classpath");
496 toExecute.createArgument().setPath(classpath);
497 }
498 if (sourceDirs.size() > 0) {
499 toExecute.createArgument().setValue("-sourcepath");
500 toExecute.createArgument().setPath(sourceDirs);
501 }
502 } else {
503 sourceDirs.append(classpath);
504 if (sourceDirs.size() > 0) {
505 toExecute.createArgument().setValue("-classpath");
506 toExecute.createArgument().setPath(sourceDirs);
507 }
508 }
509
510 if (javadoc1) {
511 if (destDir == null) {
512 String msg = "destDir attribute must be set!";
513 throw new BuildException(msg);
514 }
515 }
516
517 if (!javadoc1) {
518 toExecute.createArgument().setValue("-doclet");
519 toExecute.createArgument().setValue("attrib4j.AttributeDoclet");
520
521 if (docletPath == null) {
522 String msg = "docletpath must be set";
523 throw new BuildException(msg);
524 }
525 toExecute.createArgument().setValue("-docletpath");
526 toExecute.createArgument().setPath(docletPath);
527
528 toExecute.createArgument().setValue("-userclasspath");
529 toExecute.createArgument().setPath(classpath);
530
531 toExecute.createArgument().setValue("-attributepackages");
532 toExecute.createArgument().setValue(attributePackages);
533
534 toExecute.createArgument().setValue("-d");
535 toExecute.createArgument().setFile(destDir);
536
537 toExecute.createArgument().setValue("-loglevel");
538 toExecute.createArgument().setValue(loglevel);
539
540 }
541
542 File tmpList = null;
543 PrintWriter srcListWriter = null;
544 try {
545
546 /***
547 * Write sourcefiles and package names to a temporary file
548 * if requested.
549 */
550 if (useExternalFile) {
551 if (tmpList == null) {
552 tmpList = fileUtils.createTempFile("javadoc", "", null);
553 toExecute.createArgument().setValue(
554 "@" + tmpList.getAbsolutePath());
555 }
556 srcListWriter =
557 new PrintWriter(
558 new FileWriter(tmpList.getAbsolutePath(), true));
559 }
560
561 Enumeration enum = packagesToDoc.elements();
562 while (enum.hasMoreElements()) {
563 String packageName = (String) enum.nextElement();
564 if (useExternalFile) {
565 srcListWriter.println(packageName);
566 } else {
567 toExecute.createArgument().setValue(packageName);
568 }
569 }
570
571 enum = sourceFilesToDoc.elements();
572 while (enum.hasMoreElements()) {
573 SourceFile sf = (SourceFile) enum.nextElement();
574 String sourceFileName = sf.getFile().getAbsolutePath();
575 if (useExternalFile) {
576 srcListWriter.println(sourceFileName);
577 } else {
578 toExecute.createArgument().setValue(sourceFileName);
579 }
580 }
581
582 } catch (IOException e) {
583 tmpList.delete();
584 throw new BuildException(
585 "Error creating temporary file",
586 e,
587 location);
588 } finally {
589 if (srcListWriter != null) {
590 srcListWriter.close();
591 }
592 }
593
594 <b>if (packageList != null) {
595 toExecute.createArgument().setValue("@" + packageList);
596 }
597 log(toExecute.describeCommand(), Project.MSG_VERBOSE);
598 //log(toExecute.describeCommand(), Project.MSG_INFO);
599
600 log("Javadoc execution", Project.MSG_INFO);
601
602 JavadocOutputStream out = new JavadocOutputStream(Project.MSG_INFO);
603 JavadocOutputStream err = new JavadocOutputStream(Project.MSG_WARN);
604 Execute exe = new Execute(new PumpStreamHandler(out, err));
605 exe.setAntRun(project);
606
607 /*
608 * No reason to change the working directory as all filenames and
609 * path components have been resolved already.
610 *
611 * Avoid problems with command line length in some environments.
612 */
613 exe.setWorkingDirectory(null);
614 try {
615 exe.setCommandline(toExecute.getCommandline());
616 int ret = exe.execute();
617 if (ret != 0 && failOnError) {
618 throw new BuildException("Javadoc returned " + ret, location);
619 }
620 } catch (IOException e) {
621 throw new BuildException("Javadoc failed: " + e, e, location);
622 } finally {
623 if (tmpList != null) {
624 tmpList.delete();
625 tmpList = null;
626 }
627
628 out.logFlush();
629 err.logFlush();
630 try {
631 out.close();
632 err.close();
633 } catch (IOException e) {
634 }
635 }
636
637 }
638
639 private class JavadocOutputStream extends LogOutputStream {
640 JavadocOutputStream(int level) {
641 super(Attrib4jTask.this, level);
642 }
643
644 //
645 // Override the logging of output in order to filter out Generating
646 // messages. Generating messages are set to a priority of VERBOSE
647 // unless they appear after what could be an informational message.
648 //
649 private String queuedLine = null;
650 protected void processLine(String line, int messageLevel) {
651 if (messageLevel == Project.MSG_INFO
652 && line.startsWith("Generating ")) {
653 if (queuedLine != null) {
654 super.processLine(queuedLine, Project.MSG_VERBOSE);
655 }
656 queuedLine = line;
657 } else {
658 if (queuedLine != null) {
659 if (line.startsWith("Building ")) {
660 super.processLine(queuedLine, Project.MSG_VERBOSE);
661 } else {
662 super.processLine(queuedLine, Project.MSG_INFO);
663 }
664 queuedLine = null;
665 }
666 super.processLine(line, messageLevel);
667 }
668 }
669
670 protected void logFlush() {
671 if (queuedLine != null) {
672 super.processLine(queuedLine, Project.MSG_VERBOSE);
673 queuedLine = null;
674 }
675 }
676 }
677
678 /***
679 * Add the directories matched by the nested dirsets to the Vector
680 * and the base directories of the dirsets to the Path. It also
681 * handles the packages and excludepackages attributes and
682 * elements.
683 *
684 * @since 1.5
685 */
686 private void parsePackages(Vector pn, Path sp) {
687 Vector addedPackages = new Vector();
688 Vector dirSets = (Vector) packageSets.clone();
689
690 // for each sourcePath entry, add a directoryset with includes
691 // taken from packagenames attribute and nested package
692 // elements and excludes taken from excludepackages attribute
693 // and nested excludepackage elements
694 if (sourcePath != null && packageNames.size() > 0) {
695 PatternSet ps = new PatternSet();
696 Enumeration enum = packageNames.elements();
697 while (enum.hasMoreElements()) {
698 PackageName p = (PackageName) enum.nextElement();
699 String pkg = p.getName().replace('.', '/');
700 if (pkg.endsWith("*")) {
701 pkg += "*";
702 }
703 ps.createInclude().setName(pkg);
704 }
705
706 enum = excludePackageNames.elements();
707 while (enum.hasMoreElements()) {
708 PackageName p = (PackageName) enum.nextElement();
709 String pkg = p.getName().replace('.', '/');
710 if (pkg.endsWith("*")) {
711 pkg += "*";
712 }
713 ps.createExclude().setName(pkg);
714 }
715
716 String[] pathElements = sourcePath.list();
717 for (int i = 0; i < pathElements.length; i++) {
718 DirSet ds = new DirSet();
719 ds.setDefaultexcludes(useDefaultExcludes);
720 ds.setDir(new File(pathElements[i]));
721 ds.createPatternSet().addConfiguredPatternset(ps);
722 dirSets.addElement(ds);
723 }
724 }
725
726 Enumeration enum = dirSets.elements();
727 while (enum.hasMoreElements()) {
728 DirSet ds = (DirSet) enum.nextElement();
729 File baseDir = ds.getDir(getProject());
730 log("scanning " + baseDir + " for packages.", Project.MSG_DEBUG);
731 DirectoryScanner dsc = ds.getDirectoryScanner(getProject());
732 String[] dirs = dsc.getIncludedDirectories();
733 boolean containsPackages = false;
734 for (int i = 0; i < dirs.length; i++) {
735 // are there any java files in this directory?
736 File pd = new File(baseDir, dirs[i]);
737 String[] files = pd.list(new FilenameFilter() {
738 public boolean accept(File dir1, String name) {
739 if (name.endsWith(".java")) {
740 return true;
741 }
742 return false; // ignore dirs
743 }
744 });
745
746 if (files.length > 0) {
747 containsPackages = true;
748 String packageName =
749 dirs[i].replace(File.separatorChar, '.');
750 if (!addedPackages.contains(packageName)) {
751 addedPackages.addElement(packageName);
752 pn.addElement(packageName);
753 }
754 }
755 }
756 if (containsPackages) {
757 // We don't need to care for duplicates here,
758 // Path.list does it for us.
759 sp.createPathElement().setLocation(baseDir);
760 } else {
761 log(
762 baseDir + " doesn\'t contain any packages, dropping it.",
763 Project.MSG_VERBOSE);
764 }
765 }
766 }
767
768
769
770 /***
771 * Add the files matched by the nested filesets to the Vector as
772 * SourceFile instances.
773 *
774 * @since 1.5
775 */
776 private void addFileSets(Vector sf) {
777 Enumeration enum = fileSets.elements();
778 while (enum.hasMoreElements()) {
779 FileSet fs = (FileSet) enum.nextElement();
780 if (!fs.hasPatterns() && !fs.hasSelectors()) {
781 fs = (FileSet) fs.clone();
782 fs.createInclude().setName("**/*.java");
783 }
784 File baseDir = fs.getDir(getProject());
785 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
786 String[] files = ds.getIncludedFiles();
787 for (int i = 0; i < files.length; i++) {
788 sf.addElement(new SourceFile(new File(baseDir, files[i])));
789 }
790 }
791 }
792
793 }
This page was automatically generated by Maven