001: /*
002: * Copyright 2006 Ralf Joachim
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package org.exolab.javasource;
017:
018: import java.util.Enumeration;
019: import java.util.Vector;
020:
021: /**
022: * A abstract base class for representations of the Java Source code for a Java Class.
023: *
024: * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
025: * @version $Revision: 6668 $ $Date: 2005-05-08 12:32:06 -0600 (Sun, 08 May 2005) $
026: * @since 1.1
027: */
028: public abstract class AbstractJClass extends JStructure {
029: //--------------------------------------------------------------------------
030:
031: /** The source code for static initialization. */
032: private JSourceCode _staticInitializer;
033:
034: /** The list of member variables (fields) of this JClass. */
035: private JNamedMap _fields;
036:
037: /** The list of constructors for this JClass. */
038: private Vector _constructors;
039:
040: /** The list of methods of this JClass. */
041: private Vector _methods;
042:
043: /** A collection of inner classes for this JClass. */
044: private Vector _innerClasses;
045:
046: //--------------------------------------------------------------------------
047:
048: /**
049: * Creates a new AbstractJClass with the given name.
050: *
051: * @param name The name of the AbstractJClass to create.
052: */
053: protected AbstractJClass(final String name) {
054: super (name);
055:
056: _staticInitializer = new JSourceCode();
057: _fields = new JNamedMap();
058: _constructors = new Vector();
059: _methods = new Vector();
060: _innerClasses = null;
061:
062: //-- initialize default Java doc
063: getJDocComment().appendComment("Class " + getLocalName() + ".");
064: }
065:
066: //--------------------------------------------------------------------------
067:
068: /**
069: * Returns the JSourceCode for the static initializer of this JClass.
070: *
071: * @return The JSourceCode for the static initializer of this JClass.
072: */
073: public final JSourceCode getStaticInitializationCode() {
074: return _staticInitializer;
075: }
076:
077: /**
078: * {@inheritDoc}
079: */
080: public final JField getField(final String name) {
081: return (JField) _fields.get(name);
082: }
083:
084: /**
085: * {@inheritDoc}
086: */
087: public final JField[] getFields() {
088: int size = _fields.size();
089: JField[] farray = new JField[size];
090: for (int i = 0; i < size; i++) {
091: farray[i] = (JField) _fields.get(i);
092: }
093: return farray;
094: }
095:
096: /**
097: * {@inheritDoc}
098: */
099: public final void addField(final JField jField) {
100: if (jField == null) {
101: throw new IllegalArgumentException(
102: "Class members cannot be null");
103: }
104:
105: String name = jField.getName();
106:
107: if (_fields.get(name) != null) {
108: String nameToCompare = (name.startsWith("_")) ? name
109: .substring(1) : name;
110: nameToCompare = nameToCompare.substring(0, 1).toUpperCase()
111: + nameToCompare.substring(1);
112: if (JNaming.isReservedByCastor(nameToCompare)) {
113: String warn = "'"
114: + nameToCompare
115: + "' might conflict with a field name used"
116: + " by Castor. If you get a complaint\nabout a duplicate name, you will"
117: + " need to use a mapping file or change the name\nof the conflicting"
118: + " schema element.";
119: System.out.println(warn);
120: }
121:
122: String err = "Duplicate name found as a class member: "
123: + name;
124: throw new IllegalArgumentException(err);
125: }
126: _fields.put(name, jField);
127: }
128:
129: /**
130: * Removes the field with the given name from this JClass.
131: *
132: * @param name The name of the field to remove.
133: * @return The JField if it was found and removed.
134: */
135: public final JField removeField(final String name) {
136: if (name == null) {
137: return null;
138: }
139:
140: JField field = (JField) _fields.remove(name);
141:
142: //-- clean up imports
143: //-- NOT YET IMPLEMENTED
144: return field;
145: }
146:
147: /**
148: * Removes the given JField from this JClass.
149: *
150: * @param jField The JField to remove.
151: * @return true if the field was found and removed.
152: */
153: public final boolean removeField(final JField jField) {
154: if (jField == null) {
155: return false;
156: }
157:
158: Object field = _fields.get(jField.getName());
159: if (field == jField) {
160: _fields.remove(jField.getName());
161: return true;
162: }
163: //-- clean up imports
164: //-- NOT YET IMPLEMENTED
165: return false;
166: }
167:
168: /**
169: * Creates a new JConstructor and adds it to this JClass.
170: *
171: * @return The newly created constructor.
172: */
173: public final JConstructor createConstructor() {
174: return createConstructor(null);
175: }
176:
177: /**
178: * Creates a new JConstructor and adds it to this JClass.
179: *
180: * @param params A list of parameters for this constructor.
181: * @return The newly created constructor.
182: */
183: public final JConstructor createConstructor(
184: final JParameter[] params) {
185: JConstructor cons = new JConstructor(this );
186: if (params != null) {
187: for (int i = 0; i < params.length; i++) {
188: cons.addParameter(params[i]);
189: }
190: }
191: addConstructor(cons);
192: return cons;
193: }
194:
195: /**
196: * Returns the constructor at the specified index.
197: *
198: * @param index The index of the constructor to return.
199: * @return The JConstructor at the specified index.
200: */
201: public final JConstructor getConstructor(final int index) {
202: return (JConstructor) _constructors.elementAt(index);
203: }
204:
205: /**
206: * Returns the an array of the JConstructors contained within this JClass.
207: *
208: * @return An array of JConstructor.
209: */
210: public final JConstructor[] getConstructors() {
211: int size = _constructors.size();
212: JConstructor[] jcArray = new JConstructor[size];
213:
214: for (int i = 0; i < _constructors.size(); i++) {
215: jcArray[i] = (JConstructor) _constructors.elementAt(i);
216: }
217: return jcArray;
218: }
219:
220: /**
221: * Adds the given Constructor to this classes list of constructors. The
222: * constructor must have been created with this JClass' createConstructor.
223: *
224: * @param constructor The constructor to add.
225: */
226: public final void addConstructor(final JConstructor constructor) {
227: if (constructor == null) {
228: throw new IllegalArgumentException(
229: "Constructors cannot be null");
230: }
231:
232: if (constructor.getDeclaringClass() == this ) {
233:
234: /** check signatures (add later) **/
235: if (!_constructors.contains(constructor)) {
236: _constructors.addElement(constructor);
237: }
238: } else {
239: String err = "The given JConstructor was not created by this JClass";
240: throw new IllegalArgumentException(err);
241: }
242: }
243:
244: /**
245: * Removes the given constructor from this JClass.
246: *
247: * @param constructor The JConstructor to remove.
248: * @return true if the constructor was removed, otherwise false.
249: */
250: public final boolean removeConstructor(
251: final JConstructor constructor) {
252: return _constructors.removeElement(constructor);
253: }
254:
255: /**
256: * Returns an array of all the JMethods of this JClass.
257: *
258: * @return An array of all the JMethods of this JClass.
259: */
260: public final JMethod[] getMethods() {
261: int size = _methods.size();
262: JMethod[] marray = new JMethod[size];
263:
264: for (int i = 0; i < _methods.size(); i++) {
265: marray[i] = (JMethod) _methods.elementAt(i);
266: }
267: return marray;
268: }
269:
270: /**
271: * Returns the first occurance of the method with the given name, starting
272: * from the specified index.
273: *
274: * @param name The name of the method to look for.
275: * @param startIndex The starting index to begin the search.
276: * @return The method if found, otherwise null.
277: */
278: public final JMethod getMethod(final String name,
279: final int startIndex) {
280: for (int i = startIndex; i < _methods.size(); i++) {
281: JMethod jMethod = (JMethod) _methods.elementAt(i);
282: if (jMethod.getName().equals(name)) {
283: return jMethod;
284: }
285: }
286: return null;
287: }
288:
289: /**
290: * Returns the JMethod located at the specified index.
291: *
292: * @param index The index of the JMethod to return.
293: * @return The JMethod.
294: */
295: public final JMethod getMethod(final int index) {
296: return (JMethod) _methods.elementAt(index);
297: }
298:
299: /**
300: * Adds the given JMethod to this JClass.
301: *
302: * @param jMethod The JMethod to add.
303: * @param importReturnType true if we add the importReturnType to the class
304: * import lists. It could be useful to set it to false when all
305: * types are fully qualified.
306: */
307: public final void addMethod(final JMethod jMethod,
308: final boolean importReturnType) {
309: if (jMethod == null) {
310: throw new IllegalArgumentException(
311: "Class methods cannot be null");
312: }
313:
314: //-- check method name and signatures *add later*
315:
316: //-- keep method list sorted for esthetics when printing
317: //-- START SORT :-)
318: boolean added = false;
319: JModifiers modifiers = jMethod.getModifiers();
320:
321: if (modifiers.isAbstract()) {
322: getModifiers().setAbstract(true);
323: }
324:
325: for (int i = 0; i < _methods.size(); i++) {
326: JMethod tmp = (JMethod) _methods.elementAt(i);
327: //-- first compare modifiers
328: if (tmp.getModifiers().isPrivate()) {
329: if (!modifiers.isPrivate()) {
330: _methods.insertElementAt(jMethod, i);
331: added = true;
332: break;
333: }
334: }
335: //-- compare names
336: if (jMethod.getName().compareTo(tmp.getName()) < 0) {
337: _methods.insertElementAt(jMethod, i);
338: added = true;
339: break;
340: }
341: }
342: //-- END SORT
343: if (!added) {
344: _methods.addElement(jMethod);
345: }
346:
347: }
348:
349: /**
350: * Adds the given JMethod to this JClass.
351: *
352: * @param jMethod The JMethod to add.
353: */
354: public final void addMethod(final JMethod jMethod) {
355: addMethod(jMethod, true);
356: }
357:
358: /**
359: * Adds the given array of JMethods to this JClass.
360: *
361: * @param jMethods The JMethod[] to add.
362: */
363: public final void addMethods(final JMethod[] jMethods) {
364: for (int i = 0; i < jMethods.length; i++) {
365: addMethod(jMethods[i]);
366: }
367: }
368:
369: /**
370: * Removes the given method from this JClass.
371: *
372: * @param method The JMethod to remove.
373: * @return true if the method was removed, otherwise false.
374: */
375: public final boolean removeMethod(final JMethod method) {
376: return _methods.removeElement(method);
377: }
378:
379: /**
380: * Creates and returns an inner-class for this JClass.
381: *
382: * @param localname The name of the class (no package name).
383: * @return the new JClass.
384: */
385: public final JClass createInnerClass(final String localname) {
386: if (localname == null) {
387: String err = "argument 'localname' must not be null.";
388: throw new IllegalArgumentException(err);
389: }
390: if (localname.indexOf('.') >= 0) {
391: String err = "The name of an inner-class must not contain a package name.";
392: throw new IllegalArgumentException(err);
393: }
394: String classname = getPackageName();
395: if (classname != null) {
396: classname = classname + "." + localname;
397: } else {
398: classname = localname;
399: }
400:
401: JClass innerClass = new JInnerClass(classname);
402: if (_innerClasses == null) {
403: _innerClasses = new Vector();
404: }
405: _innerClasses.addElement(innerClass);
406: return innerClass;
407:
408: }
409:
410: /**
411: * Returns an array of JClass (the inner classes) contained within this
412: * JClass.
413: *
414: * @return An array of JClass contained within this JClass.
415: */
416: public final JClass[] getInnerClasses() {
417: if (_innerClasses != null) {
418: int size = _innerClasses.size();
419: JClass[] carray = new JClass[size];
420: _innerClasses.copyInto(carray);
421: return carray;
422: }
423: return new JClass[0];
424: }
425:
426: /**
427: * Removes the given inner-class (JClass) from this JClass.
428: *
429: * @param jClass The JClass (inner-class) to remove.
430: * @return true if the JClass was removed, otherwise false.
431: */
432: public final boolean removeInnerClass(final JClass jClass) {
433: if (_innerClasses != null) {
434: return _innerClasses.removeElement(jClass);
435: }
436: return false;
437: }
438:
439: //--------------------------------------------------------------------------
440:
441: /**
442: * {@inheritDoc}
443: */
444: public final void print(final JSourceWriter jsw) {
445: print(jsw, false);
446: }
447:
448: /**
449: * Prints the source code for this JClass to the given JSourceWriter.
450: *
451: * @param classOnly If true, the file header, package declaration, and
452: * imports are not printed.
453: * @param jsw The JSourceWriter to print to. Must not be null.
454: */
455: public abstract void print(final JSourceWriter jsw,
456: final boolean classOnly);
457:
458: /**
459: * Writes to the JSourceWriter the headers for this class file. Headers
460: * include the comment-header, the package declaration, and the imports.
461: *
462: * @param jsw The JSourceWriter to be used.
463: */
464: protected final void printClassHeaders(final JSourceWriter jsw) {
465: printHeader(jsw);
466: printPackageDeclaration(jsw);
467:
468: //-- get imports from inner-classes
469: Vector removeImports = null;
470: if ((_innerClasses != null) && (_innerClasses.size() > 0)) {
471: removeImports = new Vector();
472: for (int i = 0; i < _innerClasses.size(); i++) {
473: JClass iClass = (JClass) _innerClasses.elementAt(i);
474: Enumeration enumeration = iClass.getImports();
475: while (enumeration.hasMoreElements()) {
476: String classname = (String) enumeration
477: .nextElement();
478:
479: int paramTypeIndex = classname.indexOf("<Object>");
480: if (paramTypeIndex != -1) {
481: classname = classname.substring(0,
482: paramTypeIndex - 1);
483: }
484: if (!hasImport(classname)) {
485: addImport(classname);
486: removeImports.addElement(classname);
487: }
488: }
489: }
490: }
491:
492: printImportDeclarations(jsw);
493:
494: //-- remove imports from inner-classes, if necessary
495: if (removeImports != null) {
496: for (int i = 0; i < removeImports.size(); i++) {
497: removeImport((String) removeImports.elementAt(i));
498: }
499: }
500: }
501:
502: /**
503: * Writes to the JSourceWriter the member variables of this class.
504: *
505: * @param jsw The JSourceWriter to be used.
506: */
507: protected final void printMemberVariables(final JSourceWriter jsw) {
508: if (_fields.size() > 0) {
509: jsw.writeln();
510: jsw.writeln(" //--------------------------/");
511: jsw.writeln(" //- Class/Member Variables -/");
512: jsw.writeln("//--------------------------/");
513: jsw.writeln();
514: }
515:
516: for (int i = 0; i < _fields.size(); i++) {
517: JField jField = (JField) _fields.get(i);
518:
519: //-- print Java comment
520: JDocComment comment = jField.getComment();
521: if (comment != null) {
522: comment.print(jsw);
523: }
524:
525: //-- print Annotations
526: jField.printAnnotations(jsw);
527:
528: // -- print member
529: jsw.write(jField.getModifiers().toString());
530: jsw.write(' ');
531:
532: JType type = jField.getType();
533: String typeName = type.toString();
534: //-- for esthetics use short name in some cases
535: if (typeName.equals(toString())) {
536: typeName = type.getLocalName();
537: }
538: jsw.write(typeName);
539: jsw.write(' ');
540: jsw.write(jField.getName());
541:
542: String init = jField.getInitString();
543: if (init != null && !jField.isDateTime()) {
544: jsw.write(" = ");
545: jsw.write(init);
546: }
547:
548: jsw.writeln(';');
549: jsw.writeln();
550: }
551: }
552:
553: /**
554: * Writes to the JSourceWriter any static initialization used by this class.
555: *
556: * @param jsw The JSourceWriter to be used.
557: */
558: protected final void printStaticInitializers(final JSourceWriter jsw) {
559: //----------------------/
560: //- Static Initializer -/
561: //----------------------/
562:
563: if (!_staticInitializer.isEmpty()) {
564: jsw.writeln();
565: jsw.writeln("static {");
566: jsw.writeln(_staticInitializer.toString());
567: jsw.writeln("};");
568: jsw.writeln();
569: }
570: }
571:
572: /**
573: * Writes to the JSourceWriter all constructors for this class.
574: *
575: * @param jsw The JSourceWriter to be used.
576: */
577: protected final void printConstructors(final JSourceWriter jsw) {
578: if (_constructors.size() > 0) {
579: jsw.writeln();
580: jsw.writeln(" //----------------/");
581: jsw.writeln(" //- Constructors -/");
582: jsw.writeln("//----------------/");
583: jsw.writeln();
584: }
585:
586: for (int i = 0; i < _constructors.size(); i++) {
587: JConstructor jConstructor = (JConstructor) _constructors
588: .elementAt(i);
589: jConstructor.print(jsw);
590: jsw.writeln();
591: }
592: }
593:
594: /**
595: * Writes to the JSourceWriter all methods belonging to this class.
596: *
597: * @param jsw The JSourceWriter to be used.
598: */
599: protected final void printMethods(final JSourceWriter jsw) {
600: if (_methods.size() > 0) {
601: jsw.writeln();
602: jsw.writeln(" //-----------/");
603: jsw.writeln(" //- Methods -/");
604: jsw.writeln("//-----------/");
605: jsw.writeln();
606: }
607:
608: for (int i = 0; i < _methods.size(); i++) {
609: JMethod jMethod = (JMethod) _methods.elementAt(i);
610: jMethod.print(jsw);
611: jsw.writeln();
612: }
613: }
614:
615: /**
616: * Writes to the JSourceWriter all inner classes belonging to this class.
617: *
618: * @param jsw The JSourceWriter to be used.
619: */
620: protected final void printInnerClasses(final JSourceWriter jsw) {
621: if ((_innerClasses != null) && (_innerClasses.size() > 0)) {
622: jsw.writeln();
623: jsw.writeln(" //-----------------/");
624: jsw.writeln(" //- Inner Classes -/");
625: jsw.writeln("//-----------------/");
626: jsw.writeln();
627:
628: for (int i = 0; i < _innerClasses.size(); i++) {
629: JClass jClass = (JClass) _innerClasses.elementAt(i);
630: jClass.print(jsw, true);
631: jsw.writeln();
632: }
633: }
634: }
635:
636: //--------------------------------------------------------------------------
637: }
|