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.lang.annotation.Annotation;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.Iterator;
028: import java.util.LinkedHashMap;
029: import java.util.List;
030: import java.util.Map;
031: import java.util.Set;
032: import java.util.TreeMap;
033: import java.util.TreeSet;
034:
035: /**
036: * A generated Java class/interface/enum/....
037: *
038: * <p>
039: * This class models a declaration, and since a declaration can be always
040: * used as a reference, it inherits {@link JClass}.
041: *
042: * <h2>Where to go from here?</h2>
043: * <p>
044: * You'd want to generate fields and methods on a class.
045: * See {@link #method(int, JType, String)} and {@link #field(int, JType, String)}.
046: */
047: public class JDefinedClass extends JClass implements JDeclaration,
048: JClassContainer, JGenerifiable, JAnnotatable {
049:
050: /** Name of this class. Null if anonymous. */
051: private String name = null;
052:
053: /** Modifiers for the class declaration */
054: private JMods mods;
055:
056: /** Name of the super class of this class. */
057: private JClass super Class;
058:
059: /** List of interfaces that this class implements */
060: private final Set<JClass> interfaces = new TreeSet<JClass>();
061:
062: /** Fields keyed by their names. */
063: /*package*/final Map<String, JFieldVar> fields = new LinkedHashMap<String, JFieldVar>();
064:
065: /** Static initializer, if this class has one */
066: private JBlock init = null;
067:
068: /** class javadoc */
069: private JDocComment jdoc = null;
070:
071: /** Set of constructors for this class, if any */
072: private final List<JMethod> constructors = new ArrayList<JMethod>();
073:
074: /** Set of methods that are members of this class */
075: private final List<JMethod> methods = new ArrayList<JMethod>();
076:
077: /**
078: * Nested classes as a map from name to JDefinedClass.
079: * The name is all capitalized in a case sensitive file system
080: * ({@link JCodeModel#isCaseSensitiveFileSystem}) to avoid conflicts.
081: *
082: * Lazily created to save footprint.
083: *
084: * @see #getClasses()
085: */
086: private Map<String, JDefinedClass> classes;
087:
088: /**
089: * Flag that controls whether this class should be really generated or not.
090: *
091: * Sometimes it is useful to generate code that refers to class X,
092: * without actually generating the code of X.
093: * This flag is used to surpress X.java file in the output.
094: */
095: private boolean hideFile = false;
096:
097: /**
098: * Client-app spcific metadata associated with this user-created class.
099: */
100: public Object metadata;
101:
102: /**
103: * String that will be put directly inside the generated code.
104: * Can be null.
105: */
106: private String directBlock;
107:
108: /**
109: * If this is a package-member class, this is {@link JPackage}.
110: * If this is a nested class, this is {@link JDefinedClass}.
111: * If this is an anonymous class, this constructor shouldn't be used.
112: */
113: private JClassContainer outer = null;
114:
115: /** Default value is class or interface
116: * or annotationTypeDeclaration
117: * or enum
118: *
119: */
120: private final ClassType classType;
121:
122: /** List containing the enum value declarations
123: *
124: */
125: // private List enumValues = new ArrayList();
126: /**
127: * Set of enum constants that are keyed by names.
128: * In Java, enum constant order is actually significant,
129: * because of order ID they get. So let's preserve the order.
130: */
131: private final Map<String, JEnumConstant> enumConstantsByName = new LinkedHashMap<String, JEnumConstant>();
132:
133: /**
134: * Annotations on this variable. Lazily created.
135: */
136: private List<JAnnotationUse> annotations = null;
137:
138: /**
139: * Helper class to implement {@link JGenerifiable}.
140: */
141: private final JGenerifiableImpl generifiable = new JGenerifiableImpl() {
142: protected JCodeModel owner() {
143: return JDefinedClass.this .owner();
144: }
145: };
146:
147: JDefinedClass(JClassContainer parent, int mods, String name,
148: ClassType classTypeval) {
149: this (mods, name, parent, parent.owner(), classTypeval);
150: }
151:
152: /**
153: * Constructor for creating anonymous inner class.
154: */
155: JDefinedClass(JCodeModel owner, int mods, String name) {
156: this (mods, name, null, owner);
157: }
158:
159: private JDefinedClass(int mods, String name,
160: JClassContainer parent, JCodeModel owner) {
161: this (mods, name, parent, owner, ClassType.CLASS);
162: }
163:
164: /**
165: * JClass constructor
166: *
167: * @param mods
168: * Modifiers for this class declaration
169: *
170: * @param name
171: * Name of this class
172: */
173: private JDefinedClass(int mods, String name,
174: JClassContainer parent, JCodeModel owner,
175: ClassType classTypeVal) {
176: super (owner);
177:
178: if (name != null) {
179: if (name.trim().length() == 0)
180: throw new IllegalArgumentException("JClass name empty");
181:
182: if (!Character.isJavaIdentifierStart(name.charAt(0))) {
183: String msg = "JClass name " + name
184: + " contains illegal character"
185: + " for beginning of identifier: "
186: + name.charAt(0);
187: throw new IllegalArgumentException(msg);
188: }
189: for (int i = 1; i < name.length(); i++) {
190: if (!Character.isJavaIdentifierPart(name.charAt(i))) {
191: String msg = "JClass name " + name
192: + " contains illegal character "
193: + name.charAt(i);
194: throw new IllegalArgumentException(msg);
195: }
196: }
197: }
198:
199: this .classType = classTypeVal;
200: if (isInterface())
201: this .mods = JMods.forInterface(mods);
202: else
203: this .mods = JMods.forClass(mods);
204:
205: this .name = name;
206:
207: this .outer = parent;
208: }
209:
210: /**
211: * Returns true if this is an anonymous class.
212: */
213: public final boolean isAnonymous() {
214: return name == null;
215: }
216:
217: /**
218: * This class extends the specifed class.
219: *
220: * @param superClass
221: * Superclass for this class
222: *
223: * @return This class
224: */
225: public JDefinedClass _extends(JClass super Class) {
226: if (this .classType == ClassType.INTERFACE)
227: throw new IllegalArgumentException(
228: "unable to set the super class for an interface");
229: if (super Class == null)
230: throw new NullPointerException();
231:
232: for (JClass o = super Class.outer(); o != null; o = o.outer()) {
233: if (this == o) {
234: throw new IllegalArgumentException(
235: "Illegal class inheritance loop."
236: + " Outer class "
237: + this .name
238: + " may not subclass from inner class: "
239: + o.name());
240: }
241: }
242:
243: this .super Class = super Class;
244: return this ;
245: }
246:
247: public JDefinedClass _extends(Class super Class) {
248: return _extends(owner().ref(super Class));
249: }
250:
251: /**
252: * Returns the class extended by this class.
253: */
254: public JClass _extends() {
255: if (super Class == null)
256: super Class = owner().ref(Object.class);
257: return super Class;
258: }
259:
260: /**
261: * This class implements the specifed interface.
262: *
263: * @param iface
264: * Interface that this class implements
265: *
266: * @return This class
267: */
268: public JDefinedClass _implements (JClass iface) {
269: interfaces.add(iface);
270: return this ;
271: }
272:
273: public JDefinedClass _implements (Class iface) {
274: return _implements (owner().ref(iface));
275: }
276:
277: /**
278: * Returns an iterator that walks the nested classes defined in this
279: * class.
280: */
281: public Iterator<JClass> _implements () {
282: return interfaces.iterator();
283: }
284:
285: /**
286: * JClass name accessor.
287: *
288: * <p>
289: * For example, for <code>java.util.List</code>, this method
290: * returns <code>"List"</code>"
291: *
292: * @return Name of this class
293: */
294: public String name() {
295: return name;
296: }
297:
298: /**
299: * If the named enum already exists, the reference to it is returned.
300: * Otherwise this method generates a new enum reference with the given
301: * name and returns it.
302: *
303: * @param name
304: * The name of the constant.
305: * @return
306: * The generated type-safe enum constant.
307: */
308: public JEnumConstant enumConstant(String name) {
309: JEnumConstant ec = enumConstantsByName.get(name);
310: if (null == ec) {
311: ec = new JEnumConstant(this , name);
312: enumConstantsByName.put(name, ec);
313: }
314: return ec;
315: }
316:
317: /**
318: * Gets the fully qualified name of this class.
319: */
320: public String fullName() {
321: if (outer instanceof JDefinedClass)
322: return ((JDefinedClass) outer).fullName() + '.' + name();
323:
324: JPackage p = _package();
325: if (p.isUnnamed())
326: return name();
327: else
328: return p.name() + '.' + name();
329: }
330:
331: public String binaryName() {
332: if (outer instanceof JDefinedClass)
333: return ((JDefinedClass) outer).binaryName() + '$' + name();
334: else
335: return fullName();
336: }
337:
338: public boolean isInterface() {
339: return this .classType == ClassType.INTERFACE;
340: }
341:
342: public boolean isAbstract() {
343: return mods.isAbstract();
344: }
345:
346: /**
347: * Adds a field to the list of field members of this JDefinedClass.
348: *
349: * @param mods
350: * Modifiers for this field
351: *
352: * @param type
353: * JType of this field
354: *
355: * @param name
356: * Name of this field
357: *
358: * @return Newly generated field
359: */
360: public JFieldVar field(int mods, JType type, String name) {
361: return field(mods, type, name, null);
362: }
363:
364: public JFieldVar field(int mods, Class type, String name) {
365: return field(mods, owner()._ref(type), name);
366: }
367:
368: /**
369: * Adds a field to the list of field members of this JDefinedClass.
370: *
371: * @param mods
372: * Modifiers for this field.
373: * @param type
374: * JType of this field.
375: * @param name
376: * Name of this field.
377: * @param init
378: * Initial value of this field.
379: *
380: * @return Newly generated field
381: */
382: public JFieldVar field(int mods, JType type, String name,
383: JExpression init) {
384: JFieldVar f = new JFieldVar(this , JMods.forField(mods), type,
385: name, init);
386:
387: if (fields.put(name, f) != null)
388: throw new IllegalArgumentException(
389: "trying to create the same field twice: " + name);
390:
391: return f;
392: }
393:
394: /** This method indicates if the interface
395: * is an annotationTypeDeclaration
396: *
397: */
398: public boolean isAnnotationTypeDeclaration() {
399: return this .classType == ClassType.ANNOTATION_TYPE_DECL;
400:
401: }
402:
403: /**
404: * Add an annotationType Declaration to this package
405: * @param name
406: * Name of the annotation Type declaration to be added to this package
407: * @return
408: * newly created Annotation Type Declaration
409: * @exception JClassAlreadyExistsException
410: * When the specified class/interface was already created.
411:
412: */
413: public JDefinedClass _annotationTypeDeclaration(String name)
414: throws JClassAlreadyExistsException {
415: return _class(JMod.PUBLIC, name, ClassType.ANNOTATION_TYPE_DECL);
416: }
417:
418: /**
419: * Add a public enum to this package
420: * @param name
421: * Name of the enum to be added to this package
422: * @return
423: * newly created Enum
424: * @exception JClassAlreadyExistsException
425: * When the specified class/interface was already created.
426:
427: */
428: public JDefinedClass _enum(String name)
429: throws JClassAlreadyExistsException {
430: return _class(JMod.PUBLIC, name, ClassType.ENUM);
431: }
432:
433: /**
434: * Add a public enum to this package
435: * @param name
436: * Name of the enum to be added to this package
437: * @param mods
438: * Modifiers for this enum declaration
439: * @return
440: * newly created Enum
441: * @exception JClassAlreadyExistsException
442: * When the specified class/interface was already created.
443:
444: */
445: public JDefinedClass _enum(int mods, String name)
446: throws JClassAlreadyExistsException {
447: return _class(mods, name, ClassType.ENUM);
448: }
449:
450: public ClassType getClassType() {
451: return this .classType;
452: }
453:
454: public JFieldVar field(int mods, Class type, String name,
455: JExpression init) {
456: return field(mods, owner()._ref(type), name, init);
457: }
458:
459: /**
460: * Returns all the fields declred in this class.
461: * The returned {@link Map} is a read-only live view.
462: *
463: * @return always non-null.
464: */
465: public Map<String, JFieldVar> fields() {
466: return Collections.unmodifiableMap(fields);
467: }
468:
469: /**
470: * Removes a {@link JFieldVar} from this class.
471: *
472: * @throws IllegalArgumentException
473: * if the given field is not a field on this class.
474: */
475: public void removeField(JFieldVar field) {
476: if (fields.remove(field.name()) != field)
477: throw new IllegalArgumentException();
478: }
479:
480: /**
481: * Creates, if necessary, and returns the static initializer
482: * for this class.
483: *
484: * @return JBlock containing initialization statements for this class
485: */
486: public JBlock init() {
487: if (init == null)
488: init = new JBlock();
489: return init;
490: }
491:
492: /**
493: * Adds a constructor to this class.
494: *
495: * @param mods
496: * Modifiers for this constructor
497: */
498: public JMethod constructor(int mods) {
499: JMethod c = new JMethod(mods, this );
500: constructors.add(c);
501: return c;
502: }
503:
504: /**
505: * Returns an iterator that walks the constructors defined in this class.
506: */
507: public Iterator constructors() {
508: return constructors.iterator();
509: }
510:
511: /**
512: * Looks for a method that has the specified method signature
513: * and return it.
514: *
515: * @return
516: * null if not found.
517: */
518: public JMethod getConstructor(JType[] argTypes) {
519: for (JMethod m : constructors) {
520: if (m.hasSignature(argTypes))
521: return m;
522: }
523: return null;
524: }
525:
526: /**
527: * Add a method to the list of method members of this JDefinedClass instance.
528: *
529: * @param mods
530: * Modifiers for this method
531: *
532: * @param type
533: * Return type for this method
534: *
535: * @param name
536: * Name of the method
537: *
538: * @return Newly generated JMethod
539: */
540: public JMethod method(int mods, JType type, String name) {
541: // XXX problems caught in M constructor
542: JMethod m = new JMethod(this , mods, type, name);
543: methods.add(m);
544: return m;
545: }
546:
547: public JMethod method(int mods, Class type, String name) {
548: return method(mods, owner()._ref(type), name);
549: }
550:
551: /**
552: * Returns the set of methods defined in this class.
553: */
554: public Collection<JMethod> methods() {
555: return methods;
556: }
557:
558: /**
559: * Looks for a method that has the specified method signature
560: * and return it.
561: *
562: * @return
563: * null if not found.
564: */
565: public JMethod getMethod(String name, JType[] argTypes) {
566: for (JMethod m : methods) {
567: if (!m.name().equals(name))
568: continue;
569:
570: if (m.hasSignature(argTypes))
571: return m;
572: }
573: return null;
574: }
575:
576: public boolean isClass() {
577: return true;
578: }
579:
580: public boolean isPackage() {
581: return false;
582: }
583:
584: public JPackage getPackage() {
585: return parentContainer().getPackage();
586: }
587:
588: /**
589: * Add a new nested class to this class.
590: *
591: * @param mods
592: * Modifiers for this class declaration
593: *
594: * @param name
595: * Name of class to be added to this package
596: *
597: * @return Newly generated class
598: */
599: public JDefinedClass _class(int mods, String name)
600: throws JClassAlreadyExistsException {
601: return _class(mods, name, ClassType.CLASS);
602: }
603:
604: /**
605: * {@inheritDoc}
606: *
607: * @deprecated
608: */
609: public JDefinedClass _class(int mods, String name,
610: boolean isInterface) throws JClassAlreadyExistsException {
611: return _class(mods, name, isInterface ? ClassType.INTERFACE
612: : ClassType.CLASS);
613: }
614:
615: public JDefinedClass _class(int mods, String name,
616: ClassType classTypeVal) throws JClassAlreadyExistsException {
617:
618: String NAME;
619: if (JCodeModel.isCaseSensitiveFileSystem)
620: NAME = name.toUpperCase();
621: else
622: NAME = name;
623:
624: if (getClasses().containsKey(NAME))
625: throw new JClassAlreadyExistsException(getClasses().get(
626: NAME));
627: else {
628: // XXX problems caught in the NC constructor
629: JDefinedClass c = new JDefinedClass(this , mods, name,
630: classTypeVal);
631: getClasses().put(NAME, c);
632: return c;
633: }
634: }
635:
636: /**
637: * Add a new public nested class to this class.
638: */
639: public JDefinedClass _class(String name)
640: throws JClassAlreadyExistsException {
641: return _class(JMod.PUBLIC, name);
642: }
643:
644: /**
645: * Add an interface to this package.
646: *
647: * @param mods
648: * Modifiers for this interface declaration
649: *
650: * @param name
651: * Name of interface to be added to this package
652: *
653: * @return Newly generated interface
654: */
655: public JDefinedClass _interface(int mods, String name)
656: throws JClassAlreadyExistsException {
657: return _class(mods, name, ClassType.INTERFACE);
658: }
659:
660: /**
661: * Adds a public interface to this package.
662: */
663: public JDefinedClass _interface(String name)
664: throws JClassAlreadyExistsException {
665: return _interface(JMod.PUBLIC, name);
666: }
667:
668: /**
669: * Creates, if necessary, and returns the class javadoc for this
670: * JDefinedClass
671: *
672: * @return JDocComment containing javadocs for this class
673: */
674: public JDocComment javadoc() {
675: if (jdoc == null)
676: jdoc = new JDocComment(owner());
677: return jdoc;
678: }
679:
680: /**
681: * Mark this file as hidden, so that this file won't be
682: * generated.
683: *
684: * <p>
685: * This feature could be used to generate code that refers
686: * to class X, without actually generating X.java.
687: */
688: public void hide() {
689: hideFile = true;
690: }
691:
692: public boolean isHidden() {
693: return hideFile;
694: }
695:
696: /**
697: * Returns an iterator that walks the nested classes defined in this
698: * class.
699: */
700: public final Iterator<JDefinedClass> classes() {
701: if (classes == null)
702: return Collections.<JDefinedClass> emptyList().iterator();
703: else
704: return classes.values().iterator();
705: }
706:
707: private Map<String, JDefinedClass> getClasses() {
708: if (classes == null)
709: classes = new TreeMap<String, JDefinedClass>();
710: return classes;
711: }
712:
713: /**
714: * Returns all the nested classes defined in this class.
715: */
716: public final JClass[] listClasses() {
717: if (classes == null)
718: return new JClass[0];
719: else
720: return classes.values().toArray(
721: new JClass[classes.values().size()]);
722: }
723:
724: @Override
725: public JClass outer() {
726: if (outer.isClass())
727: return (JClass) outer;
728: else
729: return null;
730: }
731:
732: public void declare(JFormatter f) {
733: if (jdoc != null)
734: f.nl().g(jdoc);
735:
736: if (annotations != null) {
737: for (JAnnotationUse annotation : annotations)
738: f.g(annotation).nl();
739: }
740:
741: f.g(mods).p(classType.declarationToken).id(name)
742: .d(generifiable);
743:
744: if (super Class != null
745: && super Class != owner().ref(Object.class))
746: f.nl().i().p("extends").g(super Class).nl().o();
747:
748: if (!interfaces.isEmpty()) {
749: if (super Class == null)
750: f.nl();
751: f.i().p(
752: classType == ClassType.INTERFACE ? "extends"
753: : "implements");
754: f.g(interfaces);
755: f.nl().o();
756: }
757: declareBody(f);
758: }
759:
760: /**
761: * prints the body of a class.
762: */
763: protected void declareBody(JFormatter f) {
764: f.p('{').nl().nl().i();
765: boolean first = true;
766:
767: if (!enumConstantsByName.isEmpty()) {
768: for (JEnumConstant c : enumConstantsByName.values()) {
769: if (!first)
770: f.p(',').nl();
771: f.d(c);
772: first = false;
773: }
774: f.p(';').nl();
775: }
776:
777: for (JFieldVar field : fields.values())
778: f.d(field);
779: if (init != null)
780: f.nl().p("static").s(init);
781: for (JMethod m : constructors) {
782: f.nl().d(m);
783: }
784: for (JMethod m : methods) {
785: f.nl().d(m);
786: }
787: if (classes != null)
788: for (JDefinedClass dc : classes.values())
789: f.nl().d(dc);
790:
791: if (directBlock != null)
792: f.p(directBlock);
793: f.nl().o().p('}').nl();
794: }
795:
796: /**
797: * Places the given string directly inside the generated class.
798: *
799: * This method can be used to add methods/fields that are not
800: * generated by CodeModel.
801: * This method should be used only as the last resort.
802: */
803: public void direct(String string) {
804: if (directBlock == null)
805: directBlock = string;
806: else
807: directBlock += string;
808: }
809:
810: public final JPackage _package() {
811: JClassContainer p = outer;
812: while (!(p instanceof JPackage))
813: p = p.parentContainer();
814: return (JPackage) p;
815: }
816:
817: public final JClassContainer parentContainer() {
818: return outer;
819: }
820:
821: public JTypeVar generify(String name) {
822: return generifiable.generify(name);
823: }
824:
825: public JTypeVar generify(String name, Class bound) {
826: return generifiable.generify(name, bound);
827: }
828:
829: public JTypeVar generify(String name, JClass bound) {
830: return generifiable.generify(name, bound);
831: }
832:
833: @Override
834: public JTypeVar[] typeParams() {
835: return generifiable.typeParams();
836: }
837:
838: protected JClass substituteParams(JTypeVar[] variables,
839: List<JClass> bindings) {
840: return this ;
841: }
842:
843: /** Adding ability to annotate a class
844: * @param clazz
845: * The annotation class to annotate the class with
846: */
847: public JAnnotationUse annotate(Class<? extends Annotation> clazz) {
848: return annotate(owner().ref(clazz));
849: }
850:
851: /** Adding ability to annotate a class
852: * @param clazz
853: * The annotation class to annotate the class with
854: */
855: public JAnnotationUse annotate(JClass clazz) {
856: if (annotations == null)
857: annotations = new ArrayList<JAnnotationUse>();
858: JAnnotationUse a = new JAnnotationUse(clazz);
859: annotations.add(a);
860: return a;
861: }
862:
863: public <W extends JAnnotationWriter> W annotate2(Class<W> clazz) {
864: return TypedAnnotationWriter.create(clazz, this);
865: }
866: }
|