View Javadoc
1   /*
2    * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.codemodel.internal;
27  
28  import java.io.BufferedOutputStream;
29  import java.io.BufferedWriter;
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.OutputStream;
33  import java.io.PrintWriter;
34  import java.io.Writer;
35  import java.lang.annotation.Annotation;
36  import java.util.ArrayList;
37  import java.util.HashMap;
38  import java.util.HashSet;
39  import java.util.Iterator;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.Set;
43  import java.util.TreeMap;
44  import java.util.Collection;
45  import java.util.Collections;
46  
47  
48  /**
49   * A Java package.
50   */
51  public final class JPackage implements JDeclaration, JGenerable, JClassContainer, JAnnotatable, Comparable<JPackage>, JDocCommentable {
52  
53      /**
54       * Name of the package.
55       * May be the empty string for the root package.
56       */
57      private String name;
58  
59      private final JCodeModel owner;
60  
61      /**
62       * List of classes contained within this package keyed by their name.
63       */
64      private final Map<String,JDefinedClass> classes = new TreeMap<String,JDefinedClass>();
65  
66      /**
67       * List of resources files inside this package.
68       */
69      private final Set<JResourceFile> resources = new HashSet<JResourceFile>();
70  
71      /**
72       * All {@link JClass}s in this package keyed the upper case class name.
73       *
74       * This field is non-null only on Windows, to detect
75       * "Foo" and "foo" as a collision.
76       */
77      private final Map<String,JDefinedClass> upperCaseClassMap;
78  
79      /**
80       * Lazily created list of package annotations.
81       */
82      private List<JAnnotationUse> annotations = null;
83  
84      /**
85       * package javadoc.
86       */
87      private JDocComment jdoc = null;
88  
89      /**
90       * JPackage constructor
91       *
92       * @param name
93       *        Name of package
94       *
95       * @param  cw  The code writer being used to create this package
96       *
97       * @throws IllegalArgumentException
98       *         If each part of the package name is not a valid identifier
99       */
100     JPackage(String name, JCodeModel cw) {
101         this.owner = cw;
102         if (name.equals(".")) {
103             String msg = "Package name . is not allowed";
104             throw new IllegalArgumentException(msg);
105         }
106 
107         if(JCodeModel.isCaseSensitiveFileSystem)
108             upperCaseClassMap = null;
109         else
110             upperCaseClassMap = new HashMap<String,JDefinedClass>();
111 
112         this.name = name;
113     }
114 
115 
116     public JClassContainer parentContainer() {
117         return parent();
118     }
119 
120     /**
121      * Gets the parent package, or null if this class is the root package.
122      */
123     public JPackage parent() {
124         if(name.length()==0)    return null;
125 
126         int idx = name.lastIndexOf('.');
127         return owner._package(name.substring(0,idx));
128     }
129 
130     public boolean isClass() { return false; }
131     public boolean isPackage() { return true; }
132     public JPackage getPackage() { return this; }
133 
134     /**
135      * Add a class to this package.
136      *
137      * @param mods
138      *        Modifiers for this class declaration
139      *
140      * @param name
141      *        Name of class to be added to this package
142      *
143      * @return Newly generated class
144      *
145      * @exception JClassAlreadyExistsException
146      *      When the specified class/interface was already created.
147      */
148     public JDefinedClass _class(int mods, String name) throws JClassAlreadyExistsException {
149         return _class(mods,name,ClassType.CLASS);
150     }
151 
152     /**
153      * {@inheritDoc}
154      * @deprecated
155      */
156     public JDefinedClass _class( int mods, String name, boolean isInterface ) throws JClassAlreadyExistsException {
157         return _class(mods,name, isInterface?ClassType.INTERFACE:ClassType.CLASS );
158     }
159 
160     public JDefinedClass _class( int mods, String name, ClassType classTypeVal ) throws JClassAlreadyExistsException {
161         if(classes.containsKey(name))
162             throw new JClassAlreadyExistsException(classes.get(name));
163         else {
164             // XXX problems caught in the NC constructor
165             JDefinedClass c = new JDefinedClass(this, mods, name, classTypeVal);
166 
167             if( upperCaseClassMap!=null ) {
168                 JDefinedClass dc = upperCaseClassMap.get(name.toUpperCase());
169                 if(dc!=null)
170                     throw new JClassAlreadyExistsException(dc);
171                 upperCaseClassMap.put(name.toUpperCase(),c);
172             }
173             classes.put(name,c);
174             return c;
175         }
176     }
177 
178         /**
179          * Adds a public class to this package.
180          */
181     public JDefinedClass _class(String name) throws JClassAlreadyExistsException {
182                 return _class( JMod.PUBLIC, name );
183         }
184 
185     /**
186      * Gets a reference to the already created {@link JDefinedClass}.
187      *
188      * @return null
189      *      If the class is not yet created.
190      */
191     public JDefinedClass _getClass(String name) {
192         if(classes.containsKey(name))
193             return classes.get(name);
194         else
195             return null;
196     }
197 
198     /**
199      * Order is based on the lexicological order of the package name.
200      */
201     public int compareTo(JPackage that) {
202         return this.name.compareTo(that.name);
203     }
204 
205     /**
206      * Add an interface to this package.
207      *
208      * @param mods
209      *        Modifiers for this interface declaration
210      *
211      * @param name
212      *        Name of interface to be added to this package
213      *
214      * @return Newly generated interface
215      */
216     public JDefinedClass _interface(int mods, String name) throws JClassAlreadyExistsException {
217         return _class(mods,name,ClassType.INTERFACE);
218     }
219 
220     /**
221      * Adds a public interface to this package.
222      */
223     public JDefinedClass _interface(String name) throws JClassAlreadyExistsException {
224         return _interface(JMod.PUBLIC, name);
225     }
226 
227     /**
228      * Add an annotationType Declaration to this package
229      * @param name
230      *      Name of the annotation Type declaration to be added to this package
231      * @return
232      *      newly created Annotation Type Declaration
233      * @exception JClassAlreadyExistsException
234      *      When the specified class/interface was already created.
235 
236      */
237     public JDefinedClass _annotationTypeDeclaration(String name) throws JClassAlreadyExistsException {
238         return _class (JMod.PUBLIC,name,ClassType.ANNOTATION_TYPE_DECL);
239     }
240 
241     /**
242      * Add a public enum to this package
243      * @param name
244      *      Name of the enum to be added to this package
245      * @return
246      *      newly created Enum
247      * @exception JClassAlreadyExistsException
248      *      When the specified class/interface was already created.
249 
250      */
251     public JDefinedClass _enum (String name) throws JClassAlreadyExistsException {
252         return _class (JMod.PUBLIC,name,ClassType.ENUM);
253     }
254     /**
255      * Adds a new resource file to this package.
256      */
257     public JResourceFile addResourceFile(JResourceFile rsrc) {
258         resources.add(rsrc);
259         return rsrc;
260     }
261 
262     /**
263      * Checks if a resource of the given name exists.
264      */
265     public boolean hasResourceFile(String name) {
266         for (JResourceFile r : resources)
267             if (r.name().equals(name))
268                 return true;
269         return false;
270     }
271 
272     /**
273      * Iterates all resource files in this package.
274      */
275     public Iterator<JResourceFile> propertyFiles() {
276         return resources.iterator();
277     }
278 
279     /**
280      * Creates, if necessary, and returns the package javadoc for this
281      * JDefinedClass.
282      *
283      * @return JDocComment containing javadocs for this class
284      */
285     public JDocComment javadoc() {
286         if (jdoc == null)
287             jdoc = new JDocComment(owner());
288         return jdoc;
289     }
290 
291     /**
292      * Removes a class from this package.
293      */
294     public void remove(JClass c) {
295         if (c._package() != this)
296             throw new IllegalArgumentException(
297                 "the specified class is not a member of this package," + " or it is a referenced class");
298 
299         // note that c may not be a member of classes.
300         // this happens when someone is trying to remove a non generated class
301         classes.remove(c.name());
302         if (upperCaseClassMap != null)
303             upperCaseClassMap.remove(c.name().toUpperCase());
304     }
305 
306     /**
307      * Reference a class within this package.
308      */
309     public JClass ref(String name) throws ClassNotFoundException {
310         if (name.indexOf('.') >= 0)
311             throw new IllegalArgumentException("JClass name contains '.': " + name);
312 
313         String n = "";
314         if (!isUnnamed())
315             n = this.name + '.';
316         n += name;
317 
318         return owner.ref(Class.forName(n));
319     }
320 
321     /**
322      * Gets a reference to a sub package of this package.
323      */
324     public JPackage subPackage( String pkg ) {
325         if(isUnnamed())     return owner()._package(pkg);
326         else                return owner()._package(name+'.'+pkg);
327     }
328 
329     /**
330      * Returns an iterator that walks the top-level classes defined in this
331      * package.
332      */
333     public Iterator<JDefinedClass> classes() {
334         return classes.values().iterator();
335     }
336 
337     /**
338      * Checks if a given name is already defined as a class/interface
339      */
340     public boolean isDefined(String classLocalName) {
341         Iterator<JDefinedClass> itr = classes();
342         while (itr.hasNext()) {
343             if ((itr.next()).name().equals(classLocalName))
344                 return true;
345         }
346 
347         return false;
348     }
349 
350     /**
351      * Checks if this package is the root, unnamed package.
352      */
353     public final boolean isUnnamed() { return name.length() == 0; }
354 
355     /**
356      * Get the name of this package
357      *
358      * @return
359      *          The name of this package, or the empty string if this is the
360      *          null package. For example, this method returns strings like
361      *          <code>"java.lang"</code>
362      */
363     public String name() {
364         return name;
365     }
366 
367     /**
368      * Return the code model root object being used to create this package.
369      */
370     public final JCodeModel owner() { return owner; }
371 
372 
373     public JAnnotationUse annotate(JClass clazz) {
374         if(isUnnamed())
375             throw new IllegalArgumentException("the root package cannot be annotated");
376         if(annotations==null)
377            annotations = new ArrayList<JAnnotationUse>();
378         JAnnotationUse a = new JAnnotationUse(clazz);
379         annotations.add(a);
380         return a;
381     }
382 
383     public JAnnotationUse annotate(Class<? extends Annotation> clazz) {
384         return annotate(owner.ref(clazz));
385     }
386 
387     public <W extends JAnnotationWriter> W annotate2(Class<W> clazz) {
388         return TypedAnnotationWriter.create(clazz,this);
389     }
390 
391     public Collection<JAnnotationUse> annotations() {
392         if (annotations == null)
393             annotations = new ArrayList<JAnnotationUse>();
394         return Collections.unmodifiableList(annotations);
395     }
396 
397     /**
398      * Convert the package name to directory path equivalent
399      */
400     File toPath(File dir) {
401         if (name == null) return dir;
402         return new File(dir, name.replace('.', File.separatorChar));
403     }
404 
405     public void declare(JFormatter f ) {
406         if (name.length() != 0)
407             f.p("package").p(name).p(';').nl();
408     }
409 
410     public void generate(JFormatter f) {
411         f.p(name);
412     }
413 
414 
415     void build( CodeWriter src, CodeWriter res ) throws IOException {
416 
417         // write classes
418         for (JDefinedClass c : classes.values()) {
419             if (c.isHidden())
420                 continue;   // don't generate this file
421 
422             JFormatter f = createJavaSourceFileWriter(src, c.name());
423             f.write(c);
424             f.close();
425         }
426 
427         // write package annotations
428         if(annotations!=null || jdoc!=null) {
429             JFormatter f = createJavaSourceFileWriter(src,"package-info");
430 
431             if (jdoc != null)
432                 f.g(jdoc);
433 
434             // TODO: think about importing
435             if (annotations != null){
436                 for (JAnnotationUse a : annotations)
437                     f.g(a).nl();
438             }
439             f.d(this);
440 
441             f.close();
442         }
443 
444         // write resources
445         for (JResourceFile rsrc : resources) {
446             CodeWriter cw = rsrc.isResource() ? res : src;
447             OutputStream os = new BufferedOutputStream(cw.openBinary(this, rsrc.name()));
448             rsrc.build(os);
449             os.close();
450         }
451     }
452 
453     /*package*/ int countArtifacts() {
454         int r = 0;
455         for (JDefinedClass c : classes.values()) {
456             if (c.isHidden())
457                 continue;   // don't generate this file
458             r++;
459         }
460 
461         if(annotations!=null || jdoc!=null) {
462             r++;
463         }
464 
465         r+= resources.size();
466 
467         return r;
468     }
469 
470     private JFormatter createJavaSourceFileWriter(CodeWriter src, String className) throws IOException {
471         Writer bw = new BufferedWriter(src.openSource(this,className+".java"));
472         return new JFormatter(new PrintWriter(bw));
473     }
474 }