1 /**
2 * Redistribution and use of this software and associated documentation ("Software"), with or
3 * without modification, are permitted provided that the following conditions are met:
4 *
5 * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
6 * must also contain a copy of this document.
7 *
8 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
9 * conditions and the following disclaimer in the documentation and/or other materials provided with
10 * the distribution.
11 *
12 * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
13 * without prior written permission of Intalio, Inc. For written permission, please contact
14 * info@exolab.org.
15 *
16 * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
17 * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
18 * Intalio, Inc.
19 *
20 * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
21 *
22 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Copyright 2001-2002 (C) Intalio, Inc. All Rights Reserved.
32 */
33 package org.exolab.javasource;
34
35 import java.io.File;
36 import java.io.FileWriter;
37 import java.io.IOException;
38 import java.util.Enumeration;
39 import java.util.Vector;
40
41 /**
42 * This class represents the basic Java "structure" for a Java source file. This is the base class
43 * for JClass and JInterface. <br/>
44 * This is a useful utility when creating in memory source code. The code in this package was
45 * modelled after the Java Reflection API as much as possible to reduce the learning curve.
46 *
47 * @author <a href="mailto:skopp AT riege DOT de">Martin Skopp</a>
48 * @author <a href="mailto:keith AT kvisco DOT com">Keith Visco</a>
49 * @version $Revision$ $Date: 2005-12-13 14:58:48 -0700 (Tue, 13 Dec 2005) $
50 */
51 public abstract class JStructure extends JType implements JAnnotatedElement {
52
53 /**
54 * The Id for Source control systems. <br/>
55 * Note: I needed to break the String into parts to prevent CVS from expanding it here!
56 */
57 private static final String DEFAULT_HEADER = "$" + "Id$";
58
59 /**
60 * The source control version for listed in the JavaDoc <br/>
61 * Note: I needed to break the String into parts to prevent CVS from expanding it here!
62 */
63 private static final String DEFAULT_VERSION = "$" + "Revision$ $" + "Date$";
64
65 /** A standard complaint for a bad parameter. */
66 private static final String JSW_SHOULD_NOT_BE_NULL = "argument 'jsw' should not be null.";
67
68 /** The source header. */
69 private JComment _header = null;
70
71 /** The package to which this JStructure belongs. */
72 private final String _packageName;
73
74 /** List of imported classes and packages. */
75 private final Vector<String> _imports = new Vector<>();
76
77 /** The Javadoc for this JStructure. */
78 private final JDocComment _jdc =
79 new JDocComment(JDocDescriptor.createVersionDesc(DEFAULT_VERSION));
80
81 /** Implementation of JAnnoatedElement to delagate to. */
82 private final JAnnotatedElementHelper _annotatedElement = new JAnnotatedElementHelper();
83
84 /**
85 * The JModifiers for this JStructure, which allows us to change the resulting qualifiers.
86 */
87 private final JModifiers _modifiers = new JModifiers();
88
89 /** The set of interfaces implemented/extended by this JStructure. */
90 private final Vector<String> _interfaces = new Vector<>();
91
92 /**
93 * Creates a new JStructure with the given name.
94 *
95 * @param name The name of the JStructure.
96 */
97 protected JStructure(final String name) {
98 super(name);
99
100 // -- verify name is a valid java class name
101 if (!isValidClassName(name)) {
102 String lname = getLocalName();
103 String err = "'" + lname + "' is ";
104 if (JNaming.isKeyword(lname)) {
105 err += "a reserved word and may not be used as a class name.";
106 } else {
107 err += "not a valid Java identifier.";
108 }
109 throw new IllegalArgumentException(err);
110 }
111
112 _packageName = JNaming.getPackageFromClassName(name);
113 }
114
115 /**
116 * Test the provided name and return true if it is a valid class name.
117 *
118 * @param classname A class name to test.
119 * @return True if the provided class name is a valid class name.
120 */
121 private boolean isValidClassName(final String classname) {
122 if (classname == null) {
123 return false;
124 }
125
126 String name = classname;
127 int beforeTypeName = name.indexOf("<");
128 if (beforeTypeName > 0) {
129 name = name.substring(0, beforeTypeName);
130 }
131
132 // -- ignore package information, for now
133 name = JNaming.getLocalNameFromClassName(name);
134
135 return JNaming.isValidJavaIdentifier(name);
136 }
137
138 /**
139 * Returns the JComment header to display at the top of the source file for this JStructure, or
140 * null if no header was set.
141 *
142 * @return The JComment header or null if none exists.
143 */
144 public final JComment getHeader() {
145 return _header;
146 }
147
148 /**
149 * Sets the header comment for this JStructure.
150 *
151 * @param comment The comment to display at the top of the source file when printed.
152 */
153 public final void setHeader(final JComment comment) {
154 _header = comment;
155 }
156
157 /**
158 * Returns the name of the package that this JStructure is a member of.
159 *
160 * @return The name of the package that this JStructure is a member of, or null if there is no
161 * current package name defined.
162 */
163 public final String getPackageName() {
164 return _packageName;
165 }
166
167 /**
168 * Returns an Enumeration of imported package and class names for this JStructure.
169 *
170 * @return The Enumeration of imports. May be empty but will not be null.
171 */
172 public final Enumeration<String> getImports() {
173 return _imports.elements();
174 }
175
176 /**
177 * Returns the amount of imports.
178 *
179 * @return The amount of imports.
180 */
181 public final int getImportCount() {
182 return _imports.size();
183 }
184
185 /**
186 * Returns true if the given classname exists in the imports of this JStructure.
187 *
188 * @param classname The class name to check for
189 * @return True if the given classname exists in the imports list.
190 */
191 public final boolean hasImport(final String classname) {
192 return _imports.contains(classname);
193 }
194
195 /**
196 * Adds the given import to this JStructure. Note: You cannot import from the "default package,"
197 * so imports with no package are ignored.
198 *
199 * @param className Name of the class to import.
200 */
201 public abstract void addImport(final String className);
202
203 /**
204 * Adds the given import to this JStructure. Given class name should not be null or empty. <br/>
205 * Note: You cannot import from the "default package," so imports with no package are ignored.
206 *
207 * @param className Name of the class to import.
208 */
209 protected final void addImportInternal(final String className) {
210 // -- getPackageName
211 String pkgName = JNaming.getPackageFromClassName(className);
212
213 if (pkgName != null) {
214 if (pkgName.equals(_packageName) || pkgName.equals("java.lang")) {
215 return;
216 }
217
218 // -- for readabilty keep import list sorted, and make sure
219 // -- we do not include more than one of the same import
220 for (int i = 0; i < _imports.size(); i++) {
221 String imp = _imports.elementAt(i);
222 if (imp.equals(className)) {
223 return;
224 }
225 if (imp.compareTo(className) > 0) {
226 _imports.insertElementAt(className, i);
227 return;
228 }
229 }
230 _imports.add(className);
231 }
232 }
233
234 /**
235 * Adds appropriate import for this JAnnotation.
236 *
237 * @param annotation A JAnnotation for which we want to add an import to this JStructure.
238 */
239 protected final void addImport(final JAnnotation annotation) {
240 addImport(annotation.getAnnotationType().getName());
241 }
242
243 /**
244 * Adds appropriate imports for each JAnnotation in the given Array.
245 *
246 * @param annotations An Array of JAnnotation; we want to add an import to this JStructure for
247 * each JAnnotation in the Array.
248 */
249 protected final void addImport(final JAnnotation[] annotations) {
250 for (JAnnotation annotation : annotations) {
251 addImport(annotation.getAnnotationType().getName());
252 }
253 }
254
255 /**
256 * Remove the import of the given class name from this JStucture, returning true if the import was
257 * found and removed.
258 *
259 * @param className Name of the class to remove the import of.
260 * @return If the import was previously part of this JStructure, false otherwise.
261 */
262 public final boolean removeImport(final String className) {
263 boolean result = false;
264 if (className != null && !className.isEmpty()) {
265 result = _imports.remove(className);
266 }
267 return result;
268 }
269
270 /**
271 * Returns the JavaDoc comment for this JStructure.
272 *
273 * @return The JDocComment for this JStructure.
274 */
275 public final JDocComment getJDocComment() {
276 return _jdc;
277 }
278
279 /**
280 * Returns the object managing the annotations for this JStructure.
281 *
282 * @return The object managing the annotations for this JStructure.
283 */
284 protected final JAnnotatedElementHelper getAnnotatedElementHelper() {
285 return _annotatedElement;
286 }
287
288 /**
289 * {@inheritDoc}
290 */
291 public final boolean hasAnnotations() {
292 return _annotatedElement.hasAnnotations();
293 }
294
295 /**
296 * {@inheritDoc}
297 */
298 public final JAnnotation[] getAnnotations() {
299 return _annotatedElement.getAnnotations();
300 }
301
302 /**
303 * {@inheritDoc}
304 */
305 public final JAnnotation getAnnotation(final JAnnotationType annotationType) {
306 return _annotatedElement.getAnnotation(annotationType);
307 }
308
309 /**
310 * {@inheritDoc}
311 */
312 public final boolean isAnnotationPresent(final JAnnotationType annotationType) {
313 return _annotatedElement.isAnnotationPresent(annotationType);
314 }
315
316 /**
317 * {@inheritDoc}
318 */
319 public final void addAnnotation(final JAnnotation annotation) {
320 _annotatedElement.addAnnotation(annotation);
321 addImport(annotation);
322 }
323
324 /**
325 * {@inheritDoc}
326 */
327 public final JAnnotation removeAnnotation(final JAnnotationType annotationType) {
328 return _annotatedElement.removeAnnotation(annotationType);
329 }
330
331 /**
332 * Returns the JModifiers, which allows the qualifiers to be changed.
333 *
334 * @return The JModifiers for this JStructure.
335 */
336 public final JModifiers getModifiers() {
337 return _modifiers;
338 }
339
340 /**
341 * Returns an Enumeration of interface names that this JStructure inherits from.
342 *
343 * @return The Enumeration of interface names for this JStructure. May be empty but will not be
344 * null.
345 */
346 public final Enumeration<String> getInterfaces() {
347 return _interfaces.elements();
348 }
349
350 /**
351 * Return the count of the number of Interfaces that have been added to this JStructure.
352 *
353 * @return The count of the number of Interfaces that have been added to this JStructure.
354 */
355 public final int getInterfaceCount() {
356 return _interfaces.size();
357 }
358
359 /**
360 * Adds the given interface to the list of interfaces this JStructure inherits method declarations
361 * from, and either implements (JClass) or extends (JInterface).
362 *
363 * @param interfaceName The name of the interface to "inherit" method declarations from.
364 */
365 public final void addInterface(final String interfaceName) {
366 if (!_interfaces.contains(interfaceName)) {
367 _interfaces.add(interfaceName);
368 }
369 }
370
371 /**
372 * Removes the given interface from the list of interfaces this {@link JStructure} has.
373 *
374 * @param interfaceName The name of the interface to be removed.
375 * @return true if {@link JStructure} implemented the interface and it was removed, false
376 * otherwise.
377 */
378 public final boolean removeInterface(final String interfaceName) {
379 return _interfaces.remove(interfaceName);
380 }
381
382 /**
383 * Returns the field with the given name, or null if no field was found with that name.
384 *
385 * @param name The name of the field to return.
386 * @return The field with the given name, or null if no field was found with the given name.
387 */
388 public abstract JField getField(String name);
389
390 /**
391 * Returns an array of all the JFields of this JStructure.
392 *
393 * @return An array of all the JFields of this JStructure.
394 */
395 public abstract JField[] getFields();
396
397 /**
398 * Adds the given JField to this JStructure. <br/>
399 * This method is implemented by subclasses and should only accept the proper fields for the
400 * subclass otherwise an IllegalArgumentException will be thrown. For example a JInterface will
401 * only accept static fields.
402 *
403 * @param jField The JField to add.
404 */
405 public abstract void addField(JField jField);
406
407 /**
408 * Adds the given JMember to this JStructure. <br/>
409 * This method is implemented by subclasses and should only accept the proper types for the
410 * subclass otherwise an IllegalArgumentException will be thrown.
411 *
412 * @param jMember The JMember to add to this JStructure.
413 */
414 public abstract void addMember(JMember jMember);
415
416 /**
417 * Returns the name of the file that this JStructure would be printed to, given a call to
418 * {@link #print(String, String)}.
419 *
420 * @param destDir the destination directory. This may be null.
421 * @return the name of the file that this JInterface would be printed to
422 */
423 public final String getFilename(final String destDir) {
424 String filename = getLocalName() + ".java";
425
426 // -- Convert Java package to path string
427 String javaPackagePath = "";
428 if ((_packageName != null) && (_packageName.length() > 0)) {
429 javaPackagePath = _packageName.replace('.', File.separatorChar);
430 }
431
432 // -- Create fully qualified path (including 'destDir') to file
433 File pathFile;
434 if (destDir == null) {
435 pathFile = new File(javaPackagePath);
436 } else {
437 pathFile = new File(destDir, javaPackagePath);
438 }
439 if (!pathFile.exists()) {
440 pathFile.mkdirs();
441 }
442
443 // -- Prefix filename with path
444 final String pathStr = pathFile.toString();
445 if (!pathStr.isEmpty()) {
446 filename = pathStr + File.separator + filename;
447 }
448
449 return filename;
450 }
451
452 /**
453 * Prints the source code for this JStructure to the destination directory. Subdirectories will be
454 * created if necessary for the package.
455 *
456 * @param destDir Directory name to use as the root directory for all output.
457 * @param lineSeparator The line separator to use at the end of each line. If null, then the
458 * default line separator for the runtime platform will be used.
459 */
460 public final void print(final String destDir, final String lineSeparator) {
461 // -- open output file
462 String filename = getFilename(destDir);
463
464 File file = new File(filename);
465 JSourceWriter jsw = null;
466 try {
467 jsw = new JSourceWriter(new FileWriter(file));
468 } catch (IOException ioe) {
469 System.out.println("unable to create class file: " + filename);
470 return;
471 }
472 if (lineSeparator == null) {
473 jsw.setLineSeparator(System.getProperty("line.separator"));
474 } else {
475 jsw.setLineSeparator(lineSeparator);
476 }
477 print(jsw);
478 jsw.close();
479 }
480
481 /**
482 * Prints the source code for this JStructure to the given JSourceWriter.
483 *
484 * @param jsw The JSourceWriter to print to.
485 * @deprecated Please use the Velocity-template based approach instead.
486 */
487 public abstract void print(JSourceWriter jsw);
488
489 /**
490 * A utility method that prints the header to the given JSourceWriter.
491 *
492 * @param jsw The JSourceWriter to print to.
493 */
494 public final void printHeader(final JSourceWriter jsw) {
495 if (jsw == null) {
496 throw new IllegalArgumentException(JSW_SHOULD_NOT_BE_NULL);
497 }
498
499 // -- write class header
500 if (_header != null) {
501 _header.print(jsw);
502 } else {
503 jsw.writeln("/*");
504 jsw.writeln(" * " + DEFAULT_HEADER);
505 jsw.writeln(" */");
506 }
507 jsw.writeln();
508 jsw.flush();
509 }
510
511 /**
512 * A utility method that prints the packageDeclaration to the given JSourceWriter.
513 *
514 * @param jsw The JSourceWriter to print to.
515 */
516 public final void printPackageDeclaration(final JSourceWriter jsw) {
517 if (jsw == null) {
518 throw new IllegalArgumentException(JSW_SHOULD_NOT_BE_NULL);
519 }
520
521 // -- print package name
522 if ((_packageName != null) && (_packageName.length() > 0)) {
523 jsw.write("package ");
524 jsw.write(_packageName);
525 jsw.writeln(';');
526 jsw.writeln();
527 }
528 jsw.flush();
529 }
530
531 /**
532 * A utility method that prints the imports to the given JSourceWriter.
533 *
534 * @param jsw The JSourceWriter to print to.
535 */
536 protected final void printImportDeclarations(final JSourceWriter jsw) {
537 if (jsw == null) {
538 throw new IllegalArgumentException(JSW_SHOULD_NOT_BE_NULL);
539 }
540
541 // -- print imports
542 if (!_imports.isEmpty()) {
543 jsw.writeln(" //---------------------------------/");
544 jsw.writeln(" //- Imported classes and packages -/");
545 jsw.writeln("//---------------------------------/");
546 jsw.writeln();
547 Enumeration<String> enumeration = _imports.elements();
548 while (enumeration.hasMoreElements()) {
549 jsw.write("import ");
550 jsw.write(enumeration.nextElement());
551 jsw.writeln(';');
552 }
553 jsw.writeln();
554 jsw.flush();
555 }
556 }
557
558 /**
559 * {@inheritDoc} <br/>
560 * Returns the String representation of this JType.
561 */
562 public final String toString() {
563 return getName();
564 }
565
566 // --------------------------------------------------------------------------
567 }