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 2001-2002 (C) Intalio, Inc. All Rights Reserved. 42 * 43 * Contributors: 44 * -------------- 45 * Gary Shea (shea@gtsdesign.com) - Original Author 46 * Keith Visco (kvisco@intalio.com) - Changed JCompElement references to 47 * JStructure, some additional tweaking 48 * to get it working with the current 49 * Javasource package. 50 * 51 * $Id$ 52 */ 53 package org.codehaus.modello.plugin.java.javasource; 54 55 /* 56 * Copyright (c) 2004, Codehaus.org 57 * 58 * Permission is hereby granted, free of charge, to any person obtaining a copy of 59 * this software and associated documentation files (the "Software"), to deal in 60 * the Software without restriction, including without limitation the rights to 61 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 62 * of the Software, and to permit persons to whom the Software is furnished to do 63 * so, subject to the following conditions: 64 * 65 * The above copyright notice and this permission notice shall be included in all 66 * copies or substantial portions of the Software. 67 * 68 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 69 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 70 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 71 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 72 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 73 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 74 * SOFTWARE. 75 */ 76 77 import java.io.File; 78 import java.nio.file.Files; 79 import java.util.ArrayList; 80 import java.util.Enumeration; 81 import java.util.List; 82 import java.util.SortedSet; 83 import java.util.TreeSet; 84 85 /** 86 * A representation of the Java Source code for a Java compilation 87 * unit. This is 88 * a useful utility when creating in memory source code. 89 * This package was modelled after the Java Reflection API 90 * as much as possible to reduce the learning curve. 91 * @author <a href="mailto:shea@gtsdesign.com">Gary Shea</a> 92 * @version $Revision$ $Date$ 93 **/ 94 public class JCompUnit { 95 96 /** 97 * The Id for Source control systems 98 * I needed to separate this line to prevent CVS from 99 * expanding it here! ;-) 100 **/ 101 private static final String DEFAULT_HEADER = "$" + "Id$"; 102 103 private JComment header = null; 104 105 /** 106 * The package for this JCompUnit 107 **/ 108 private String packageName = null; 109 110 /** 111 * The file to which this JCompUnit will be saved 112 **/ 113 private String fileName = null; 114 115 /** 116 * The set of top-level classes that live in this compilation unit. 117 **/ 118 // private TypeList classes = null; 119 private List<JClass> classes = null; 120 121 /** 122 * The set of top-level interfaces that live in this compilation unit. 123 **/ 124 // private TypeList interfaces = null; 125 private List<JInterface> interfaces = null; 126 127 /** 128 * Creates a new JCompUnit 129 * @param packageName the name of the package for this JCompUnit. 130 * If packageName is null or empty, no 'package' line will be generated. 131 * @param fileName the name of the file in which this JCompUnit 132 * will be stored 133 **/ 134 public JCompUnit(String packageName, String fileName) { 135 this.packageName = packageName; 136 this.fileName = fileName; 137 init(); 138 } // -- JCompUnit 139 140 /** 141 * Creates a new JCompUnit with the given JClass (which must have 142 * been created with either a full class name or package/local 143 * name) as the public class. Package and file name are taken 144 * from jClass. 145 * @param jClass the public class for this JCompUnit. 146 **/ 147 public JCompUnit(JClass jClass) { 148 this.packageName = jClass.getPackageName(); 149 150 // The outer name is the package plus the simple name of the 151 // outermost enclosing class. The file name is just the 152 // simple name of the outermost enclosing class, so the 153 // package name part must be stripped off. 154 155 /* 156 Commented out until inner-class support has been added. 157 kvisco - 20021211 158 159 String outer = jClass.getOuterName(); 160 int lastDot = outer.lastIndexOf("."); 161 String filePrefix; 162 if (lastDot != -1) { 163 filePrefix = outer.substring (lastDot + 1); 164 } else { 165 filePrefix = outer; 166 } 167 */ 168 String filePrefix = jClass.getLocalName(); 169 170 this.fileName = filePrefix + ".java"; 171 init(); 172 classes.add(jClass); 173 } // -- JCompUnit 174 175 /** 176 * Creates a new JCompUnit with the given JInterface as public interface 177 * Package and file name are taken from jInterface. 178 * @param jInterface the public interface for this JCompUnit. 179 **/ 180 public JCompUnit(JInterface jInterface) { 181 this.packageName = jInterface.getPackageName(); 182 this.fileName = jInterface.getLocalName() + ".java"; 183 init(); 184 interfaces.add(jInterface); 185 } // -- JCompUnit 186 187 private void init() { 188 classes = new ArrayList<JClass>(); 189 interfaces = new ArrayList<JInterface>(); 190 } 191 192 /** 193 * Adds the given JStructure (either a JInterface or 194 * a JClass) to this JCompUnit. 195 * 196 * @param jStructure the JStructure to add 197 * @exception java.lang.IllegalArgumentException when the given 198 * JStructure has the same name of an existing JStructure 199 * or if the class of jStructure is unknown. 200 */ 201 public void addStructure(JStructure jStructure) throws IllegalArgumentException { 202 if (jStructure instanceof JInterface) addInterface((JInterface) jStructure); 203 else if (jStructure instanceof JClass) addClass((JClass) jStructure); 204 else { 205 String err = "Unknown JStructure subclass '" + jStructure.getClass().getName() + "'."; 206 throw new IllegalArgumentException(err); 207 } 208 } // -- addStructure 209 210 /** 211 * Adds a JClass which should be printed in this file. 212 * 213 * @param jClass the JClass to add 214 **/ 215 public void addClass(JClass jClass) { 216 classes.add(jClass); 217 } // -- addClass 218 219 /** 220 * Adds a JInterface which should be printed in this file. 221 * 222 * @param jInterface the JInterface to add 223 **/ 224 public void addInterface(JInterface jInterface) { 225 interfaces.add(jInterface); 226 } // -- addInterface 227 228 /** 229 * returns a array of String containing all import classes/packages, 230 * also imports within the same package of this object. 231 * @return a array of String containing all import classes/packages, 232 * also imports within the same package of this object. 233 */ 234 public SortedSet<String> getImports() { 235 236 SortedSet<String> allImports = new TreeSet<String>(); 237 238 // add imports from classes 239 for (JClass jClass : classes) { 240 Enumeration<String> e = jClass.getImports(); 241 while (e.hasMoreElements()) { 242 allImports.add(e.nextElement()); 243 } 244 } 245 246 for (JInterface jInterface : interfaces) { 247 Enumeration<String> e = jInterface.getImports(); 248 while (e.hasMoreElements()) { 249 allImports.add(e.nextElement()); 250 } 251 } 252 253 return allImports; 254 } 255 256 /** 257 * Returns the name of the file that this JCompUnit would be 258 * printed as, given a call to #print. 259 * 260 * @param destDir the destination directory. This may be null. 261 * @return the name of the file that this JCompUnit would be 262 * printed as, given a call to #print. 263 **/ 264 public String getFilename(String destDir) { 265 266 String filename = new String(fileName); 267 268 // -- Convert Java package to path string 269 String javaPackagePath = ""; 270 if ((packageName != null) && (packageName.length() > 0)) { 271 javaPackagePath = packageName.replace('.', File.separatorChar); 272 } 273 274 // -- Create fully qualified path (including 'destDir') to file 275 File pathFile; 276 if (destDir == null) pathFile = new File(javaPackagePath); 277 else pathFile = new File(destDir, javaPackagePath); 278 if (!pathFile.exists()) { 279 pathFile.mkdirs(); 280 } 281 282 // -- Prefix filename with path 283 if (pathFile.toString().length() > 0) filename = pathFile.toString() + File.separator + filename; 284 285 return filename; 286 } // -- getFilename 287 288 /** 289 * Returns the name of the package that this JCompUnit is a member of 290 * @return the name of the package that this JCompUnit is a member of, 291 * or null if there is no current package name defined 292 **/ 293 public String getPackageName() { 294 return this.packageName; 295 } // -- getPackageName 296 297 protected static String getPackageFromClassName(String className) { 298 int idx = -1; 299 if ((idx = className.lastIndexOf('.')) > 0) { 300 return className.substring(0, idx); 301 } 302 return null; 303 } // -- getPackageFromClassName 304 305 /** 306 * Prints the source code for this JClass in the current directory 307 * with the default line seperator of the the runtime platform. 308 * @see #print(java.lang.String, java.lang.String) 309 **/ 310 public void print() { 311 print(null, null); 312 } // -- print 313 314 /** 315 * Prints the source code for this JClass 316 * with the default line seperator of the the runtime platform. 317 * @param destDir the destination directory to generate the file. 318 * @see #print(java.lang.String, java.lang.String) 319 **/ 320 public void print(String destDir) { 321 print(destDir, null); 322 } // -- print 323 324 /** 325 * Prints the source code for this JCompUnit. 326 * @param destDir the destination directory to generate the file. 327 * @param lineSeparator the line separator to use at the end of each line. 328 * If null, then the default line separator for the runtime platform will 329 * be used. 330 **/ 331 public void print(String destDir, String lineSeparator) { 332 333 // -- open output file 334 String filename = getFilename(destDir); 335 336 File file = new File(filename); 337 JSourceWriter jsw = null; 338 try { 339 jsw = new JSourceWriter(Files.newBufferedWriter(file.toPath())); 340 } catch (java.io.IOException ioe) { 341 System.out.println("unable to create compilation unit file: " + filename); 342 return; 343 } 344 345 if (lineSeparator == null) { 346 lineSeparator = System.getProperty("line.separator"); 347 } 348 jsw.setLineSeparator(lineSeparator); 349 print(jsw); 350 jsw.flush(); 351 jsw.close(); 352 } // -- print 353 354 /** 355 * Prints the source code for this JClass. 356 * @param jsw the JSourceWriter to print to. 357 **/ 358 public void print(JSourceWriter jsw) { 359 360 // Traverse the nested class and interface heirarchy and 361 // update the names to match the compilation unit. 362 363 resolveNames(); 364 StringBuilder buffer = new StringBuilder(); 365 366 // -- write file header 367 if (header != null) header.print(jsw); 368 else { 369 jsw.writeln("/*"); 370 jsw.writeln(" * " + DEFAULT_HEADER); 371 jsw.writeln("*/"); 372 } 373 jsw.writeln(); 374 jsw.flush(); 375 376 // -- print package name 377 if ((packageName != null) && (packageName.length() > 0)) { 378 379 buffer.setLength(0); 380 buffer.append("package "); 381 buffer.append(packageName); 382 buffer.append(';'); 383 jsw.writeln(buffer.toString()); 384 jsw.writeln(); 385 } 386 387 // -- print imports 388 jsw.writeln(" //---------------------------------------------/"); 389 jsw.writeln(" //- Imported classes, interfaces and packages -/"); 390 jsw.writeln("//---------------------------------------------/"); 391 jsw.writeln(); 392 SortedSet<String> allImports = getImports(); 393 String compUnitPackage = getPackageName(); 394 for (String importName : allImports) { 395 String importsPackage = JStructure.getPackageFromClassName(importName); 396 if (importsPackage != null && !importsPackage.equals(compUnitPackage)) { 397 jsw.write("import "); 398 jsw.write(importName); 399 jsw.writeln(';'); 400 } 401 } 402 jsw.writeln(); 403 404 // Print the public elements, interfaces first, then classes. 405 // There should only be one public element, but if there are 406 // more we let the compiler catch it. 407 printStructures(jsw, true); 408 409 // Print the remaining non-public elements, interfaces first. 410 printStructures(jsw, false); 411 412 jsw.flush(); 413 } // -- print 414 415 /** 416 * Print the source code for the contained JClass objects. 417 * @param jsw the JSourceWriter to print to. 418 * @param printPublic if true, print only public classes; if 419 * false, print only non-public classes. 420 **/ 421 public final void printStructures(JSourceWriter jsw, boolean printPublic) { 422 423 // -- print class information 424 // -- we need to add some JavaDoc API adding comments 425 426 boolean isFirst = true; 427 428 // SortedSet interfaceList = interfaces.sortedOnFullName(); 429 for (JInterface jInterface : interfaces) { 430 if (jInterface.getModifiers().isPublic() == printPublic) { 431 if (isFirst) { 432 Header.print(jsw, printPublic); 433 isFirst = false; 434 } 435 jInterface.print(jsw, true); 436 jsw.writeln(); 437 } 438 } 439 440 // SortedSet classList = classes.sortedOnFullName(); 441 for (JClass jClass : classes) { 442 if (jClass.getModifiers().isPublic() == printPublic) { 443 if (isFirst) { 444 Header.print(jsw, printPublic); 445 isFirst = false; 446 } 447 jClass.print(jsw, true); 448 jsw.writeln(); 449 } 450 } 451 } // -- printElements(JSourceWriter, int) 452 453 /** 454 * Sets the header comment for this JCompUnit 455 * @param comment the comment to display at the top of the source file 456 * when printed 457 **/ 458 public void setHeader(JComment comment) { 459 this.header = comment; 460 } // -- setHeader 461 462 // -------------------/ 463 // - Private Methods -/ 464 // -------------------/ 465 466 /** 467 * Update the names of nested classes and interfaces. 468 **/ 469 private void resolveNames() { 470 471 /* 472 Commented out until support for inner-classes is added 473 kvisco - 20021211 474 475 for (int i = 0; i < classes.size(); i++) { 476 JClass jClass = (JClass) classes.get(i); 477 jClass.resolveNames(packageName, null); 478 } 479 for (int i = 0; i < interfaces.size(); i++) { 480 JInterface jInterface = (JInterface) interfaces.get(i); 481 jInterface.resolveNames(packageName, null); 482 } 483 */ 484 } // -- resolveNames 485 } // -- JCompUnit 486 487 /** 488 * Print the headers delineating the public and non-public elements of 489 * the compilation unit. 490 **/ 491 class Header { 492 493 private static String[] publicHeader = { 494 " //-----------------------------/", " //- Public Class / Interface -/", "//-----------------------------/", 495 }; 496 private static String[] nonPublicHeader = { 497 " //-------------------------------------/", 498 " //- Non-Public Classes / Interfaces -/", 499 "//-------------------------------------/", 500 }; 501 502 /** 503 * Print the specified header to the given Writer. 504 * @params JSourceWriter an open JSourceWriter 505 * @params boolean if true print the public header, otherwise 506 * print the non-public header. 507 **/ 508 protected static void print(JSourceWriter jsw, boolean printPublic) { 509 String[] header = printPublic ? publicHeader : nonPublicHeader; 510 for (String aHeader : header) { 511 jsw.writeln(aHeader); 512 } 513 jsw.writeln(); 514 } 515 } // -- Header