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

Quick Search    Search Deep

Source code: xdoclet/tagshandler/PackageTagsHandler.java


1   /*
2    * Copyright (c) 2001, 2002 The XDoclet team
3    * All rights reserved.
4    */
5   package xdoclet.tagshandler;
6   
7   import java.io.Serializable;
8   import java.util.*;
9   
10  import xjavadoc.*;
11  
12  import xdoclet.DocletContext;
13  import xdoclet.XDocletException;
14  import xdoclet.XDocletMessages;
15  import xdoclet.template.TemplateException;
16  import xdoclet.util.Translator;
17  
18  /**
19   * Tags which manipulate package names and packages, including substitutions.
20   *
21   * @author               Ara Abrahamian (ara_e@email.com)
22   * @created              Oct 14, 2001
23   * @xdoclet.taghandler   namespace="Package"
24   * @version              $Revision: 1.16 $
25   */
26  public class PackageTagsHandler extends AbstractProgramElementTagsHandler
27  {
28      /**
29       * Gets the name of a package, optionally applying any substitutions.
30       *
31       * @param pak               package
32       * @param withSubstitution  whether to apply any substitutions
33       * @return                  package name
34       */
35      public static String getPackageNameFor(XPackage pak, boolean withSubstitution)
36      {
37          return getPackageNameFor(pak.getName(), withSubstitution);
38      }
39  
40      /**
41       * Gets a package name with any subsitutions applied.
42       *
43       * @param packageName  package name
44       * @return             package name
45       */
46      public static String getPackageNameFor(String packageName)
47      {
48          return getPackageNameFor(packageName, true);
49      }
50  
51      /**
52       * Apply package substitutions. If <tt>useFirst</tt> is <tt>true</tt> , the first occurrence of <tt>substituteWith
53       * </tt> will be replaced by <tt>packages</tt> , else the one of the direct container of the current class.
54       *
55       * @param packageName       The (current) package name, on which substitution shall take place.
56       * @param withSubstitution  true if package substitutions shall take place.
57       * @return                  The package name after substitutions.
58       */
59      public static String getPackageNameFor(String packageName, boolean withSubstitution)
60      {
61          ArrayList packageSubstitutions = getPackageSubstitutions(DocletContext.getInstance().getActiveSubTask().getSubTaskName());
62  
63          if (packageSubstitutions == null || !withSubstitution) {
64              return packageName;
65          }
66  
67          for (int i = 0; i < packageSubstitutions.size(); i++) {
68              PackageSubstitution ps = (PackageSubstitution) packageSubstitutions.get(i);
69              StringTokenizer st = new StringTokenizer(ps.getPackages(), ",", false);
70  
71              if (ps.getUseFirst() == false) {
72                  while (st.hasMoreTokens()) {
73                      String packages = st.nextToken();
74                      String suffix = "." + packages;
75  
76                      if (packageName.endsWith(suffix)) {
77                          if (ps.getSubstituteWith() == null || ps.getSubstituteWith().length() == 0) {
78                              packageName = packageName.substring(0, packageName.length() - suffix.length());
79                          }
80                          else {
81                              packageName = packageName.substring(0, packageName.length() -
82                                  suffix.length()) + '.' + ps.getSubstituteWith();
83                          }
84                          break;
85                      }
86                  }
87              }
88              else {
89                  packageName = replaceInline(packageName, ps.getPackages(), ps.getSubstituteWith());
90              }
91          }
92  
93          return packageName;
94      }
95  
96      /**
97       * Gets any PackageSubstitutions defined for a specified subtask.
98       *
99       * @param subtaskName  subtask name
100      * @return             ArrayList of substitutions
101      */
102     public static ArrayList getPackageSubstitutions(String subtaskName)
103     {
104         // SubTask's packageSubstitutions has precedence over
105         // the global packageSubstitutions defined in DocletTask
106         ArrayList packageSubstitutions = null;
107         boolean supportsPackageSubstitutionInheritance = true;
108 
109         Boolean supports = ((Boolean) DocletContext.getInstance().getConfigParam(subtaskName + ".packageSubstitutionInheritanceSupported"));
110 
111         if (supports != null) {
112             supportsPackageSubstitutionInheritance = supports.booleanValue();
113         }
114 
115         packageSubstitutions = (ArrayList) DocletContext.getInstance().getConfigParam(subtaskName + ".packageSubstitutions");
116 
117         // nothing specified for subtask, inherit the one from DocletTask
118         if (supportsPackageSubstitutionInheritance && (packageSubstitutions == null || packageSubstitutions.isEmpty())) {
119             packageSubstitutions = (ArrayList) DocletContext.getInstance().getConfigParam("packageSubstitutions");
120         }
121 
122         return packageSubstitutions;
123     }
124 
125     /**
126      * Returns a package name as a path, after applying any substitutions.
127      *
128      * @param pak  package
129      * @return     package name as path
130      * @doc.tag    type="content"
131      */
132     public static String packageNameAsPathFor(XPackage pak)
133     {
134         return getPackageNameFor(pak, true).replace('.', '/');
135     }
136 
137     /**
138      * Returns a package name as a path, without applying any substitutions.
139      *
140      * @param pak  package
141      * @return     package name as path
142      * @doc.tag    type="content"
143      */
144     public static String packageNameAsPathWithoutSubstitutionFor(XPackage pak)
145     {
146         return getPackageNameFor(pak, false).replace('.', '/');
147     }
148 
149     /**
150      * Returns a package name as a path, after applying any substitutions.
151      *
152      * @param qualifiedName  package name
153      * @return               package name as path
154      * @doc.tag              type="content"
155      */
156     public static String packageNameAsPathFor(String qualifiedName)
157     {
158         String qName = qualifiedName;
159 
160         ArrayList pss = getPackageSubstitutions(DocletContext.getInstance().getActiveSubTask().getSubTaskName());
161 
162         PackageSubstitution ps;
163 
164         for (int i = 0; i < pss.size(); i++) {
165             ps = (PackageSubstitution) pss.get(i);
166             if (ps.getUseFirst() == true) {
167                 qName = replaceInline(qName, ps.getPackages(), ps.getSubstituteWith());
168             }
169         }
170 
171         return qName.replace('.', '/');
172     }
173 
174     /**
175      * Replace the first occurrence of <code>oldOne</code> in <code>original</code> with <code>newOne</code>, or returns
176      * the original string if <code>oldOne</code> is not found.
177      *
178      * @param original  String in which replacement should occour
179      * @param oldOne    String to be replaced
180      * @param newOne    String that replaces
181      * @return          String original string with replacements
182      */
183     public static String replaceInline(String original, String oldOne, String newOne)
184     {
185         int index = original.indexOf(oldOne);
186 
187         if (index > -1)
188             return original.substring(0, index) + newOne + original.substring(index + oldOne.length());
189         else
190             return original;
191     }
192 
193     /**
194      * Returns the current package name. If we're in the context of a package iteration, this is the name of the current
195      * package. If we're in the context of a class iteration without a package iteration, return the name of the current
196      * class' package.
197      *
198      * @return                      current package name
199      * @exception XDocletException  Description of Exception
200      * @doc.tag                     type="content"
201      */
202     public String packageName() throws XDocletException
203     {
204         if (getCurrentPackage() != null) {
205             // first try to get the name from current package. It exists if
206             return getCurrentPackage().getName();
207         }
208         else {
209             return getCurrentClass().getContainingPackage().getName();
210         }
211     }
212 
213     /**
214      * Returns the not-full-qualified package name of the full-qualified class name specified in the body of this tag.
215      *
216      * @param template              The body of the block tag
217      * @exception XDocletException  Description of Exception
218      * @doc.tag                     type="block"
219      */
220     public void packageOf(String template) throws XDocletException
221     {
222         getEngine().print(getPackageNameFrom(template));
223     }
224 
225     /**
226      * Writes the package declaration for the package name of the full-qualified class name specified in the body of
227      * this tag. No package declaration is written if the full-qualified class name has no package.
228      *
229      * @param template              The body of the block tag
230      * @exception XDocletException  Description of Exception
231      * @doc.tag                     type="block"
232      */
233     public void packageDeclarationOf(String template) throws XDocletException
234     {
235         String packageName = getPackageNameFrom(template);
236 
237         if (packageName != null && packageName.length() > 0) {
238             getEngine().print("package " + packageName + ";");
239         }
240     }
241 
242     /**
243      * Iterates over all packages loaded by XJavadoc. Subsequent calls to forAllClasses will only iterate over the
244      * classes in the current package.
245      *
246      * @param template              The body of the block tag
247      * @param attributes            The attributes of the template tag
248      * @exception XDocletException  Description of Exception
249      * @doc.tag                     type="block"
250      * @doc.param                   name="abstract" optional="true" values="true,false" description="If true then accept
251      *      abstract classes also; otherwise don't."
252      * @doc.param                   name="type" optional="true" description="For all classes by the type."
253      * @doc.param                   name="extent" optional="true" values="concrete-type,superclass,hierarchy"
254      *      description="Specifies the extent of the type search. If concrete-type then only check the concrete type, if
255      *      superclass then check also superclass, if hierarchy then search the whole hierarchy and find if the class is
256      *      of the specified type. Default is hierarchy."
257      */
258     public void forAllPackages(String template, Properties attributes) throws XDocletException
259     {
260         Collection classes = getXJavaDoc().getSourceClasses();
261         SortedSet packages = new TreeSet();
262 
263         for (Iterator i = classes.iterator(); i.hasNext(); ) {
264             XClass clazz = (XClass) i.next();
265 
266             packages.add(clazz.getContainingPackage());
267         }
268 
269         XPackage currentPackage = null;
270 
271         for (Iterator packageIterator = packages.iterator(); packageIterator.hasNext(); ) {
272             currentPackage = (XPackage) packageIterator.next();
273             setCurrentPackage(currentPackage);
274             generate(template);
275         }
276         // restore current package to null, so subsequent class iterations can
277         // perform outside the context of a current package
278         setCurrentPackage(null);
279     }
280 
281     /**
282      * Returns the current package name as a path.
283      *
284      * @return                      current package name as path
285      * @exception XDocletException  Description of Exception
286      * @doc.tag                     type="content"
287      */
288     public String packageNameAsPath() throws XDocletException
289     {
290         return packageNameAsPathFor(packageName());
291     }
292 
293     /**
294      * Returns the package name for the full-qualified class name specified in the body of the passed tag.
295      *
296      * @param template              The body of the block tag
297      * @return                      the package name or an empty string if the full-qualified class name has no package
298      * @exception XDocletException  Description of Exception
299      */
300     private String getPackageNameFrom(String template) throws XDocletException
301     {
302         try {
303             String fullClassName = getEngine().outputOf(template);
304             int pos = fullClassName.lastIndexOf('.');
305 
306             if (pos < 0) {
307                 return "";
308             }
309             else {
310                 return getPackageNameFor(fullClassName.substring(0, pos), true);
311             }
312         }
313         catch (TemplateException ex) {
314             throw new XDocletException(ex, Translator.getString(XDocletMessages.class, XDocletMessages.METHOD_FAILED, new String[]{"packageOf"}));
315         }
316 
317     }
318 
319     /**
320      * It's good practice to put interfaces (such as remote/local interfaces, data objects and home interfaces) in a
321      * separate "interfaces" package rather than in the EJB bean implementation package. Previous versions of XDoclet
322      * dictated this behavior, so if package name of a bean ended with <code>.beans</code> or <code>.ejb</code>
323      * interfaces were put into .interfaces package. It's no more the case. You have full control over it. If you don't
324      * use a <code>packageSubstitution</code> element, then all interfaces are generated to the same package as the bean
325      * implementation class. But if you want to follow the pattern and put interfaces into a separate package you can,
326      * by providing the list of package name tails that interfaces of beans inside that packages should be placed into
327      * the package you define. For example interfaces of <code>test.ejb.CustomerBean</code> will be placed in <code>test.interfaces</code>
328      * by the following <code>packageSubstitution</code>:<p/>
329      *
330      * <pre><code>
331      *&lt;packageSubstitution packages="ejb,beans" substituteWith="interfaces" /&gt;
332      *</code></pre> <p/>
333      *
334      * By using the <code>useFirst</code> attribute, you can tell XDoclet to substitute the first occurrence and not the
335      * last. <br/>
336      * Now if you have a structure like<p/>
337      *
338      * com.acme.foo.bar.ejb <br/>
339      * com.acme.baz.lala.ejb<p/>
340      *
341      * you want to gather all interfaces under one root/subtree like e.g. <p/>
342      *
343      * com.acme.interfaces.bar.* <br/>
344      * com.acme.interfaces.lala.*<p/>
345      *
346      * now you can say:<br/>
347      * &lt;packagesubstitution packages="foo,baz" substituteWith="interfaces" useFirst="true"/&gt;
348      *
349      * @created   10. september 2002
350      */
351     public static class PackageSubstitution implements Serializable
352     {
353         private String packages = null;
354         private String substituteWith = null;
355         private boolean useFirst = false;
356 
357         /**
358          * Get the comma-separated list of packages to be substituted.
359          *
360          * @return   package list
361          */
362         public String getPackages()
363         {
364             return packages;
365         }
366 
367         /**
368          * Get the substitute package name.
369          *
370          * @return   package
371          */
372         public String getSubstituteWith()
373         {
374             return substituteWith;
375         }
376 
377         /**
378          * Return the useFirst attribute. This attribute specifies if the substitution can only appear at the end of a
379          * package (useFirst=false) or also in the middle.
380          *
381          * @return   boolean
382          */
383         public boolean getUseFirst()
384         {
385             return this.useFirst;
386         }
387 
388         /**
389          * Set the comma-separated list of packages to be substituted.
390          *
391          * @param packages  The new Packages value
392          */
393         public void setPackages(String packages)
394         {
395             this.packages = packages;
396         }
397 
398         /**
399          * Set the substitute package name.
400          *
401          * @param substituteWith  The new SubstituteWith value
402          */
403         public void setSubstituteWith(String substituteWith)
404         {
405             this.substituteWith = substituteWith;
406         }
407 
408         /**
409          * Specify whether the first occurrence of a package from the list should be substituted, or the last.
410          *
411          * @param first  should the first occurrence be used or not?
412          */
413         public void setUseFirst(boolean first)
414         {
415             this.useFirst = first;
416 
417         }
418     }
419 }