root/trunk/src/java/org/jcoderz/commons/taskdefs/ApiDocTask.java

Revision 1011, 12.9 kB (checked in by amandel, 4 years ago)

Aligned svn keyword settings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *    * Redistributions of source code must retain the above copyright
11 *      notice, this list of conditions and the following disclaimer.
12 *    * Redistributions in binary form must reproduce the above
13 *      copyright notice, this list of conditions and the following
14 *      disclaimer in the documentation and/or other materials
15 *      provided with the distribution.
16 *    * Neither the name of the jCoderZ.org Project nor the names of
17 *      its contributors may be used to endorse or promote products
18 *      derived from this software without specific prior written
19 *      permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33package org.jcoderz.commons.taskdefs;
34
35import java.io.File;
36import java.io.FileInputStream;
37import java.io.IOException;
38import java.util.ArrayList;
39import java.util.Collections;
40import java.util.Iterator;
41import java.util.List;
42
43import javax.xml.parsers.SAXParser;
44import javax.xml.parsers.SAXParserFactory;
45
46import org.apache.tools.ant.BuildException;
47import org.apache.tools.ant.Project;
48import org.apache.tools.ant.Task;
49import org.apache.tools.ant.taskdefs.Javadoc;
50import org.apache.tools.ant.taskdefs.Javadoc.DocletInfo;
51import org.apache.tools.ant.types.FileSet;
52import org.apache.tools.ant.types.Path;
53import org.apache.tools.ant.types.PatternSet.NameEntry;
54import org.jcoderz.commons.util.IoUtil;
55import org.xml.sax.Attributes;
56import org.xml.sax.InputSource;
57import org.xml.sax.helpers.DefaultHandler;
58
59
60/**
61 * Generates API documentation (DocBook format).
62 *
63 * @author Michael Griffel
64 */
65public class ApiDocTask
66        extends Task
67{
68    /** Task name. */
69    public static final String NAME = "apidoc";
70
71    /** File name extension of Java files. */
72    private static final String JAVA_EXTENSION = ".java";
73
74    /** The output directory. */
75    private File mOutDir;
76
77    /** The input file. */
78    private File mInFile;
79
80    /** terminate ant build on error. */
81    private boolean mFailOnError;
82
83    /** Doclet path. */
84    private Path mDocletPath;
85
86    /** Source path - list of SourceDirectory. */
87    private final List mSources = new ArrayList();
88
89    /**
90     * Sets the XML input file that contains the document.
91     *
92     * @param f the XML input file (log message info).
93     */
94    public void setIn (File f)
95    {
96        mInFile = f;
97    }
98
99    /**
100     * Set the destination directory into which the result files should
101     * be copied to. This parameter is required.
102     *
103     * @param dir the name of the destination directory.
104     */
105    public void setOut (File dir)
106    {
107        mOutDir = dir;
108    }
109
110    /**
111     * Set whether we should fail on an error.
112     *
113     * @param b Whether we should fail on an error.
114     */
115    public void setFailonerror (boolean b)
116    {
117        mFailOnError = b;
118    }
119
120    /**
121     * Set the source path to be used for this task run.
122     *
123     * @param src an Ant FileSet object containing the compilation
124     *        source path.
125     */
126    public void addSrc (SourceDirectory src)
127    {
128        mSources.add(src);
129    }
130
131    /**
132     * Set the doclet path to be used for this task run.
133     *
134     * @param path an Ant Path object containing the compilation source
135     *        path.
136     */
137    public void setDocletPath (Path path)
138    {
139        if (mDocletPath == null)
140        {
141            mDocletPath = path;
142        }
143        else
144        {
145            mDocletPath.add(path);
146        }
147    }
148
149    /**
150     * Execute this task.
151     *
152     * @throws BuildException An building exception occurred.
153     */
154    public void execute ()
155            throws BuildException
156    {
157        try
158        {
159            checkAttributes();
160            final ApiDocSaxHandler handler = parse();
161            log("APIs: " + handler.apiDocs().toString(), Project.MSG_DEBUG);
162            final Iterator iterator = handler.apiDocs().iterator();
163            while (iterator.hasNext())
164            {
165                final ApiDocType apiDoc = (ApiDocType) iterator.next();
166                final File xmlFile = runXmlDoclet(apiDoc);
167                log("Generated xmlFile " + xmlFile);
168            }
169        }
170        catch (BuildException e)
171        {
172            if (mFailOnError)
173            {
174                throw e;
175            }
176            log(e.getMessage(), Project.MSG_ERR);
177        }
178    }
179
180    private File runXmlDoclet (final ApiDocType apiDoc)
181    {
182        final Javadoc javadocTask = new Javadoc();
183        javadocTask.setProject(getProject());
184        javadocTask.setFailonerror(mFailOnError);
185        javadocTask.setTaskName("xml-doclet");
186        javadocTask.setPackage(true);
187        javadocTask.setClasspath(mDocletPath);
188        javadocTask.setClasspath(Path.systemClasspath);
189        javadocTask.setDestdir(mOutDir);
190        javadocTask.setAdditionalparam("-quiet");
191        for (final Iterator i = mSources.iterator(); i.hasNext();)
192        {
193            final SourceDirectory fs = (SourceDirectory) i.next();
194            javadocTask.addFileset(addClasses(apiDoc, fs.getDir()));
195        }
196        final DocletInfo info = javadocTask.createDoclet();
197        info.setProject(getProject());
198        info.setName("org.jcoderz.commons.doclet.XmlDoclet");
199        info.setPath(mDocletPath);
200        final File tmpFile = new File(mOutDir, "/javadoc.xml");
201        javadocTask.execute();
202        final File outFile = new File(mOutDir, apiDoc.getName() + ".xml");
203        if (!tmpFile.renameTo(outFile))
204        {
205            try
206            {
207                // copy && delete
208                IoUtil.copy(tmpFile, outFile);
209                if (!tmpFile.delete())
210                {
211                    throw new BuildException("Cannot delete file " + tmpFile);
212                }
213            }
214            catch (IOException e)
215            {
216                throw new BuildException("Cannot move file " + tmpFile
217                        + " to " + outFile);
218            }
219        }
220        try
221        {
222            IoUtil.copy(outFile, new File(outFile.getParent(), outFile
223                    .getName()
224                    + ".in"));
225        }
226        catch (IOException e)
227        {
228            throw new BuildException("Failed to copy file: " + outFile, e);
229        }
230        return outFile;
231    }
232
233    private FileSet addClasses (final ApiDocType diagram, File path)
234    {
235        final FileSet filez = new FileSet();
236        filez.setDir(path);
237        filez.setProject(getProject());
238        final Iterator i = diagram.classList().iterator();
239        while (i.hasNext())
240        {
241            final String name = (String) i.next();
242            final NameEntry entry = filez.createInclude();
243            final String pathName = name.replaceAll("\\.", "/")
244                    + JAVA_EXTENSION;
245            log("Adding Source file " + pathName, Project.MSG_VERBOSE);
246            entry.setName(pathName);
247        }
248        log("Source files: " + filez, Project.MSG_VERBOSE);
249        return filez;
250    }
251
252    private ApiDocSaxHandler parse ()
253    {
254        final ApiDocSaxHandler handler = new ApiDocSaxHandler();
255        try
256        {
257            // create a new XML parser
258            final SAXParserFactory factory = SAXParserFactory.newInstance();
259            factory.setNamespaceAware(true);
260            factory.setValidating(true);
261            final SAXParser parser = factory.newSAXParser();
262            /*
263             * parser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
264             * parser.setProperty(JAXP_SCHEMA_SOURCE,
265             * AppInfoTask.class.getResource(APP_INFO_SCHEMA).toExternalForm());
266             */
267            parser
268                    .parse(new InputSource(new FileInputStream(mInFile)),
269                            handler);
270            log(mInFile + " parsed successfully.", Project.MSG_INFO);
271        }
272        catch (Exception e)
273        {
274            throw new BuildException(
275                    "Failed to parse " + mInFile + ": " + e, e);
276        }
277        return handler;
278    }
279
280    /**
281     * Checks the attributes provided by this class.
282     *
283     * @throws BuildException
284     */
285    private void checkAttributes ()
286            throws BuildException
287    {
288        checkAttributeInFile();
289    }
290
291    private void checkAttributeInFile ()
292    {
293        if (mInFile == null)
294        {
295            throw new BuildException("Missing mandatory attribute 'in'.",
296                    getLocation());
297        }
298        if (!mInFile.exists())
299        {
300            throw new BuildException("Input file '" + mInFile + "' not found.",
301                    getLocation());
302        }
303    }
304
305    private static class ApiDocSaxHandler
306            extends DefaultHandler
307    {
308        private final StringBuffer mBuffer = new StringBuffer();
309
310        private boolean mCaptureCharacters = false;
311
312        private final List mApiDocElementList = new ArrayList();
313
314        private ApiDocType mCurrentApiDocElement = null;
315
316        /** {@inheritDoc} */
317        public void startElement (String uri, String localName, String qName,
318                Attributes attributes)
319        {
320            if ("apidoc".equals(localName))
321            {
322                mCurrentApiDocElement = new ApiDocType(attributes
323                        .getValue("name"));
324                mApiDocElementList.add(mCurrentApiDocElement);
325            }
326            else if ("class".equals(localName) && mCurrentApiDocElement != null)
327            {
328                mCurrentApiDocElement.add(attributes.getValue("name"));
329            }
330            else if ("description".equals(localName)
331                    && mCurrentApiDocElement != null)
332            {
333                captureCharacters();
334            }
335        }
336
337        /** {@inheritDoc} */
338        public void endElement (String uri, String localName, String qName)
339        {
340            if ("apidoc".equals(localName))
341            {
342                mCurrentApiDocElement = null;
343            }
344            else if ("description".equals(localName)
345                    && mCurrentApiDocElement != null)
346            {
347                mCurrentApiDocElement.setDescription(characters().trim());
348            }
349        }
350
351        /** {@inheritDoc} */
352        public void characters (char[] ch, int start, int length)
353        {
354            if (mCaptureCharacters)
355            {
356                mBuffer.append(ch, start, length);
357            }
358        }
359
360        void captureCharacters ()
361        {
362            mCaptureCharacters = true;
363        }
364
365        /**
366         * Returns the captured characters and <b>clears</b> the
367         * internal buffer.
368         *
369         * @return the captured characters.
370         */
371        String characters ()
372        {
373            final String result = mBuffer.toString();
374            mBuffer.setLength(0);
375            mCaptureCharacters = false;
376            return result;
377        }
378
379        /**
380         * Returns a list of {@link ApiDocType}.
381         *
382         * @return a list of {@link ApiDocType}.
383         */
384        public List apiDocs ()
385        {
386            return Collections.unmodifiableList(mApiDocElementList);
387        }
388    }
389
390    private static class ApiDocType
391    {
392        private final String mName;
393
394        private final List mClasses = new ArrayList();
395
396        private String mDescription = "";
397
398        ApiDocType (String name)
399        {
400            mName = name;
401        }
402
403        void add (String clazz)
404        {
405            mClasses.add(clazz);
406        }
407
408        List classList ()
409        {
410            return Collections.unmodifiableList(mClasses);
411        }
412
413        String getName ()
414        {
415            return mName;
416        }
417
418        /** {@inheritDoc} */
419        public String toString ()
420        {
421            final StringBuffer sb = new StringBuffer();
422            sb.append("clazzes ");
423            sb.append(mName);
424            sb.append(" = ");
425            sb.append(mClasses);
426            sb.append(" description: '");
427            sb.append(mDescription);
428            sb.append('\'');
429            return sb.toString();
430        }
431
432        String getDescription ()
433        {
434            return mDescription;
435        }
436
437        void setDescription (String description)
438        {
439            mDescription = description;
440        }
441    }
442}
Note: See TracBrowser for help on using the browser.