View Javadoc
1   /*
2    * Copyright 2006 Ralf Joachim
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.exolab.javasource;
17  
18  import java.util.Enumeration;
19  import java.util.LinkedHashMap;
20  import java.util.Map;
21  import java.util.Vector;
22  
23  import org.exolab.castor.builder.SourceGenerator;
24  
25  /**
26   * A abstract base class for representations of the Java Source code for a Java Class.
27   *
28   * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
29   * @version $Revision: 6668 $ $Date: 2005-05-08 12:32:06 -0600 (Sun, 08 May 2005) $
30   * @since 1.1
31   */
32  public abstract class AbstractJClass extends JStructure {
33  
34      /** The source code for static initialization. */
35      private JSourceCode _staticInitializer;
36  
37      /** The list of member variables (fields) of this JClass. */
38      private Map<String, JField> _fields = new LinkedHashMap<String, JField>();
39  
40      /** The list of member constants of this {@link JClass}. */
41      private Map<String, JConstant> _constants = new LinkedHashMap<String, JConstant>();
42  
43      /** The list of constructors for this JClass. */
44      private Vector<JConstructor> _constructors;
45  
46      /** The list of methods of this JClass. */
47      private Vector<JMethod> _methods;
48  
49      /** A collection of inner classes for this JClass. */
50      private Vector<JClass> _innerClasses;
51  
52      private Vector<String> _sourceCodeEntries = new Vector<String>();
53  
54      /**
55       * Returns a collection of (complete) source code fragments.
56       * @return A collection of (complete) source code fragments.
57       */
58      public String[] getSourceCodeEntries() {
59          return _sourceCodeEntries.toArray(new String[_sourceCodeEntries.size()]);
60      }
61  
62      /**
63       * Creates a new AbstractJClass with the given name.
64       *
65       * @param name The name of the AbstractJClass to create.
66       */
67      protected AbstractJClass(final String name) {
68         this(name, false);
69      }
70  
71      /**
72       * Creates a new AbstractJClass with the given name.
73       *
74       * @param name The name of the AbstractJClass to create.
75       */
76      protected AbstractJClass(final String name, boolean useOldFieldNaming) {
77          super(name);
78          
79          _staticInitializer = new JSourceCode();
80          _constructors = new Vector<JConstructor>();
81          _methods = new Vector<JMethod>();
82          _innerClasses = null;
83          
84          if (useOldFieldNaming) {
85             //-- initialize default Java doc
86             getJDocComment().appendComment("Class " + getLocalName() + ".");
87          }
88      }
89  
90      //--------------------------------------------------------------------------
91  
92      /**
93       * Returns the JSourceCode for the static initializer of this JClass.
94       *
95       * @return The JSourceCode for the static initializer of this JClass.
96       */
97      public final JSourceCode getStaticInitializationCode() {
98          return _staticInitializer;
99      }
100 
101     /**
102      * {@inheritDoc}
103      */
104     public final JField getField(final String name) {
105         return _fields.get(name);
106     }
107 
108     /**
109      * {@inheritDoc}
110      */
111     public final JConstant getConstant(final String name) {
112         return _constants.get(name);
113     }
114 
115     /**
116      * {@inheritDoc}
117      */
118     public final JField[] getFields() {
119         return _fields.values().toArray(new JField[_fields.size()]);
120     }
121 
122     /**
123      * {@inheritDoc}
124      */
125     public final JConstant[] getConstants() {
126         return _constants.values().toArray(new JConstant[_constants.size()]);
127     }
128 
129     /**
130      * Returns the amount of fields.
131      * @return The amount of fields.
132      */
133     public final int getFieldCount() {
134         return _fields.size();
135     }
136 
137     /**
138      * Returns the amount of constants.
139      * @return The amount of constants.
140      */
141     public final int getConstantCount() {
142         return _constants.size();
143     }
144 
145     /**
146      * {@inheritDoc}
147      */
148     public final void addField(final JField jField) {
149         if (jField == null) {
150             throw new IllegalArgumentException("Class members cannot be null");
151         }
152 
153         String name = jField.getName();
154 
155         if (_fields.get(name) != null) {
156             String nameToCompare = (name.startsWith("_")) ? name.substring(1) : name;
157             nameToCompare = nameToCompare.substring(0, 1).toUpperCase()
158                             + nameToCompare.substring(1);
159             if (JNaming.isReservedByCastor(nameToCompare)) {
160                 String warn = "'" + nameToCompare + "' might conflict with a field name used"
161                         + " by Castor.  If you get a complaint\nabout a duplicate name, you will"
162                         + " need to use a mapping file or change the name\nof the conflicting"
163                         + " schema element.";
164                 System.out.println(warn);
165             }
166 
167             String err = "Duplicate name found as a class member: " + name;
168             throw new IllegalArgumentException(err);
169         }
170         _fields.put(name, jField);
171     }
172 
173     public final void addConstant(final JConstant jConstant) {
174         if (jConstant == null) {
175             throw new IllegalArgumentException("Class constants cannot be null");
176         }
177 
178         String name = jConstant.getName();
179 
180         if (_constants.get(name) != null) {
181             String nameToCompare = (name.startsWith("_")) ? name.substring(1) : name;
182             nameToCompare = nameToCompare.substring(0, 1).toUpperCase()
183                             + nameToCompare.substring(1);
184             if (JNaming.isReservedByCastor(nameToCompare)) {
185                 String warn = "'" + nameToCompare + "' might conflict with a constant name used"
186                         + " by Castor.  If you get a complaint\nabout a duplicate name, you will"
187                         + " need to use a mapping file or change the name\nof the conflicting"
188                         + " schema element.";
189                 System.out.println(warn);
190             }
191 
192             String err = "Duplicate name found as a class member: " + name;
193             throw new IllegalArgumentException(err);
194         }
195         _constants.put(name, jConstant);
196     }
197 
198     /**
199      * Removes the field with the given name from this JClass.
200      *
201      * @param name The name of the field to remove.
202      * @return The JField if it was found and removed.
203      */
204     public final JField removeField(final String name) {
205         if (name == null) { return null; }
206 
207         JField field = _fields.remove(name);
208 
209         //-- clean up imports
210         //-- NOT YET IMPLEMENTED
211         return field;
212     }
213 
214     /**
215      * Removes the constant with the given name from this {@link JClass}.
216      *
217      * @param name The name of the constant to remove.
218      * @return The JConstant if it was found and removed.
219      */
220     public final JConstant removeConstant(final String name) {
221         if (name == null) { return null; }
222 
223         JConstant constant = _constants.remove(name);
224 
225         //-- clean up imports
226         //-- NOT YET IMPLEMENTED
227         return constant;
228     }
229 
230     /**
231      * Removes the given JField from this JClass.
232      *
233      * @param jField The JField to remove.
234      * @return true if the field was found and removed.
235      */
236     public final boolean removeField(final JField jField) {
237         if (jField == null) { return false; }
238 
239         Object field = _fields.get(jField.getName());
240         if (field == jField) {
241             _fields.remove(jField.getName());
242             return true;
243         }
244         //-- clean up imports
245         //-- NOT YET IMPLEMENTED
246         return false;
247     }
248 
249     /**
250      * Removes the given {@link JConstant} from this {@link JClass}.
251      *
252      * @param jConstant The {@link JConstant} to remove.
253      * @return true if the constant was found and removed.
254      */
255     public final boolean removeConstant(final JConstant jConstant) {
256         if (jConstant == null) { return false; }
257 
258         Object constant = _constants.get(jConstant.getName());
259         if (constant == jConstant) {
260             _constants.remove(jConstant.getName());
261             return true;
262         }
263         //-- clean up imports
264         //-- NOT YET IMPLEMENTED
265         return false;
266     }
267 
268     /**
269      * Creates a new JConstructor and adds it to this JClass.
270      *
271      * @return The newly created constructor.
272      */
273     public final JConstructor createConstructor() {
274         return createConstructor(null);
275     }
276 
277     /**
278      * Creates a new JConstructor and adds it to this JClass.
279      *
280      * @param params A list of parameters for this constructor.
281      * @return The newly created constructor.
282      */
283     public final JConstructor createConstructor(final JParameter[] params) {
284         JConstructor cons = new JConstructor(this);
285         if (params != null) {
286             for (int i = 0; i < params.length; i++) {
287                 cons.addParameter(params[i]);
288             }
289         }
290         addConstructor(cons);
291         return cons;
292     }
293 
294     /**
295      * Returns the constructor at the specified index.
296      *
297      * @param index The index of the constructor to return.
298      * @return The JConstructor at the specified index.
299      */
300     public final JConstructor getConstructor(final int index) {
301         return _constructors.elementAt(index);
302     }
303 
304     /**
305      * Returns the an array of the JConstructors contained within this JClass.
306      *
307      * @return An array of JConstructor.
308      */
309     public final JConstructor[] getConstructors() {
310         int size = _constructors.size();
311         JConstructor[] jcArray = new JConstructor[size];
312 
313         for (int i = 0; i < _constructors.size(); i++) {
314             jcArray[i] = _constructors.elementAt(i);
315         }
316         return jcArray;
317     }
318     
319     public final int getContructorsCount() {
320     	return _constructors.size();
321     }
322     
323     /**
324      * Adds the given Constructor to this classes list of constructors. The
325      * constructor must have been created with this JClass' createConstructor.
326      *
327      * @param constructor The constructor to add.
328      */
329     public void addConstructor(final JConstructor constructor) {
330         if (constructor == null) {
331             throw new IllegalArgumentException("Constructors cannot be null");
332         }
333 
334         if (constructor.getDeclaringClass() == this) {
335 
336             /** check signatures (add later) **/
337             if (!_constructors.contains(constructor)) {
338                 _constructors.addElement(constructor);
339             }
340         } else {
341             String err = "The given JConstructor was not created by this JClass";
342             throw new IllegalArgumentException(err);
343         }
344     }
345 
346     /**
347      * Removes the given constructor from this JClass.
348      *
349      * @param constructor The JConstructor to remove.
350      * @return true if the constructor was removed, otherwise false.
351      */
352     public final boolean removeConstructor(final JConstructor constructor) {
353         return _constructors.removeElement(constructor);
354     }
355 
356     /**
357      * Returns an array of all the JMethods of this JClass.
358      *
359      * @return An array of all the JMethods of this JClass.
360      */
361     public final JMethod[] getMethods() {
362         int size = _methods.size();
363         JMethod[] marray = new JMethod[size];
364 
365         for (int i = 0; i < _methods.size(); i++) {
366             marray[i] = _methods.elementAt(i);
367         }
368         return marray;
369     }
370 
371     /**
372      * Returns the first occurance of the method with the given name, starting
373      * from the specified index.
374      *
375      * @param name The name of the method to look for.
376      * @param startIndex The starting index to begin the search.
377      * @return The method if found, otherwise null.
378      */
379     public final JMethod getMethod(final String name, final int startIndex) {
380         for (int i = startIndex; i < _methods.size(); i++) {
381             JMethod jMethod = _methods.elementAt(i);
382             if (jMethod.getName().equals(name)) { return jMethod; }
383         }
384         return null;
385     }
386 
387     /**
388      * Returns the JMethod located at the specified index.
389      *
390      * @param index The index of the JMethod to return.
391      * @return The JMethod.
392      */
393     public final JMethod getMethod(final int index) {
394         return _methods.elementAt(index);
395     }
396     
397     public final int getMethodCount() {
398     	return _methods.size(); 
399     }
400 
401     /**
402      * Adds the given JMethod to this JClass.
403      *
404      * @param jMethod The JMethod to add.
405      * @param importReturnType true if we add the importReturnType to the class
406      *        import lists. It could be useful to set it to false when all
407      *        types are fully qualified.
408      */
409     public final void addMethod(final JMethod jMethod, final boolean importReturnType) {
410         if (jMethod == null) {
411             throw new IllegalArgumentException("Class methods cannot be null");
412         }
413 
414         //-- check method name and signatures *add later*
415 
416         //-- keep method list sorted for esthetics when printing
417         //-- START SORT :-)
418         boolean added = false;
419         JModifiers modifiers = jMethod.getModifiers();
420 
421         if (modifiers.isAbstract()) {
422             getModifiers().setAbstract(true);
423         }
424 
425         for (int i = 0; i < _methods.size(); i++) {
426             JMethod tmp = _methods.elementAt(i);
427             //-- first compare modifiers
428             if (tmp.getModifiers().isPrivate()) {
429                 if (!modifiers.isPrivate()) {
430                     _methods.insertElementAt(jMethod, i);
431                     added = true;
432                     break;
433                 }
434             }
435             //-- compare names
436             if (jMethod.getName().compareTo(tmp.getName()) < 0) {
437                     _methods.insertElementAt(jMethod, i);
438                     added = true;
439                     break;
440             }
441         }
442         //-- END SORT
443         if (!added) { _methods.addElement(jMethod); }
444 
445     }
446 
447     /**
448      * Adds the given JMethod to this JClass.
449      *
450      * @param jMethod The JMethod to add.
451      */
452     public final void addMethod(final JMethod jMethod) {
453          addMethod(jMethod, true);
454     }
455 
456     /**
457      * Adds the given array of JMethods to this JClass.
458      *
459      * @param jMethods The JMethod[] to add.
460      */
461     public final void addMethods(final JMethod[] jMethods) {
462         for (int i = 0; i < jMethods.length; i++) { addMethod(jMethods[i]); }
463     }
464 
465     /**
466      * Removes the given method from this JClass.
467      *
468      * @param method The JMethod to remove.
469      * @return true if the method was removed, otherwise false.
470      */
471     public final boolean removeMethod(final JMethod method) {
472         return _methods.removeElement(method);
473     }
474 
475     /**
476      * Creates and returns an inner-class for this JClass.
477      *
478      * @param localname The name of the class (no package name).
479      * @return the new JClass.
480      */
481     public final JClass createInnerClass(final String localname) {
482         if (localname == null) {
483             String err = "argument 'localname' must not be null.";
484             throw new IllegalArgumentException(err);
485         }
486         if (localname.indexOf('.') >= 0) {
487             String err = "The name of an inner-class must not contain a package name.";
488             throw new IllegalArgumentException(err);
489         }
490         String classname = getPackageName();
491         if (classname != null) {
492             classname = classname + "." + localname;
493         } else {
494             classname = localname;
495         }
496 
497         JClass innerClass = new JInnerClass(classname);
498         if (_innerClasses == null) {
499             _innerClasses = new Vector<JClass>();
500         }
501         _innerClasses.addElement(innerClass);
502         return innerClass;
503 
504     }
505 
506     /**
507      * Returns an array of JClass (the inner classes) contained within this
508      * JClass.
509      *
510      * @return An array of JClass contained within this JClass.
511      */
512     public final JClass[] getInnerClasses() {
513         if (_innerClasses != null) {
514             int size = _innerClasses.size();
515             JClass[] carray = new JClass[size];
516             _innerClasses.copyInto(carray);
517             return carray;
518         }
519         return new JClass[0];
520     }
521     
522     public final int getInnerClassCount() {
523     	if (_innerClasses != null) {
524     		return _innerClasses.size();
525     	} 
526     	return 0;
527     	
528     		
529     }
530 
531     /**
532      * Removes the given inner-class (JClass) from this JClass.
533      *
534      * @param jClass The JClass (inner-class) to remove.
535      * @return true if the JClass was removed, otherwise false.
536      */
537     public final boolean removeInnerClass(final JClass jClass) {
538         if (_innerClasses != null) {
539             return _innerClasses.removeElement(jClass);
540         }
541         return false;
542     }
543 
544     //--------------------------------------------------------------------------
545 
546     /**
547      * {@inheritDoc}
548      * @deprecated Please use the Velocity-template based approach instead.
549      * @see SourceGenerator#setJClassPrinterType(String) 
550      */
551     public final void print(final JSourceWriter jsw) {
552         print(jsw, false);
553     }
554 
555     /**
556      * Prints the source code for this JClass to the given JSourceWriter.
557      *
558      * @param classOnly If true, the file header, package declaration, and
559      *        imports are not printed.
560      * @param jsw The JSourceWriter to print to. Must not be null.
561      * 
562      * @deprecated Please use the Velocity-template based approach instead.
563      * @see SourceGenerator#setJClassPrinterType(String) 
564      */
565     public abstract void print(final JSourceWriter jsw, final boolean classOnly);
566 
567     /**
568      * Writes to the JSourceWriter the headers for this class file.  Headers
569      * include the comment-header, the package declaration, and the imports.
570      * 
571      * @param jsw The JSourceWriter to be used.
572      */
573     protected final void printClassHeaders(final JSourceWriter jsw) {
574         printHeader(jsw);
575         printPackageDeclaration(jsw);
576 
577         //-- get imports from inner-classes
578         Vector<String> removeImports = null;
579         if ((_innerClasses != null) && (_innerClasses.size() > 0)) {
580             removeImports = new Vector<String>();
581             for (int i = 0; i < _innerClasses.size(); i++) {
582                 JClass iClass = _innerClasses.elementAt(i);
583                 Enumeration<String> enumeration = iClass.getImports();
584                 while (enumeration.hasMoreElements()) {
585                     String classname = enumeration.nextElement();
586 
587                     int paramTypeIndex = classname.indexOf("<Object>");
588                     if (paramTypeIndex != -1) {
589                         classname = classname.substring(0, paramTypeIndex - 1);
590                     }
591                     if (!hasImport(classname)) {
592                         addImport(classname);
593                         removeImports.addElement(classname);
594                     }
595                 }
596             }
597         }
598 
599         printImportDeclarations(jsw);
600 
601         //-- remove imports from inner-classes, if necessary
602         if (removeImports != null) {
603             for (int i = 0; i < removeImports.size(); i++) {
604                 removeImport(removeImports.elementAt(i));
605             }
606         }
607     }
608 
609     /**
610      * Writes to the {@link JSourceWriter} the constant definitions of this class.
611      * 
612      * @param jsw The JSourceWriter to be used.
613      */
614     protected final void printConstantDefinitions(final JSourceWriter jsw) {
615         for (JConstant constant : _constants.values()) {
616             printAbstractJField(jsw, constant);
617         }
618     }
619 
620     /**
621      * Prints an {@link AbstractJField} instance to the given {@link JSourceWriter}.
622      * 
623      * @param jsw The {@link JSourceWriter} to print to.
624      * @param field The field to print.
625      */
626     private void printAbstractJField(final JSourceWriter jsw, final AbstractJField field) {
627         //-- print Java comment
628         JDocComment comment = field.getComment();
629         if (comment != null && comment.getLength() > 0) { 
630            comment.print(jsw); 
631         }
632 
633         //-- print Annotations
634         field.printAnnotations(jsw);
635 
636         // -- print member
637         jsw.write(field.getModifiers().toString());
638         jsw.write(' ');
639 
640         JType type = field.getType();
641         String typeName = type.toString();
642         //-- for esthetics use short name in some cases
643         if (typeName.equals(toString())) {
644             typeName = type.getLocalName();
645         }
646         jsw.write(typeName);
647         jsw.write(' ');
648         jsw.write(field.getName());
649 
650         String init = field.getInitString();
651         if (init != null && !field.isDateTime()) {
652             jsw.write(" = ");
653             jsw.write(init);
654         }
655 
656         jsw.writeln(';');
657         jsw.writeln();
658     }
659     
660     /**
661      * Writes to the JSourceWriter the member variables of this class.
662      * 
663      * @param jsw The JSourceWriter to be used.
664      */
665     protected final void printMemberVariables(final JSourceWriter jsw) {
666         for (JField field : _fields.values()) {
667             printAbstractJField(jsw, field);
668         }
669     }
670 
671     /**
672      * Writes to the JSourceWriter any static initialization used by this class.
673      * 
674      * @param jsw The JSourceWriter to be used.
675      */
676     protected final void printStaticInitializers(final JSourceWriter jsw) {
677         if (!_staticInitializer.isEmpty()) {
678             jsw.writeln();
679             jsw.writeln("static {");
680             jsw.writeln(_staticInitializer.toString());
681             jsw.writeln("}");
682             jsw.writeln();
683         }
684     }
685 
686     /**
687      * Writes to the JSourceWriter all constructors for this class.
688      * 
689      * @param jsw The JSourceWriter to be used.
690      */
691     protected final void printConstructors(final JSourceWriter jsw) {
692         for (int i = 0; i < _constructors.size(); i++) {
693             JConstructor jConstructor = _constructors.elementAt(i);
694             jConstructor.print(jsw);
695             jsw.writeln();
696         }
697     }
698 
699     /**
700      * Writes to the JSourceWriter all methods belonging to this class.
701      * 
702      * @param jsw The JSourceWriter to be used.
703      */
704     protected final void printMethods(final JSourceWriter jsw) {
705         for (int i = 0; i < _methods.size(); i++) {
706             JMethod jMethod = _methods.elementAt(i);
707             jMethod.print(jsw);
708             jsw.writeln();
709         }
710     }
711     
712     protected final void printSourceCodeFragments(final JSourceWriter sourceWriter) {
713         for (String sourceCode : _sourceCodeEntries) {
714             sourceWriter.writeln(sourceCode);
715             sourceWriter.writeln();
716         }
717         
718     }
719 
720     /**
721      * Writes to the JSourceWriter all inner classes belonging to this class.
722      * 
723      * @param jsw The JSourceWriter to be used.
724      */
725     protected final void printInnerClasses(final JSourceWriter jsw) {
726         if ((_innerClasses != null) && (_innerClasses.size() > 0)) {
727             for (int i = 0; i < _innerClasses.size(); i++) {
728                 JClass jClass = _innerClasses.elementAt(i);
729                 jClass.print(jsw, true);
730                 jsw.writeln();
731             }
732         }
733     }
734 
735     /**
736      * Adds a complete source code fragment (method) to this class.
737      * @param sourceCode The complete source code fragment to be added.
738      */
739     public void addSourceCode(final String sourceCode) {
740         _sourceCodeEntries.add(sourceCode);
741     }
742 
743     public final int getSourceCodeEntryCount() {
744         return _sourceCodeEntries.size(); 
745     }
746     
747 }