1 /**
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Intalio, Inc. For written permission,
18 * please contact info@codehaus.org.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Intalio, Inc. Exolab is a registered
23 * trademark of Intalio, Inc.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.codehaus.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 1999-2002 (C) Intalio, Inc. All Rights Reserved.
42 *
43 * $Id$
44 *
45 * Contributors:
46 * --------------
47 * Keith Visco (kvisco@intalio.com) - Original Author
48 * Martin Skopp (skopp@riege.de) - Moved some core code into JStructure
49 * and revised to extend JStructure
50 *
51 */
52 package org.codehaus.modello.plugin.java.javasource;
53
54 /*
55 * Copyright (c) 2004, Codehaus.org
56 *
57 * Permission is hereby granted, free of charge, to any person obtaining a copy of
58 * this software and associated documentation files (the "Software"), to deal in
59 * the Software without restriction, including without limitation the rights to
60 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
61 * of the Software, and to permit persons to whom the Software is furnished to do
62 * so, subject to the following conditions:
63 *
64 * The above copyright notice and this permission notice shall be included in all
65 * copies or substantial portions of the Software.
66 *
67 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
73 * SOFTWARE.
74 */
75
76 import java.util.ArrayList;
77 import java.util.Enumeration;
78 import java.util.LinkedHashMap;
79 import java.util.List;
80 import java.util.Map;
81
82 /**
83 * A representation of the Java Source code for a Java Class. This is
84 * a useful utility when creating in memory source code.
85 * This package was modelled after the Java Reflection API
86 * as much as possible to reduce the learning curve.
87 *
88 * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
89 * @author <a href="mailto:skopp@riege.de">Martin Skopp</a>
90 * @version $Revision$ $Date$
91 */
92 public class JClass extends JStructure {
93
94 /**
95 * The list of constructors for this JClass
96 */
97 private List<JConstructor> _constructors = null;
98
99 /**
100 * The list of member variables (fields) of this JClass
101 */
102 private Map<String, JField> _fields = null;
103
104 private List<JClass> _innerClasses = null;
105
106 /**
107 * The list of methods of this JClass
108 */
109 private List<JMethod> _methods = null;
110
111 /**
112 * The superclass for this JClass
113 */
114 private String _superClass = null;
115
116 /**
117 * The source code for static initialization
118 **/
119 private JSourceCode _staticInitializer = new JSourceCode();
120
121 /**
122 * Creates a new JClass with the given name
123 * @param name the name of the JClass to create
124 * @exception java.lang.IllegalArgumentException when the given name
125 * is not a valid Class name
126 **/
127 public JClass(String name) throws IllegalArgumentException {
128 super(name);
129 _constructors = new ArrayList<JConstructor>();
130 _fields = new LinkedHashMap<>();
131 _methods = new ArrayList<JMethod>();
132 _innerClasses = new ArrayList<JClass>();
133 // -- initialize default Java doc
134 getJDocComment().appendComment("Class " + getLocalName() + ".");
135 } // -- JClass
136
137 /**
138 * Adds the given Constructor to this classes list of constructors.
139 * The constructor must have been created with this JClass'
140 * createConstructor.
141 *
142 * @param constructor a constructor
143 * @throws java.lang.IllegalArgumentException if {@code constructor} is {@code null} or invalid.
144 */
145 public void addConstructor(JConstructor constructor) throws IllegalArgumentException {
146 if (constructor == null) throw new IllegalArgumentException("Constructors cannot be null");
147
148 if (constructor.getDeclaringClass() == this) {
149
150 /** check signatures (add later) **/
151 if (!_constructors.contains(constructor)) {
152 _constructors.add(constructor);
153 }
154 } else {
155 String err = "The given JConstructor was not created ";
156 err += "by this JClass";
157 throw new IllegalArgumentException(err);
158 }
159 }
160
161 /**
162 * Adds the given JField to this JClass
163 *
164 * @param jField, the JField to add
165 * @exception java.lang.IllegalArgumentException when the given
166 * JField has a name of an existing JField
167 **/
168 public void addField(JField jField) throws IllegalArgumentException {
169 if (jField == null) {
170 throw new IllegalArgumentException("Class members cannot be null");
171 }
172
173 String name = jField.getName();
174
175 if (_fields.get(name) != null) {
176 String err = "duplicate name found: " + name;
177 throw new IllegalArgumentException(err);
178 }
179 _fields.put(name, jField);
180 } // -- addField
181
182 /**
183 * Adds the given JMember to this JClass
184 *
185 * @param jMember, the JMember to add
186 * @exception java.lang.IllegalArgumentException when the given
187 * JMember has the same name of an existing JField
188 * or JMethod respectively, or if the JMember is of an
189 * unrecognized class.
190 **/
191 public void addMember(JMember jMember) throws IllegalArgumentException {
192 if (jMember instanceof JField) addField((JField) jMember);
193 else if (jMember instanceof JMethod) addMethod((JMethod) jMember);
194 else {
195 String error = null;
196 if (jMember == null) {
197 error = "the argument 'jMember' must not be null.";
198 } else {
199 error = "Cannot add JMember '" + jMember.getClass().getName() + "' to JClass, unrecognized type.";
200 }
201 throw new IllegalArgumentException(error);
202 }
203 } // -- addMember
204
205 /**
206 * Adds the given JMethod to this JClass
207 *
208 * @param jMethod the JMethod to add
209 **/
210 public void addMethod(JMethod jMethod) {
211 addMethod(jMethod, true);
212 }
213
214 /**
215 * Adds the given JMethod to this JClass
216 *
217 * @param jMethod, the JMethod to add
218 * @param importReturnType true if we add the importReturnType to
219 * the class import lists. It could be useful to set it to false when
220 * all types are fully qualified.
221 * @throws java.lang.IllegalArgumentException when the given
222 * JMethod has the same name of an existing JMethod.
223 **/
224 public void addMethod(JMethod jMethod, boolean importReturnType) throws IllegalArgumentException {
225 if (jMethod == null) {
226 throw new IllegalArgumentException("Class methods cannot be null");
227 }
228
229 // -- check method name and signatures *add later*
230
231 // -- keep method list sorted for esthetics when printing
232 // -- START SORT :-)
233 boolean added = false;
234 // short modifierVal = 0;
235 JModifiers modifiers = jMethod.getModifiers();
236
237 if (modifiers.isAbstract()) {
238 getModifiers().setAbstract(true);
239 }
240
241 for (int i = 0; i < _methods.size(); i++) {
242 JMethod tmp = (JMethod) _methods.get(i);
243 // -- first compare modifiers
244 if (tmp.getModifiers().isPrivate()) {
245 if (!modifiers.isPrivate()) {
246 _methods.add(i, jMethod);
247 added = true;
248 break;
249 }
250 }
251 // -- compare names
252 if (jMethod.getName().compareTo(tmp.getName()) < 0) {
253 _methods.add(i, jMethod);
254 added = true;
255 break;
256 }
257 }
258 // -- END SORT
259 if (!added) _methods.add(jMethod);
260 } // -- addMethod
261
262 /**
263 * Adds the given array of JMethods to this JClass
264 *
265 * @param jMethods, the JMethod[] to add
266 * @exception java.lang.IllegalArgumentException when any of the given
267 * JMethods has the same name of an existing JMethod.
268 **/
269 public void addMethods(JMethod[] jMethods) throws IllegalArgumentException {
270 for (JMethod jMethod : jMethods) {
271 addMethod(jMethod);
272 }
273 } // -- addMethods
274
275 /**
276 * Creates a new JConstructor and adds it to this
277 * JClass.
278 *
279 * @return the newly created constructor
280 */
281 public JConstructor createConstructor() {
282 return createConstructor(null);
283 } // -- createConstructor
284
285 /**
286 * Creates a new JConstructor and adds it to this
287 * JClass.
288 *
289 * @param params the parameters
290 * @return the newly created constructor
291 */
292 public JConstructor createConstructor(JParameter[] params) {
293 JConstructor cons = new JConstructor(this);
294 if (params != null) {
295 for (JParameter param : params) {
296 cons.addParameter(param);
297 }
298 }
299 addConstructor(cons);
300 return cons;
301 } // -- createConstructor
302
303 /**
304 * Creates and returns an inner-class for this JClass
305 *
306 * @param localname the name of the class (no package name)
307 * @return the new JClass
308 */
309 public JClass createInnerClass(String localname) {
310 if (localname == null) {
311 String err = "argument 'localname' must not be null.";
312 throw new IllegalArgumentException(err);
313 }
314 if (localname.indexOf('.') >= 0) {
315 String err = "The name of an inner-class must not contain a package name.";
316 throw new IllegalArgumentException(err);
317 }
318 String classname = getPackageName();
319 if (classname != null) {
320 classname = classname + "." + localname;
321 } else {
322 classname = localname;
323 }
324
325 JClass innerClass = new JInnerClass(classname);
326 _innerClasses.add(innerClass);
327 return innerClass;
328 } // -- createInnerClass
329
330 /**
331 * Returns the constructor at the specified index.
332 *
333 * @param index the index of the constructor to return
334 * @return the JConstructor at the specified index.
335 */
336 public JConstructor getConstructor(int index) {
337 return (JConstructor) _constructors.get(index);
338 } // -- getConstructor
339
340 /**
341 * Returns the an array of the JConstructors contained within this JClass
342 *
343 * @return an array of JConstructor
344 */
345 public JConstructor[] getConstructors() {
346
347 int size = _constructors.size();
348 JConstructor[] jcArray = new JConstructor[size];
349
350 for (int i = 0; i < _constructors.size(); i++) {
351 jcArray[i] = _constructors.get(i);
352 }
353 return jcArray;
354 } // -- getConstructors
355
356 /**
357 * Returns the member with the given name, or null if no member
358 * was found with the given name
359 * @param name the name of the member to return
360 * @return the member with the given name, or null if no member
361 * was found with the given name
362 **/
363 public JField getField(String name) {
364 return (JField) _fields.get(name);
365 } // -- getField
366
367 /**
368 * Returns an array of all the JFields of this JClass
369 * @return an array of all the JFields of this JClass
370 **/
371 public JField[] getFields() {
372 return _fields.values().toArray(new JField[0]);
373 } // -- getFields
374
375 /**
376 * Returns an array of JClass (the inner classes)
377 * contained within this JClass.
378 *
379 * @return an array of JClass contained within this JClass
380 */
381 public JClass[] getInnerClasses() {
382 return _innerClasses.toArray(new JClass[0]);
383 } // -- getInnerClasses;
384
385 /**
386 * Returns an array of all the JMethods of this JClass
387 *
388 * @return an array of all the JMethods of this JClass
389 */
390 public JMethod[] getMethods() {
391 int size = _methods.size();
392 JMethod[] marray = new JMethod[size];
393
394 for (int i = 0; i < _methods.size(); i++) {
395 marray[i] = _methods.get(i);
396 }
397 return marray;
398 } // -- getMethods
399
400 /**
401 * Returns the first occurrence of the method with the
402 * given name, starting from the specified index.
403 *
404 * @param name the name of the method to look for
405 * @param startIndex the starting index to begin the search
406 * @return the method if found, otherwise null.
407 */
408 public JMethod getMethod(String name, int startIndex) {
409 for (int i = startIndex; i < _methods.size(); i++) {
410 JMethod jMethod = _methods.get(i);
411 if (jMethod.getName().equals(name)) return jMethod;
412 }
413 return null;
414 } // -- getMethod
415
416 /**
417 * Returns the JMethod located at the specified index
418 *
419 * @param index the index of the JMethod to return.
420 * @return the JMethod
421 */
422 public JMethod getMethod(int index) {
423 return _methods.get(index);
424 } // -- getMethod
425
426 /**
427 * Returns the JSourceCode for the static initializer
428 * of this JClass
429 *
430 * @return the JSourceCode for the static initializer
431 * of this JClass
432 */
433 public JSourceCode getStaticInitializationCode() {
434 return _staticInitializer;
435 } // -- getStaticInitializationCode
436
437 /**
438 * Gets the super Class that this class extends
439 * @return superClass the super Class that this Class extends
440 */
441 public String getSuperClass() {
442 return _superClass;
443 } // -- getSuperClass
444
445 /**
446 * Prints the source code for this JClass to the given JSourceWriter
447 *
448 * @param jsw the JSourceWriter to print to. [May not be null]
449 */
450 public void print(JSourceWriter jsw) {
451 print(jsw, false);
452 } // -- print
453
454 /**
455 * Prints the source code for this JClass to the given JSourceWriter
456 *
457 * @param jsw the JSourceWriter to print to. [May not be null]
458 * @param classOnly whether the header, package and imports should be printed too
459 */
460 public void print(JSourceWriter jsw, boolean classOnly) {
461
462 if (jsw == null) {
463 throw new IllegalArgumentException("argument 'jsw' should not be null.");
464 }
465
466 StringBuilder buffer = new StringBuilder();
467
468 if (!classOnly) {
469 printHeader(jsw);
470 printPackageDeclaration(jsw);
471
472 // -- get imports from inner-classes
473 List<String> removeImports = null;
474 if (_innerClasses.size() > 0) {
475 removeImports = new ArrayList<String>();
476 for (JClass iClass : _innerClasses) {
477 Enumeration<String> e = iClass.getImports();
478 while (e.hasMoreElements()) {
479 String classname = e.nextElement();
480 if (!hasImport(classname)) {
481 addImport(classname);
482 removeImports.add(classname);
483 }
484 }
485 }
486 }
487 printImportDeclarations(jsw);
488
489 // -- remove imports from inner-classes, if necessary
490 if (removeImports != null) {
491 for (int i = 0; i < removeImports.size(); i++) {
492 removeImport(removeImports.get(i));
493 }
494 }
495 }
496
497 // ------------/
498 // - Java Doc -/
499 // ------------/
500
501 getJDocComment().print(jsw);
502
503 JAnnotations annotations = getAnnotations();
504 if (annotations != null) annotations.print(jsw);
505
506 // -- print class information
507 // -- we need to add some JavaDoc API adding comments
508
509 buffer.setLength(0);
510
511 JModifiers modifiers = getModifiers();
512 if (modifiers.isPrivate()) {
513 buffer.append("private ");
514 } else if (modifiers.isPublic()) {
515 buffer.append("public ");
516 }
517
518 if (modifiers.isAbstract()) {
519 buffer.append("abstract ");
520 }
521
522 if (this instanceof JInnerClass && modifiers.isStatic()) {
523 buffer.append("static ");
524 }
525
526 if (modifiers.isFinal()) {
527 buffer.append("final ");
528 }
529
530 buffer.append("class ");
531 buffer.append(getLocalName());
532 jsw.writeln(buffer.toString());
533 buffer.setLength(0);
534 jsw.indent();
535
536 if (_superClass != null) {
537 buffer.append("extends ");
538 buffer.append(_superClass);
539 jsw.writeln(buffer.toString());
540 buffer.setLength(0);
541 }
542
543 if (getInterfaceCount() > 0) {
544 buffer.append("implements ");
545
546 Enumeration<String> e = getInterfaces();
547 while (e.hasMoreElements()) {
548 buffer.append(e.nextElement());
549 if (e.hasMoreElements()) buffer.append(", ");
550 }
551
552 jsw.writeln(buffer.toString());
553 buffer.setLength(0);
554 }
555
556 jsw.unindent();
557
558 jsw.writeln('{');
559
560 jsw.indent();
561
562 // -- declare members
563
564 if (_fields.size() > 0) {
565 jsw.writeln();
566 jsw.writeln(" //--------------------------/");
567 jsw.writeln(" //- Class/Member Variables -/");
568 jsw.writeln("//--------------------------/");
569 jsw.writeln();
570 }
571
572 for (JField jField : _fields.values()) {
573 // -- print Java comment
574 JDocComment comment = jField.getComment();
575 if (comment != null) comment.print(jsw);
576
577 JAnnotations fieldAnnotations = jField.getAnnotations();
578 if (fieldAnnotations != null) fieldAnnotations.print(jsw);
579
580 // -- print member
581 jsw.write(jField.getModifiers().toString());
582 jsw.write(' ');
583
584 JType type = jField.getType();
585 String typeName = type.toString();
586 // -- for esthetics use short name in some cases
587 if (typeName.equals(toString())) {
588 typeName = type.getLocalName();
589 }
590 jsw.write(typeName);
591 jsw.write(' ');
592 jsw.write(jField.getName());
593
594 String init = jField.getInitString();
595 if (init != null) {
596 jsw.write(" = ");
597 jsw.write(init);
598 }
599
600 jsw.writeln(';');
601 jsw.writeln();
602 }
603
604 // ----------------------/
605 // - Static Initializer -/
606 // ----------------------/
607
608 if (!_staticInitializer.isEmpty()) {
609 jsw.writeln();
610 jsw.writeln("static");
611 jsw.writeln("{");
612 _staticInitializer.print(jsw);
613 jsw.writeln("};");
614 jsw.writeln();
615 }
616
617 // -- print constructors
618 if (_constructors.size() > 0) {
619 jsw.writeln();
620 jsw.writeln(" //----------------/");
621 jsw.writeln(" //- Constructors -/");
622 jsw.writeln("//----------------/");
623 jsw.writeln();
624 }
625 for (int i = 0; i < _constructors.size(); i++) {
626 JConstructor jConstructor = _constructors.get(i);
627 jConstructor.print(jsw);
628 jsw.writeln();
629 }
630
631 // -- print methods
632 if (_methods.size() > 0) {
633 jsw.writeln();
634 jsw.writeln(" //-----------/");
635 jsw.writeln(" //- Methods -/");
636 jsw.writeln("//-----------/");
637 jsw.writeln();
638 }
639
640 for (int i = 0; i < _methods.size(); i++) {
641 JMethod jMethod = _methods.get(i);
642 jMethod.print(jsw);
643 jsw.writeln();
644 }
645
646 // -- print inner-classes
647 if (_innerClasses.size() > 0) {
648 jsw.writeln();
649 jsw.writeln(" //-----------------/");
650 jsw.writeln(" //- Inner Classes -/");
651 jsw.writeln("//-----------------/");
652 jsw.writeln();
653 }
654 for (int i = 0; i < _innerClasses.size(); i++) {
655 JClass jClass = _innerClasses.get(i);
656 jClass.print(jsw, true);
657 jsw.writeln();
658 }
659
660 for (String sourceCodeEntry : sourceCodeEntries) {
661 jsw.writeln(sourceCodeEntry);
662 }
663
664 jsw.unindent();
665
666 jsw.writeln('}');
667 jsw.flush();
668 } // -- printSource
669
670 private List<String> sourceCodeEntries = new ArrayList<String>();
671
672 public void addSourceCode(String sourceCode) {
673 sourceCodeEntries.add(sourceCode);
674 }
675
676 /**
677 * Removes the given constructor from this JClass
678 *
679 * @param constructor the JConstructor to remove
680 * @return true if the constructor was removed, otherwise false.
681 */
682 public boolean removeConstructor(JConstructor constructor) {
683 return _constructors.remove(constructor);
684 } // -- removeConstructor
685
686 /**
687 * Removes the field with the given name from this JClass
688 *
689 * @param name the name of the field to remove
690 * @return the removed field
691 **/
692 public JField removeField(String name) {
693 if (name == null) return null;
694
695 JField field = (JField) _fields.remove(name);
696
697 // -- clean up imports
698 // -- NOT YET IMPLEMENTED
699 return field;
700 } // -- removeField
701
702 /**
703 * Removes the given JField from this JClass
704 *
705 * @param jField, the JField to remove
706 * @return {{@code true} if and only if the field was successfully removed
707 **/
708 public boolean removeField(JField jField) {
709 if (jField == null) return false;
710
711 Object field = _fields.get(jField.getName());
712 if (field == jField) {
713 _fields.remove(jField.getName());
714 return true;
715 }
716 // -- clean up imports
717 // -- NOT YET IMPLEMENTED
718 return false;
719 } // -- removeField
720
721 /**
722 * Removes the given inner-class (JClass) from this JClass.
723 *
724 * @param jClass the JClass (inner-class) to remove.
725 * @return true if the JClass was removed, otherwise false.
726 */
727 public boolean removeInnerClass(JClass jClass) {
728 return _innerClasses.remove(jClass);
729 } // -- removeInnerClass
730
731 /**
732 * Sets the super Class that this class extends
733 * @param superClass the super Class that this Class extends
734 */
735 public void setSuperClass(String superClass) {
736 _superClass = superClass;
737 } // -- setSuperClass
738
739 final class JInnerClass extends JClass {
740 JInnerClass(String name) {
741 super(name);
742 }
743 } // -- JInnerClass
744 } // -- JClass