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

Revision 1011, 16.7 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.taskdefs.Javadoc.DocletParam;
52import org.apache.tools.ant.types.FileSet;
53import org.apache.tools.ant.types.Path;
54import org.apache.tools.ant.types.PatternSet.NameEntry;
55import org.jcoderz.phoenix.sqlparser.SqlToXml;
56import org.xml.sax.Attributes;
57import org.xml.sax.InputSource;
58import org.xml.sax.helpers.DefaultHandler;
59
60/**
61 * Generates UML diagrams.
62 *
63 * @author Michael Griffel
64 */
65public class DiagramTask
66      extends Task
67{
68   /** Task name. */
69   public static final String NAME = "diagram";
70   /** File name extension of graphviz dot files. */
71   private static final String DOTTY_EXTENSION = ".dot";
72   /** File name extension of Java files. */
73   private static final String JAVA_EXTENSION = ".java";
74   /** Font name for graphviz. */
75   private static final String DEFAULT_FONTNAME = "verdana";
76   /** Font size for graphviz. */
77   private static final String DEFAULT_FONTSIZE = "7";
78
79   /** The output directory. */
80   private File mOutDir;
81   /** The input file. */
82   private File mInFile;
83   /** terminate ant build on error. */
84   private boolean mFailOnError;
85   /** Doclet path. */
86   private Path mDocletPath;
87   /** Source path - list of SourceDirectory. */
88   private final List mSources = new ArrayList();
89
90   /**
91    * Sets the XML input file that contains the document.
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
101    * files should be copied to. This parameter is required.
102    * @param dir the name of the destination directory.
103    **/
104   public void setOut (File dir)
105   {
106       mOutDir = dir;
107   }
108
109   /**
110    * Set the document type.
111    * @param type the document type.
112    */
113   public void setType (String type)
114   {
115      // TODO:
116   }
117
118   /**
119    * Set whether we should fail on an error.
120    * @param b Whether we should fail on an error.
121    */
122   public void setFailonerror (boolean b)
123   {
124      mFailOnError = b;
125   }
126
127   /**
128    * Set the source path to be used for this task run.
129    * @param src an Ant FileSet object containing the compilation
130    *        source path.
131    */
132   public void addSrc (SourceDirectory src)
133   {
134      mSources.add(src);
135   }
136
137   /**
138    * Set the doclet path to be used for this task run.
139    * @param path an Ant Path object containing the compilation
140    *        source path.
141    */
142   public void setDocletPath (Path path)
143   {
144      if (mDocletPath == null)
145      {
146         mDocletPath = path;
147      }
148      else
149      {
150         mDocletPath.add(path);
151      }
152   }
153
154   /**
155    * Execute this task.
156    *
157    * @throws BuildException An building exception occurred.
158    */
159   public void execute ()
160         throws BuildException
161   {
162      try
163      {
164         checkAttributes();
165
166         final DiagramSaxHandler handler = parse();
167         log("Diagrams: " + handler.diagrams().toString(), Project.MSG_DEBUG);
168
169         final Iterator iterator = handler.diagrams().iterator();
170         while (iterator.hasNext())
171         {
172            final Diagram diagram = (Diagram) iterator.next();
173            if ("class".equals(diagram.getType()))
174            {
175               generateUmlDiagram(diagram);
176            }
177            else if ("ER".equals(diagram.getType()))
178            {
179               generateEntityRelationshipDiagram(diagram);
180            }
181         }
182         generateStateDiagram();
183         AntTaskUtil.renderDotFiles(this, mOutDir, mFailOnError);
184      }
185      catch (BuildException e)
186      {
187         if (mFailOnError)
188         {
189            throw e;
190         }
191         log(e.getMessage(), Project.MSG_ERR);
192      }
193   }
194
195
196   private void generateEntityRelationshipDiagram (Diagram diagram)
197   {
198      final File inFile = new File(
199            getProject().getBaseDir(), diagram.getFile());
200      final File tmpFile;
201      try
202      {
203         tmpFile = File.createTempFile("xdoc", ".tmp");
204      }
205      catch (IOException e)
206      {
207         throw new BuildException("Failed to create temp file: " + e, e);
208      }
209      final SqlToXml sqlToXml
210            = new SqlToXml(inFile.getAbsolutePath(), tmpFile.getAbsolutePath());
211      try
212      {
213         sqlToXml.transformSqlToXml();
214      }
215      catch (Exception e)
216      {
217         throw new BuildException("Failed to transform SQL '" + inFile
218               + "' file to XML: " + e, e);
219      }
220
221      final File outFile = new File(mOutDir, diagram.getName() + ".dot");
222      final XsltBasedTask t = new XsltBasedTask()
223      {
224         String getDefaultStyleSheet ()
225         {
226            return "generate-er-diagram.xsl";
227         }
228      };
229      t.setProject(getProject());
230      t.setTaskName("xml2dot");
231      t.setFailonerror(mFailOnError);
232      t.setIn(tmpFile);
233      t.setForce(true);
234      t.setDestdir(mOutDir);
235      t.setOut(outFile);
236      log("Generating ER diagram " + outFile, Project.MSG_VERBOSE);
237      t.execute();
238   }
239
240   private void generateStateDiagram ()
241   {
242      final XsltBasedTask t = new XsltBasedTask()
243      {
244         String getDefaultStyleSheet ()
245         {
246            return "generate-state-diagram.xsl";
247         }
248      };
249      t.setProject(getProject());
250      t.setTaskName("xml2dot");
251      t.setFailonerror(mFailOnError);
252      t.setIn(mInFile);
253      t.setForce(true);
254      t.setDestdir(mOutDir);
255      try
256      {
257         t.setOut(File.createTempFile("xdoc", ".tmp"));
258      }
259      catch (IOException e)
260      {
261         throw new BuildException("Cannot create temp file: " + e, e);
262      }
263      log("Generating state diagrams from file "
264            + mInFile, Project.MSG_VERBOSE);
265      t.execute();
266   }
267/*
268   private void generateUmlDiagram (final Diagram diagram)
269   {
270      final Javadoc javadocTask = new Javadoc();
271      javadocTask.setProject(getProject());
272      javadocTask.setFailonerror(mFailOnError);
273      javadocTask.setTaskName("umlgraph");
274      javadocTask.setPackage(true);
275      javadocTask.setClasspath(mDocletPath);
276      javadocTask.setClasspath(Path.systemClasspath);
277      for (final Iterator i = mSources.iterator(); i.hasNext();)
278      {
279         final SourceDirectory fs = (SourceDirectory) i.next();
280         javadocTask.addFileset(addClasses(diagram, fs.getDir()));
281      }
282      final DocletInfo info = javadocTask.createDoclet();
283      info.setProject(getProject());
284      info.setName("gr.spinellis.umlgraph.doclet.UmlGraph");
285      info.setPath(mDocletPath);
286      addDocletParam(info, "-operations");
287      addDocletParam(info, "-visibility");
288      addDocletParam(info, "-types");
289//      addDocletParam(info, "-noguillemot");
290      addDocletParam(info, "-nodefontname", DEFAULT_FONTNAME);
291      addDocletParam(info, "-nodefontsize", DEFAULT_FONTSIZE);
292      addDocletParam(info, "-nodefontabstractname", DEFAULT_FONTNAME);
293      addDocletParam(info, "-edgefontname", DEFAULT_FONTNAME);
294      addDocletParam(info, "-edgefontsize", DEFAULT_FONTSIZE);
295//      final File dotFile
296//            = new File(mOutDir, diagram.getName() + DOTTY_EXTENSION);
297//      dotFile.getParentFile().mkdirs();
298//      addDocletParam(info, "-output", dotFile.getAbsolutePath());
299    addDocletParam(info, "-d", mOutDir.getAbsolutePath());
300    addDocletParam(info, "-output", diagram.getName() + DOTTY_EXTENSION);
301    mOutDir.mkdirs();
302      javadocTask.execute();
303   }
304*/
305
306   private void generateUmlDiagram (final Diagram diagram)
307   {
308      final Javadoc javadocTask = new Javadoc();
309      javadocTask.setProject(getProject());
310      javadocTask.setFailonerror(mFailOnError);
311      javadocTask.setTaskName("umlgraph");
312      javadocTask.setPackage(true);
313      javadocTask.setClasspath(mDocletPath);
314      javadocTask.setClasspath(Path.systemClasspath);
315      for (final Iterator i = mSources.iterator(); i.hasNext();)
316      {
317         final SourceDirectory fs = (SourceDirectory) i.next();
318         javadocTask.addFileset(addClasses(diagram, fs.getDir()));
319      }
320      final DocletInfo info = javadocTask.createDoclet();
321      info.setProject(getProject());
322      info.setName("UmlGraph");
323      info.setPath(mDocletPath);
324      addDocletParam(info, "-operations");
325      addDocletParam(info, "-visibility");
326      addDocletParam(info, "-types");
327      addDocletParam(info, "-noguillemot");
328      addDocletParam(info, "-nodefontname", DEFAULT_FONTNAME);
329      addDocletParam(info, "-nodefontsize", DEFAULT_FONTSIZE);
330      addDocletParam(info, "-nodefontabstractname", DEFAULT_FONTNAME);
331      addDocletParam(info, "-edgefontname", DEFAULT_FONTNAME);
332      addDocletParam(info, "-edgefontsize", DEFAULT_FONTSIZE);
333      final File dotFile
334            = new File(mOutDir, diagram.getName() + DOTTY_EXTENSION);
335      dotFile.getParentFile().mkdirs();
336      addDocletParam(info, "-output", dotFile.getAbsolutePath());
337      javadocTask.execute();
338   }
339
340   
341   private FileSet addClasses (final Diagram diagram, File path)
342   {
343      final FileSet filez = new FileSet();
344      filez.setProject(getProject());
345      filez.setDir(path);
346      final Iterator i = diagram.classList().iterator();
347      while (i.hasNext())
348      {
349         final String name = (String) i.next();
350         final NameEntry entry = filez.createInclude();
351         final String pathName = name.replaceAll("\\.", "/") + JAVA_EXTENSION;
352         log("Adding Source file " + pathName, Project.MSG_VERBOSE);
353         entry.setName(pathName);
354      }
355      return filez;
356   }
357
358   private void addDocletParam (DocletInfo info, String key)
359   {
360      final DocletParam param = info.createParam();
361      param.setName(key);
362   }
363
364   private void addDocletParam (DocletInfo info, String key, String value)
365   {
366      final DocletParam param = info.createParam();
367      param.setName(key);
368      param.setValue(value);
369   }
370
371   private DiagramSaxHandler parse ()
372   {
373      final DiagramSaxHandler handler = new DiagramSaxHandler();
374      try
375      {
376         // create a new XML parser
377         final SAXParserFactory factory = SAXParserFactory.newInstance();
378         factory.setNamespaceAware(true);
379         factory.setValidating(true);
380         final SAXParser parser = factory.newSAXParser();
381         /*
382         parser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
383         parser.setProperty(JAXP_SCHEMA_SOURCE,
384               AppInfoTask.class.getResource(APP_INFO_SCHEMA).toExternalForm());
385               */
386         parser.parse(new InputSource(new FileInputStream(mInFile)), handler);
387         log(mInFile + " parsed successfully.", Project.MSG_INFO);
388      }
389      catch (Exception e)
390      {
391         throw new BuildException("Failed to parse " + mInFile + ": " + e, e);
392      }
393      return handler;
394   }
395
396   /**
397    * Checks the attributes provided by this class.
398    * @throws BuildException
399    */
400   private void checkAttributes ()
401         throws BuildException
402   {
403      checkAttributeInFile();
404   }
405
406   private void checkAttributeInFile ()
407   {
408      if (mInFile == null)
409      {
410         throw new BuildException(
411               "Missing mandatory attribute 'in'.", getLocation());
412      }
413      if (!mInFile.exists())
414      {
415         throw new BuildException(
416               "Input file '" + mInFile + "' not found.", getLocation());
417      }
418   }
419
420
421   private static class DiagramSaxHandler
422         extends DefaultHandler
423   {
424      private final StringBuffer mBuffer = new StringBuffer();
425      private boolean mCaptureCharacters = false;
426
427      private final List mDiagrams = new ArrayList();
428      private Diagram mCurrentDiagram = null;
429
430      /** {@inheritDoc} */
431      public void startElement (String uri, String localName, String qName,
432            Attributes attributes)
433      {
434         if ("diagram".equals(localName))
435         {
436            mCurrentDiagram = new Diagram(
437                  attributes.getValue("name"), attributes.getValue("type"));
438            if (attributes.getValue("file") != null)
439            {
440               mCurrentDiagram.setFile(attributes.getValue("file"));
441            }
442            mDiagrams.add(mCurrentDiagram);
443         }
444         else if ("class".equals(localName) && mCurrentDiagram != null)
445         {
446            mCurrentDiagram.add(attributes.getValue("name"));
447         }
448         else if ("description".equals(localName) && mCurrentDiagram != null)
449         {
450            captureCharacters();
451         }
452      }
453
454      /** {@inheritDoc} */
455      public void endElement (String uri, String localName, String qName)
456      {
457         if ("diagram".equals(localName))
458         {
459            mCurrentDiagram = null;
460         }
461         else if ("description".equals(localName) && mCurrentDiagram != null)
462         {
463            mCurrentDiagram.setDescription(characters().trim());
464         }
465
466      }
467
468      /** {@inheritDoc} */
469      public void characters (char[] ch, int start, int length)
470      {
471         if (mCaptureCharacters)
472         {
473            mBuffer.append(ch, start, length);
474         }
475      }
476
477
478      void captureCharacters ()
479      {
480         mCaptureCharacters = true;
481      }
482
483      /**
484       * Returns the captured characters and <b>clears</b> the internal
485       * buffer.
486       * @return the captured characters.
487       */
488      String characters ()
489      {
490         final String result = mBuffer.toString();
491         mBuffer.setLength(0);
492         mCaptureCharacters = false;
493         return result;
494      }
495
496
497      /**
498       * Returns a list of {@link Diagram}.
499       * @return a list of {@link Diagram}.
500       */
501      public List diagrams ()
502      {
503         return Collections.unmodifiableList(mDiagrams);
504      }
505
506   }
507
508   private static class Diagram
509   {
510      private final String mName;
511      private final String mType;
512      private final List mClasses = new ArrayList();
513      private String mDescription = "";
514      private String mFile;
515
516      Diagram (String name, String type)
517      {
518         mName = name;
519         mType = type;
520      }
521
522      void add (String clazz)
523      {
524         mClasses.add(clazz);
525      }
526
527      List classList ()
528      {
529         return Collections.unmodifiableList(mClasses);
530      }
531
532      String getName ()
533      {
534         return mName;
535      }
536
537      String getType ()
538      {
539         return mType;
540      }
541
542      String getDescription ()
543      {
544         return mDescription;
545      }
546
547      void setDescription (String description)
548      {
549         mDescription = description;
550      }
551
552      String getFile ()
553      {
554         return mFile;
555      }
556
557      void setFile (String file)
558      {
559         mFile = file;
560      }
561
562      /** {@inheritDoc} */
563      public String toString ()
564      {
565         final StringBuffer sb = new StringBuffer();
566         sb.append("diagram ");
567         sb.append(mName);
568         sb.append(" (");
569         sb.append(mType);
570         sb.append(") = ");
571         sb.append(mClasses);
572         sb.append(" description: '");
573         sb.append(mDescription);
574         sb.append('\'');
575         return sb.toString();
576      }
577   }
578}
Note: See TracBrowser for help on using the browser.