001: /*
002: * The contents of this file are subject to the terms
003: * of the Common Development and Distribution License
004: * (the "License"). You may not use this file except
005: * in compliance with the License.
006: *
007: * You can obtain a copy of the license at
008: * https://jwsdp.dev.java.net/CDDLv1.0.html
009: * See the License for the specific language governing
010: * permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL
013: * HEADER in each file and include the License file at
014: * https://jwsdp.dev.java.net/CDDLv1.0.html If applicable,
015: * add the following below this CDDL HEADER, with the
016: * fields enclosed by brackets "[]" replaced with your
017: * own identifying information: Portions Copyright [yyyy]
018: * [name of copyright owner]
019: */
020:
021: package com.sun.codemodel;
022:
023: import java.io.BufferedOutputStream;
024: import java.io.BufferedWriter;
025: import java.io.File;
026: import java.io.IOException;
027: import java.io.OutputStream;
028: import java.io.PrintWriter;
029: import java.io.Writer;
030: import java.lang.annotation.Annotation;
031: import java.util.ArrayList;
032: import java.util.HashMap;
033: import java.util.HashSet;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.Set;
038: import java.util.TreeMap;
039:
040: /**
041: * A Java package.
042: */
043: public final class JPackage implements JDeclaration, JGenerable,
044: JClassContainer, JAnnotatable, Comparable<JPackage> {
045:
046: /**
047: * Name of the package.
048: * May be the empty string for the root package.
049: */
050: private String name;
051:
052: private final JCodeModel owner;
053:
054: /**
055: * List of classes contained within this package keyed by their name.
056: */
057: private final Map<String, JDefinedClass> classes = new TreeMap<String, JDefinedClass>();
058:
059: /**
060: * List of resources files inside this package.
061: */
062: private final Set<JResourceFile> resources = new HashSet<JResourceFile>();
063:
064: /**
065: * All {@link JClass}s in this package keyed the upper case class name.
066: *
067: * This field is non-null only on Windows, to detect
068: * "Foo" and "foo" as a collision.
069: */
070: private final Map<String, JDefinedClass> upperCaseClassMap;
071:
072: /**
073: * Lazily created list of package annotations.
074: */
075: private List<JAnnotationUse> annotations = null;
076:
077: /**
078: * package javadoc.
079: */
080: private JDocComment jdoc = null;
081:
082: /**
083: * JPackage constructor
084: *
085: * @param name
086: * Name of package
087: *
088: * @param cw The code writer being used to create this package
089: *
090: * @throws IllegalArgumentException
091: * If each part of the package name is not a valid identifier
092: */
093: JPackage(String name, JCodeModel cw) {
094: this .owner = cw;
095: if (name.equals(".")) {
096: String msg = "Package name . is not allowed";
097: throw new IllegalArgumentException(msg);
098: }
099:
100: if (JCodeModel.isCaseSensitiveFileSystem)
101: upperCaseClassMap = null;
102: else
103: upperCaseClassMap = new HashMap<String, JDefinedClass>();
104:
105: this .name = name;
106: }
107:
108: public JClassContainer parentContainer() {
109: return parent();
110: }
111:
112: /**
113: * Gets the parent package, or null if this class is the root package.
114: */
115: public JPackage parent() {
116: if (name.length() == 0)
117: return null;
118:
119: int idx = name.lastIndexOf('.');
120: return owner._package(name.substring(0, idx));
121: }
122:
123: public boolean isClass() {
124: return false;
125: }
126:
127: public boolean isPackage() {
128: return true;
129: }
130:
131: public JPackage getPackage() {
132: return this ;
133: }
134:
135: /**
136: * Add a class to this package.
137: *
138: * @param mods
139: * Modifiers for this class declaration
140: *
141: * @param name
142: * Name of class to be added to this package
143: *
144: * @return Newly generated class
145: *
146: * @exception JClassAlreadyExistsException
147: * When the specified class/interface was already created.
148: */
149: public JDefinedClass _class(int mods, String name)
150: throws JClassAlreadyExistsException {
151: return _class(mods, name, ClassType.CLASS);
152: }
153:
154: /**
155: * {@inheritDoc}
156: * @deprecated
157: */
158: public JDefinedClass _class(int mods, String name,
159: boolean isInterface) throws JClassAlreadyExistsException {
160: return _class(mods, name, isInterface ? ClassType.INTERFACE
161: : ClassType.CLASS);
162: }
163:
164: public JDefinedClass _class(int mods, String name,
165: ClassType classTypeVal) throws JClassAlreadyExistsException {
166: if (classes.containsKey(name))
167: throw new JClassAlreadyExistsException(classes.get(name));
168: else {
169: // XXX problems caught in the NC constructor
170: JDefinedClass c = new JDefinedClass(this , mods, name,
171: classTypeVal);
172:
173: if (upperCaseClassMap != null) {
174: JDefinedClass dc = upperCaseClassMap.get(name
175: .toUpperCase());
176: if (dc != null)
177: throw new JClassAlreadyExistsException(dc);
178: upperCaseClassMap.put(name.toUpperCase(), c);
179: }
180: classes.put(name, c);
181: return c;
182: }
183: }
184:
185: /**
186: * Adds a public class to this package.
187: */
188: public JDefinedClass _class(String name)
189: throws JClassAlreadyExistsException {
190: return _class(JMod.PUBLIC, name);
191: }
192:
193: /**
194: * Gets a reference to the already created {@link JDefinedClass}.
195: *
196: * @return null
197: * If the class is not yet created.
198: */
199: public JDefinedClass _getClass(String name) {
200: if (classes.containsKey(name))
201: return classes.get(name);
202: else
203: return null;
204: }
205:
206: /**
207: * Order is based on the lexicological order of the package name.
208: */
209: public int compareTo(JPackage that) {
210: return this .name.compareTo(that.name);
211: }
212:
213: /**
214: * Add an interface to this package.
215: *
216: * @param mods
217: * Modifiers for this interface declaration
218: *
219: * @param name
220: * Name of interface to be added to this package
221: *
222: * @return Newly generated interface
223: */
224: public JDefinedClass _interface(int mods, String name)
225: throws JClassAlreadyExistsException {
226: return _class(mods, name, ClassType.INTERFACE);
227: }
228:
229: /**
230: * Adds a public interface to this package.
231: */
232: public JDefinedClass _interface(String name)
233: throws JClassAlreadyExistsException {
234: return _interface(JMod.PUBLIC, name);
235: }
236:
237: /**
238: * Add an annotationType Declaration to this package
239: * @param name
240: * Name of the annotation Type declaration to be added to this package
241: * @return
242: * newly created Annotation Type Declaration
243: * @exception JClassAlreadyExistsException
244: * When the specified class/interface was already created.
245:
246: */
247: public JDefinedClass _annotationTypeDeclaration(String name)
248: throws JClassAlreadyExistsException {
249: return _class(JMod.PUBLIC, name, ClassType.ANNOTATION_TYPE_DECL);
250: }
251:
252: /**
253: * Add a public enum to this package
254: * @param name
255: * Name of the enum to be added to this package
256: * @return
257: * newly created Enum
258: * @exception JClassAlreadyExistsException
259: * When the specified class/interface was already created.
260:
261: */
262: public JDefinedClass _enum(String name)
263: throws JClassAlreadyExistsException {
264: return _class(JMod.PUBLIC, name, ClassType.ENUM);
265: }
266:
267: /**
268: * Adds a new resource file to this package.
269: */
270: public JResourceFile addResourceFile(JResourceFile rsrc) {
271: resources.add(rsrc);
272: return rsrc;
273: }
274:
275: /**
276: * Checks if a resource of the given name exists.
277: */
278: public boolean hasResourceFile(String name) {
279: for (JResourceFile r : resources)
280: if (r.name().equals(name))
281: return true;
282: return false;
283: }
284:
285: /**
286: * Iterates all resource files in this package.
287: */
288: public Iterator propertyFiles() {
289: return resources.iterator();
290: }
291:
292: /**
293: * Creates, if necessary, and returns the package javadoc for this
294: * JDefinedClass.
295: *
296: * @return JDocComment containing javadocs for this class
297: */
298: public JDocComment javadoc() {
299: if (jdoc == null)
300: jdoc = new JDocComment(owner());
301: return jdoc;
302: }
303:
304: /**
305: * Removes a class from this package.
306: */
307: public void remove(JClass c) {
308: if (c._package() != this )
309: throw new IllegalArgumentException(
310: "the specified class is not a member of this package,"
311: + " or it is a referenced class");
312:
313: // note that c may not be a member of classes.
314: // this happens when someone is trying to remove a non generated class
315: classes.remove(c.name());
316: if (upperCaseClassMap != null)
317: upperCaseClassMap.remove(c.name().toUpperCase());
318: }
319:
320: /**
321: * Reference a class within this package.
322: */
323: public JClass ref(String name) throws ClassNotFoundException {
324: if (name.indexOf('.') >= 0)
325: throw new IllegalArgumentException(
326: "JClass name contains '.': " + name);
327:
328: String n = "";
329: if (!isUnnamed())
330: n = this .name + '.';
331: n += name;
332:
333: return owner.ref(Class.forName(n));
334: }
335:
336: /**
337: * Gets a reference to a sub package of this package.
338: */
339: public JPackage subPackage(String pkg) {
340: if (isUnnamed())
341: return owner()._package(pkg);
342: else
343: return owner()._package(name + '.' + pkg);
344: }
345:
346: /**
347: * Returns an iterator that walks the top-level classes defined in this
348: * package.
349: */
350: public Iterator<JDefinedClass> classes() {
351: return classes.values().iterator();
352: }
353:
354: /**
355: * Checks if a given name is already defined as a class/interface
356: */
357: public boolean isDefined(String classLocalName) {
358: Iterator itr = classes();
359: while (itr.hasNext()) {
360: if (((JClass) itr.next()).name().equals(classLocalName))
361: return true;
362: }
363:
364: return false;
365: }
366:
367: /**
368: * Checks if this package is the root, unnamed package.
369: */
370: public final boolean isUnnamed() {
371: return name.length() == 0;
372: }
373:
374: /**
375: * Get the name of this package
376: *
377: * @return
378: * The name of this package, or the empty string if this is the
379: * null package. For example, this method returns strings like
380: * <code>"java.lang"</code>
381: */
382: public String name() {
383: return name;
384: }
385:
386: /**
387: * Return the code model root object being used to create this package.
388: */
389: public final JCodeModel owner() {
390: return owner;
391: }
392:
393: public JAnnotationUse annotate(JClass clazz) {
394: if (isUnnamed())
395: throw new IllegalArgumentException(
396: "the root package cannot be annotated");
397: if (annotations == null)
398: annotations = new ArrayList<JAnnotationUse>();
399: JAnnotationUse a = new JAnnotationUse(clazz);
400: annotations.add(a);
401: return a;
402: }
403:
404: public JAnnotationUse annotate(Class<? extends Annotation> clazz) {
405: return annotate(owner.ref(clazz));
406: }
407:
408: public <W extends JAnnotationWriter> W annotate2(Class<W> clazz) {
409: return TypedAnnotationWriter.create(clazz, this );
410: }
411:
412: /**
413: * Convert the package name to directory path equivalent
414: */
415: File toPath(File dir) {
416: if (name == null)
417: return dir;
418: return new File(dir, name.replace('.', File.separatorChar));
419: }
420:
421: public void declare(JFormatter f) {
422: if (name.length() != 0)
423: f.p("package").p(name).p(';').nl();
424: }
425:
426: public void generate(JFormatter f) {
427: f.p(name);
428: }
429:
430: void build(CodeWriter src, CodeWriter res) throws IOException {
431:
432: // write classes
433: for (JDefinedClass c : classes.values()) {
434: if (c.isHidden())
435: continue; // don't generate this file
436:
437: JFormatter f = createJavaSourceFileWriter(src, c.name());
438: f.write(c);
439: f.close();
440: }
441:
442: // write package annotations
443: if (annotations != null || jdoc != null) {
444: JFormatter f = createJavaSourceFileWriter(src,
445: "package-info");
446:
447: if (jdoc != null)
448: f.g(jdoc);
449:
450: // TODO: think about importing
451: if (annotations != null) {
452: for (JAnnotationUse a : annotations)
453: f.g(a).nl();
454: }
455: f.d(this );
456:
457: f.close();
458: }
459:
460: // write resources
461: for (JResourceFile rsrc : resources) {
462: CodeWriter cw = rsrc.isResource() ? res : src;
463: OutputStream os = new BufferedOutputStream(cw.openBinary(
464: this , rsrc.name()));
465: rsrc.build(os);
466: os.close();
467: }
468: }
469:
470: /*package*/int countArtifacts() {
471: int r = 0;
472: for (JDefinedClass c : classes.values()) {
473: if (c.isHidden())
474: continue; // don't generate this file
475: r++;
476: }
477:
478: if (annotations != null || jdoc != null) {
479: r++;
480: }
481:
482: r += resources.size();
483:
484: return r;
485: }
486:
487: private JFormatter createJavaSourceFileWriter(CodeWriter src,
488: String className) throws IOException {
489: Writer bw = new BufferedWriter(src.openSource(this , className
490: + ".java"));
491: return new JFormatter(new PrintWriter(bw));
492: }
493: }
|