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