Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: xdoclet/GenerationManager.java


1   /*
2    * Copyright (c) 2001, 2002 The XDoclet team
3    * All rights reserved.
4    */
5   package xdoclet;
6   
7   import java.io.File;
8   import java.net.URL;
9   import java.text.MessageFormat;
10  import java.util.ArrayList;
11  import java.util.Arrays;
12  
13  import java.util.HashMap;
14  import java.util.Iterator;
15  import java.util.List;
16  import java.util.Map;
17  
18  import org.apache.commons.logging.Log;
19  import xjavadoc.XClass;
20  import xjavadoc.XJavaDoc;
21  
22  import xdoclet.loader.ModuleFinder;
23  import xdoclet.tagshandler.AbstractProgramElementTagsHandler;
24  import xdoclet.tagshandler.PackageTagsHandler;
25  import xdoclet.template.TemplateEngine;
26  import xdoclet.template.TemplateException;
27  import xdoclet.template.TemplateParser;
28  import xdoclet.util.LogUtil;
29  
30  /**
31   * Verify if the generation is needed for Java files and Xml files based templates.
32   *
33   * @author    Vincent Harcq (vincent.harcq@hubmethods.com)
34   * @created   March 30, 2002
35   * @version   $Revision: 1.19 $
36   */
37  public class GenerationManager
38  {
39      private final static File newestJar = ModuleFinder.getNewestFileOnClassPath();
40  
41      private static Map parserDb = null;
42  
43      private final TemplateSubTask subTask;
44  
45      private boolean guessGenerationNeeded = true;
46  
47      private XJavaDoc _xJavaDoc;
48  
49      /**
50       * Describe what the GenerationManager constructor does
51       *
52       * @param subTask   Describe what the parameter does
53       * @param xJavaDoc
54       */
55      public GenerationManager(XJavaDoc xJavaDoc, TemplateSubTask subTask)
56      {
57          if (xJavaDoc == null) {
58              throw new IllegalArgumentException("xJavaDoc can't be null");
59          }
60          _xJavaDoc = xJavaDoc;
61          this.subTask = subTask;
62      }
63  
64      /**
65       * Return (and construct) the template database. It is a map between a <code>String</code> representing the template
66       * file and an array of <String> representing the merge files that are part of the generation.
67       *
68       * @return   the <code>Map</code>
69       */
70      private static Map getParserDb()
71      {
72          if (parserDb == null) {
73              parserDb = new HashMap();
74          }
75          return parserDb;
76      }
77  
78      /**
79       * During parsing we build the Template database. We store it on file.
80       *
81       * @param templateURL  the template file
82       * @param files        the merge files involved in the generation
83       */
84      private static void updateParserDb(URL templateURL, String[] files)
85      {
86          // Merge existing list with new list
87          String[] mergeFiles = (String[]) getParserDb().get(new File(templateURL.getFile()).getName());
88          List complete = new ArrayList(Arrays.asList(files));
89  
90          if (mergeFiles != null) {
91              for (int j = 0; j < mergeFiles.length; j++) {
92                  String file = mergeFiles[j];
93  
94                  if (!complete.contains(file)) {
95                      complete.add(file);
96                  }
97              }
98          }
99          getParserDb().put(new File(templateURL.getFile()).getName(), complete.toArray(new String[complete.size()]));
100     }
101 
102     /**
103      * Gets the GuessGenerationNeeded attribute of the GenerationManager object
104      *
105      * @return   The GuessGenerationNeeded value
106      */
107     public boolean isGuessGenerationNeeded()
108     {
109         return guessGenerationNeeded;
110     }
111 
112     /**
113      * Test if a Java source mmust be generated or not depending of timestamp of elements involved.
114      *
115      * @param clazz                 the Class from wich we generate
116      * @param file                  the File that will be generated
117      * @param withTemplate
118      * @return                      true if generation is needed
119      * @exception XDocletException
120      */
121     public boolean isGenerationNeeded(XClass clazz, File file, boolean withTemplate)
122          throws XDocletException
123     {
124         Log log = LogUtil.getLog(GenerationManager.class, "generation");
125 
126         if (subTask.getContext().isForce()) {
127             log.debug("Force generation enabled");
128             return true;
129         }
130 
131         if (isGuessGenerationNeeded() == false) {
132             log.debug("guessGenerationNeeded enabled");
133             return true;
134         }
135 
136         // 1. Check whether a file on classpath is newer than the destination file
137         if (isClasspathNewerThanFile(file))
138             return true;
139 
140         // 2. Check whether the class (or any superclass) is newer than the destination file
141         if (isClassHierarchyNewerThanFile(clazz, file))
142             return true;
143 
144         // 3. Check whether the template file or any merge files are newer than the destination file
145         if (isTemplateNewerThanFile(withTemplate, file))
146             return true;
147 
148         return false;
149     }
150 
151     /**
152      * Verify if the generation of a file to generate is needed because either the Template used to generate the file
153      * have a later timestamp, or because ALL the Java sources imported in this task have a sooner timestamp. This is
154      * used to test if xml files generation is needed.
155      *
156      * @param file                  The file to check
157      * @return                      true if the generation is needed
158      * @exception XDocletException
159      */
160     public boolean isGenerationNeeded(File file)
161          throws XDocletException
162     {
163         Log log = LogUtil.getLog(GenerationManager.class, "generation");
164 
165         log.debug("Generation need check for " + file.getName());
166 
167         if (subTask.getContext().isForce()) {
168             log.debug("Force generation enabled");
169             return true;
170         }
171 
172         if (isGuessGenerationNeeded() == false) {
173             log.debug("guessGenerationNeeded enabled");
174             return true;
175         }
176 
177         // 1. Check on Jar timestamp
178         if (isClasspathNewerThanFile(file) == true)
179             return true;
180 
181         // 2. Check the timestamp of template file and merge files
182         if (isGenerationNeeded(file, subTask.getTemplateURL())) {
183             return true;
184         }
185 
186         log.debug("Generation need check for " + file.getName());
187 
188         // 3. Check Timestamp of all java sources in sourcepath
189 
190         for (Iterator i = _xJavaDoc.getSourceClasses().iterator(); i.hasNext(); ) {
191             if (isGenerationNeeded((XClass) i.next(), file, false)) {
192                 return true;
193             }
194         }
195 
196         return false;
197     }
198 
199     /**
200      * Sets the GuessGenerationNeeded attribute of the GenerationManager object.
201      *
202      * @param guessGenerationNeeded  The new GuessGenerationNeeded value
203      */
204     public void setGuessGenerationNeeded(boolean guessGenerationNeeded)
205     {
206         this.guessGenerationNeeded = guessGenerationNeeded;
207     }
208 
209     private boolean isClassHierarchyNewerThanFile(XClass clazz, File file)
210     {
211         Log log = LogUtil.getLog(GenerationManager.class, "generation");
212 
213         while (clazz != null) {
214             if (clazz.getQualifiedName().equals("java.lang.Object")) {
215                 return false;
216             }
217             if (file.lastModified() < clazz.lastModified()) {
218                 if (log.isDebugEnabled()) {
219                     log.debug("Generation needed for '" + file.getAbsolutePath() + "' because " + clazz.getQualifiedName() + " is newer (it's in the class hierarchy)");
220                 }
221                 return true;
222             }
223             clazz = clazz.getSuperclass();
224         }
225 
226         return false;
227     }
228 
229     private boolean isTemplateNewerThanFile(boolean withTemplate, File file) throws XDocletException
230     {
231         Log log = LogUtil.getLog(GenerationManager.class, "generation");
232 
233         log.debug("Checking template. withTemplate=" + withTemplate);
234 
235         if (withTemplate) {
236             if (isGenerationNeeded(file, subTask.getTemplateURL())) {
237                 if (log.isDebugEnabled()) {
238                     log.debug("Generation needed for '" + file.getAbsolutePath() + "' because template file is newer.");
239                 }
240 
241                 return true;
242             }
243         }
244 
245         return false;
246     }
247 
248     private boolean isClasspathNewerThanFile(File file)
249     {
250         Log log = LogUtil.getLog(GenerationManager.class, "generation");
251 
252         if (file.lastModified() < newestJar.lastModified()) {
253             if (log.isDebugEnabled()) {
254                 log.debug("Generation needed for '" + file.getAbsolutePath() + "' because " + newestJar.getName() + " is newer.");
255             }
256             return true;
257         }
258 
259         if (log.isDebugEnabled()) {
260             log.debug("No files on classpath are newer than '" + file.getAbsolutePath() + "'");
261         }
262 
263         return false;
264     }
265 
266     /**
267      * Verify if the generation of a file is needed because either the template file has a sooner timestamp, or because
268      * one of the merge files have a sooner timestamp
269      *
270      * @param file                  The file to generate
271      * @param templateURL           the Template file to use
272      * @return                      true if generation is needed.
273      * @exception XDocletException
274      */
275     private boolean isGenerationNeeded(File file, URL templateURL)
276          throws XDocletException
277     {
278         Log log = LogUtil.getLog(GenerationManager.class, "xml");
279 
280         if (log.isDebugEnabled()) {
281             log.debug("Generation need check for " + file.getAbsolutePath());
282         }
283 
284         // 1. Check Timestamp of Template file
285         File templateFile = new File(subTask.getTemplateURL().getFile());
286 
287         if (templateFile.exists() && file.lastModified() < templateFile.lastModified()) {
288             if (log.isDebugEnabled()) {
289                 log.debug("Generation needed for '" + file.getAbsolutePath() + "' because of timestamp of " + subTask.getTemplateURL());
290             }
291             return true;
292         }
293         if (log.isDebugEnabled()) {
294             log.debug("Reject file '" + file.getAbsolutePath() + "' because of timestamp of " + subTask.getTemplateURL());
295         }
296 
297         // 2. Check timestamp of Merge files found inside Template
298         String[] files;
299 
300         if (getParserDb().get(templateFile) == null) {
301             TemplateEngine the_engine = subTask.getEngine();
302             TemplateParser the_parser = TemplateParser.getParserInstance();
303 
304             subTask.setEngine(the_parser);
305 
306             // Why is setOutput called here? We're only checking _IF_ we're going to generate! (Aslak)
307             the_parser.setOutput(file);
308             the_parser.setTemplateURL(templateURL);
309 
310             try {
311                 the_parser.start();
312             }
313             catch (TemplateException e) {
314                 throw new XDocletException(e, e.toString());
315             }
316 
317             files = the_parser.getMergeFiles();
318             if (files != null) {
319                 updateParserDb(templateURL, files);
320             }
321 
322             //restore
323             subTask.setEngine(the_engine);
324         }
325         else {
326             files = (String[]) getParserDb().get(new File(templateURL.getFile()).getName());
327             for (int i = 0; i < files.length; i++) {
328                 if (log.isDebugEnabled()) {
329                     log.debug(templateURL.getFile() + " : " + files[i]);
330                 }
331             }
332         }
333 
334         log.debug("Number of Merge files involved = " + files.length);
335 
336         for (int i = 0; i < files.length; i++) {
337             String mergeFilePattern = files[i];
338             List mergeFiles = new ArrayList();
339 
340             if (mergeFilePattern.indexOf("{0}") != -1) {
341 
342                 for (Iterator j = _xJavaDoc.getSourceClasses().iterator(); j.hasNext(); ) {
343                     XClass aClass = (XClass) j.next();
344                     String ejbName = MessageFormat.format(mergeFilePattern, new Object[]{AbstractProgramElementTagsHandler.getClassNameFor(aClass)});
345                     String mergeFileName = PackageTagsHandler.packageNameAsPathFor(aClass.getContainingPackage()) + File.separator + ejbName;
346 
347                     if (subTask.getMergeDir() != null)
348                         mergeFiles.add(new File(subTask.getMergeDir(), mergeFileName));
349                 }
350             }
351             else {
352                 if (subTask.getMergeDir() != null)
353                     mergeFiles.add(new File(subTask.getMergeDir(), mergeFilePattern));
354             }
355             for (Iterator iterator = mergeFiles.iterator(); iterator.hasNext(); ) {
356                 File mergeFile = (File) iterator.next();
357 
358                 log.debug("Generation check for '" + file.getAbsolutePath() + "' because of " + mergeFile.getName());
359 
360                 if (mergeFile.exists()) {
361                     if (file.lastModified() < mergeFile.lastModified()) {
362                         log.debug("Generation needed for '" + file.getAbsolutePath() + "' because of timestamp of " + mergeFile.getName());
363                         return true;
364                     }
365                     log.debug("Reject file '" + file.getAbsolutePath() + "' because of timestamp of " + mergeFile.getName());
366                 }
367             }
368         }
369         return false;
370     }
371 }