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 }