View Javadoc
1 /* 2 * Copyright (c) 2001, 2002 The XDoclet team 3 * All rights reserved. 4 */ 5 package xdoclet; 6 7 import org.apache.commons.collections.CollectionUtils; 8 import org.apache.commons.collections.Predicate; 9 import org.apache.commons.logging.LogFactory; 10 import org.apache.commons.logging.LogConfigurationException; 11 12 import xdoclet.beans.BeanContextSupportEx; 13 14 import java.io.File; 15 import java.io.IOException; 16 17 import java.text.MessageFormat; 18 19 import java.util.Collection; 20 import java.util.Iterator; 21 import java.lang.reflect.Method; 22 import java.lang.reflect.InvocationTargetException; 23 24 /*** 25 * <p>A Plugin is responsible for the generation of a particular kind of file. It 26 * is also an abstraction of underlying generation mechanisms such as Velocity, 27 * Jelly and Betwixt.</p> 28 * 29 * <p>A plugin also implements {@link java.beans.beancontext.BeanContextChild}, 30 * because it might be used in a Java Beans compliant environment such as 31 * an IDE. Therefore, this class is implicitly a {@link java.util.Collection}, 32 * and all contained objects (package substitutions etc) are contained directly 33 * in the instances of this class. They are filtered out by various internal 34 * {@link Predicate}s.</p> 35 * 36 * <p>A plugin can operate in two different modes, depending on the 37 * value of the fileName:</p> 38 * 39 * <ul> 40 * <li>If there is a "{0}" in the fileName, one file will be generated for 41 * each object in the collection provided by the MetadataProvider.</li> 42 * <li>If there is no "{0}" in the fileName, one file will be generated for each object 43 * in the collection provided by the MetadataProvider. This makes it possible to use the XDoclet 44 * core for generation of files where the "metadata source" is other kinds of 45 * objects than e.g. xjavadoc.</li> 46 * </ul> 47 * 48 * @see PluginFactory 49 * @see XDoclet 50 * 51 * @author <a href="mailto:aslak.hellesoy at netcom.no">Aslak Hellesøy</a> 52 * @version $Revision: 1.29 $ 53 */ 54 public abstract class Plugin extends BeanContextSupportEx { 55 /*** Encoding of output. */ 56 private String _encoding = "ISO-8859-1"; 57 private String _packageName = ""; 58 private String _fileName = ""; 59 60 /*** The name. */ 61 private String _name; 62 63 /*** The directory where plugin will write the files. */ 64 private File _destinationDir; 65 66 private MetadataProvider _metadataProvider; 67 68 public Plugin() { 69 // We'll use our own name as default 70 setName(getClass().getName()); 71 } 72 73 public XDoclet getXDoclet() { 74 return (XDoclet) getParent(); 75 } 76 77 public void setMetadataProvider( MetadataProvider metadataProvider ) { 78 _metadataProvider = metadataProvider; 79 } 80 81 protected MetadataProvider getMetadataProvider() { 82 return _metadataProvider; 83 } 84 85 /*** 86 * Gets the name of the plugin. 87 * @return the name of the plugin. 88 */ 89 public final String getName() { 90 return _name; 91 } 92 93 /*** 94 * Sets the name of the plugin. 95 * @param name the name of the plugin. 96 * @bean.property hidden="true" 97 */ 98 public final void setName(String name) { 99 _name = name; 100 } 101 102 /*** 103 * Gets the XML encoding. 104 * 105 * @see #setEncoding 106 * @return the XML encoding. 107 */ 108 public String getEncoding() { 109 return _encoding; 110 } 111 112 /*** 113 * Sets the encoding to use. Only applies to generation of XML files. 114 * Default is "ISO-8859-1" 115 * 116 * @bean.property 117 * shortDescription="Encoding of generated file(s). Applies to XML generation only." 118 * displayName="Encoding" 119 * @param encoding the encoding to use. 120 */ 121 public void setEncoding(String encoding) { 122 _encoding = encoding; 123 } 124 125 /*** 126 * Returns the destination directory (without package directory). 127 * @return the destination directory 128 */ 129 public File getDestinationDir() { 130 return _destinationDir; 131 } 132 133 /*** 134 * Sets (and creates) the destination directory. 135 * 136 * @bean.property 137 * shortDescription="Directory where files will be written." 138 * displayName="Destination Directory" 139 * editor="xdoclet.beans.FilePropertyEditor" 140 * @param destinationDir the directory where the plugin will write its files. 141 */ 142 public void setDestinationDir(File destinationDir) { 143 _destinationDir = destinationDir; 144 _destinationDir.mkdirs(); 145 } 146 147 /*** 148 * Sets the destination directory. The reason why the argument is not a 149 * java.io.File is that 150 * the ant wrapper uses reflection in order to call this method, and it only works 151 * with java.lang.String. If this method is called explicitly (typically from 152 * an IDE wrapper), make sure the destination is an absolute path. 153 * 154 * @param destination the directory where the plugin will write its files. 155 */ 156 public void setDestination(String destination) { 157 setDestinationDir( new File(destination) ); 158 } 159 160 /*** 161 * Sets the file name of the generated file(s). The value should be a plain 162 * a pattern such as Foo{0}.java or a plain String such as Bar.txt 163 * The occurrance of {0} will be substituted by the name of the class 164 * the file is generated from. 165 * 166 * @bean.property 167 * shortDescription="Name of generated file. If multiple files should be generated, use {0} in the name." 168 * displayName="Generated File" 169 * @param fileName the name of the generated file(s). 170 */ 171 public void setFileName(String fileName) { 172 _fileName = fileName; 173 } 174 175 public final String getFileName() { 176 return _fileName; 177 } 178 179 /*** 180 * Creates a new Accept 181 * 182 * @bean.method shortDescription="Add a new filter" 183 * displayName="New Filter" 184 */ 185 public final Accept createAccept() { 186 if (getAccept() != null) { 187 throw new IllegalStateException("Only one accept is allowed"); 188 } 189 190 Accept accept = new Accept(); 191 192 add(accept); 193 194 return accept; 195 } 196 197 private Accept getAccept() { 198 return (Accept) CollectionUtils.find(this, 199 new Predicate() { 200 public boolean evaluate(Object o) { 201 return o instanceof Accept; 202 } 203 }); 204 } 205 206 /*** 207 * Creates a new PackageSubstitution 208 * 209 * @bean.method shortDescription="Add a new Package Substitution" 210 * displayName="New Package Substitution" 211 */ 212 public final PackageSubstitution createPackageSubstitution() { 213 LogFactory.getLog(Plugin.class).info(this + ".createPackageSubstitution"); 214 215 PackageSubstitution packageSubstitution = new PackageSubstitution()/package-summary.html">PackageSubstitution packageSubstitution = new PackageSubstitution(); 216 217 add(packageSubstitution); 218 219 return packageSubstitution; 220 } 221 222 private Collection getPackageSubstitutions() { 223 return CollectionUtils.select(this, 224 new Predicate() { 225 public boolean evaluate(Object o) { 226 return o instanceof PackageSubstitution; 227 } 228 }); 229 } 230 231 /*** 232 * Generates the content. 233 * 234 * @throws XDocletException 235 */ 236 public void execute() throws IOException, XDocletException { 237 validate(); 238 239 File destinationFile; 240 241 if (getFileName() == null) { 242 throw new XDocletException("fileName was not specified for plugin " + getName()); 243 } 244 if (getDestinationDir() == null) { 245 throw new XDocletException("destinationDir was not specified for plugin " + getName()); 246 } 247 248 if (isGenerateOneFile()) { 249 Collection metaData = getFilteredMetadataCollection(); 250 destinationFile = getDestinationFileForAll(); 251 LogFactory.getLog(Plugin.class).info("Generating " + destinationFile.getAbsolutePath()); 252 generate(destinationFile,metaData); 253 } else { 254 LogFactory.getLog(Plugin.class).info("Generating " + getFileName() + "..."); 255 256 for (Iterator i = getFilteredMetadataCollection().iterator(); i.hasNext();) { 257 Object object = i.next(); 258 259 destinationFile = getDestinationFileForOne(object); 260 LogFactory.getLog(Plugin.class).info("Generating " + destinationFile.getAbsolutePath()); 261 262 generate(destinationFile,object); 263 } 264 } 265 } 266 267 /*** 268 * Generates a file. 269 * 270 * @param destinationFile file to be generated. 271 * @param metaData metadata used during generation. 272 * @throws XDocletException if generation fails. 273 * @throws IOException if an IO error occurs. 274 */ 275 protected abstract void generate(File destinationFile, Collection metaData) 276 throws IOException, XDocletException; 277 278 /*** 279 * Generates a file. 280 * 281 * @param destinationFile file to be generated. 282 * @param metaData metadata used during generation. 283 * @throws XDocletException if generation fails. 284 * @throws IOException if an IO error occurs. 285 */ 286 protected abstract void generate(File destinationFile, Object metaData) 287 throws IOException, XDocletException; 288 289 protected void validate() 290 throws XDocletException { 291 if ( !"".equals( _packageName )) { 292 if (!getPackageSubstitutions().isEmpty()) { 293 throw new XDocletException("Can't specify both packageSubstitution and packageName."); 294 } 295 } 296 } 297 298 /*** 299 * Indicates whether or not to generate one file per metadata object or 300 * one file for all metadata objects. 301 * 302 * @return <code>true</code> if the fileName does not contain <code>"{0}"</code>. 303 * @see #setFileName(java.lang.String) 304 */ 305 protected boolean isGenerateOneFile() { 306 return getFileName().indexOf("{0}") == -1; 307 } 308 309 /*** 310 * Sets the package name of the generated files. This will be converted 311 * to a path and appended to destinationDir. Only specify this if no 312 * packageSubstitution is used. 313 * 314 * @bean.property 315 * shortDescription="Package name of generated file(s)." 316 * displayName="Package Name" 317 * @param packageName the package name. 318 */ 319 public final void setPackageName(String packageName) { 320 _packageName = packageName; 321 } 322 323 public final String getPackageName() { 324 return _packageName; 325 } 326 327 private final String getSubstitutedPackageName(Object object) 328 throws XDocletException { 329 String packageName = null; 330 String originalPackageName = getMetadataProvider().getPackageName(object); 331 332 for (Iterator i = getPackageSubstitutions().iterator(); i.hasNext();) { 333 PackageSubstitution packageSubstitution = (PackageSubstitution) i/next()/package-summary.html">PackageSubstitution packageSubstitution = (PackageSubstitution) i.next(); 334 335 // Check that we don't have a conflict. We must verify that there is not more than one match for from. 336 String candidate = packageSubstitution.getSubstitutedPackageName(originalPackageName); 337 338 if ((packageName != null) && (candidate != null)) { 339 throw new XDocletException("Ambiguous package substitution for " 340 + getMetadataProvider().getPackageName(object) + ". Both " + packageName + " or " 341 + candidate + " are possible substitution results."); 342 } 343 344 packageName = candidate; 345 } 346 347 <b>if (packageName == null) { 348 // If packageName is null (no match), use the originalPackageName 349 packageName = originalPackageName; 350 } 351 352 return packageName; 353 } 354 355 /*** 356 * Gets all accepted objects. Subclasses can control what's accepted 357 * by calling {@link #createAccept} and call setPredicate on it, using 358 * a predicate from the xdoclet.util.predicates package. If no predicate 359 * is set, all classes that were parsed will be returned. 360 * 361 * @return all accepted classes. 362 */ 363 protected final Collection getFilteredMetadataCollection() 364 throws XDocletException { 365 366 // TODO should we cache this? Probably. 367 Collection metadataCollection = getMetadataProvider().createMetadataCollection(); 368 369 Collection result; 370 371 if (getAccept() != null) { 372 // Filter out the objects we want. 373 result = CollectionUtils.select(metadataCollection, getAccept()); 374 } else { 375 result = metadataCollection; 376 } 377 378 if (result.isEmpty()) { 379 LogFactory.getLog(Plugin.class).warn("No objects for " + getName()); 380 } 381 382 return result; 383 } 384 385 /*** 386 * Creates the destination directory. 387 * 388 * @return the destination directory. 389 */ 390 private final File getAndCreateRealDestDir(String packageName) 391 throws XDocletException { 392 >if( packageName == null ) { 393 // if package name is null, use the global one. 394 packageName = getPackageName(); 395 } 396 >if( packageName == null ) { 397 throw new XDocletException( "packageName can't be null" ); 398 } 399 if (getDestinationDir() == null) { 400 throw new XDocletException("destination was not specified for plugin \"" + getName() + "\""); 401 } 402 403 String packageNameAsPath = packageName.replace('.', File.separatorChar); 404 File dir = new File(getDestinationDir(), packageNameAsPath); 405 406 dir.mkdirs(); 407 408 return dir; 409 } 410 411 /*** 412 * Returns the destination file derived from a particular object. Will be 413 * called if fileName does not have "{0}" in it. 414 * 415 * @return the File where content will be written 416 * @throws XDocletException 417 */ 418 public final File getDestinationFileForAll() 419 throws XDocletException { 420 return new File(getAndCreateRealDestDir(getPackageName()), getFileName()); 421 } 422 423 /*** 424 * Returns the destination file derived from a particular object. Will be 425 * called if fileName has "{0}" in it. 426 * 427 * @param object the object the generated file is derived from 428 * @return the File where content will be written 429 * @throws XDocletException 430 */ 431 public final File getDestinationFileForOne(Object object) 432 throws XDocletException { 433 String fileNameSubstitutionValue = getMetadataProvider().getFilenameSubstitutionValue(object); 434 String packageName = getSubstitutedPackageName(object); 435 436 File destinationDir = null; 437 String[] formatArguments = null; 438 439 if (getFileName().indexOf("{1}") != -1) { 440 formatArguments = new String[] { fileNameSubstitutionValue, packageName }; 441 442 // Don't use package name when finding the destDir. It's in the fileName 443 packageName = ""; 444 } else { 445 formatArguments = new String[] { fileNameSubstitutionValue }; 446 } 447 448 destinationDir = getAndCreateRealDestDir(packageName); 449 450 String fileName = MessageFormat.format(getFileName(), formatArguments); 451 452 return new File(destinationDir, fileName); 453 } 454 455 /*** 456 * Throws XDocletException if a specific class is not on the CP. Should be called from subclasses 457 * constructors to verify that classpath is OK. 458 * 459 * @param className the name of the class to check. 460 */ 461 protected static void checkClass(String className) 462 throws XDocletException { 463 try { 464 Class.forName(className); 465 } catch (ClassNotFoundException e) { 466 throw new XDocletException("Couldn't load " + className 467 + ". Make sure you have this class on the classpath used to define XDoclet."); 468 } 469 } 470 471 /*** 472 * Hook to JXPath, which lets templates query the datamodel with xpath. 473 * 474 * @param contextBean the bean to query. 475 * @param xpath the xpath expression. 476 * @return the resulting bean. 477 */ 478 public Object jxpath(Object contextBean, String xpath) { 479 Object result = null; 480 try { 481 // We're using reflection here to avoid dependency on jxpath. 482 // JXPathContext context = JXPathContext.newContext(contextBean); 483 // result = context.getValue(xpath); 484 485 Class jxPathContextClass = getClass().getClassLoader().loadClass("org.apache.commons.jxpath.JXPathContext"); 486 Method newContextMethod = jxPathContextClass.getMethod( "newContext", new Class[]{ Object.class } ); 487 Object jxPathContext = newContextMethod.invoke( null, new Object[] { contextBean } ); 488 Method getValueMethod = jxPathContext.getClass().getMethod( "getValue", new Class[]{ String.class } ); 489 result = getValueMethod.invoke( jxPathContext, new Object[] { xpath } ); 490 491 LogFactory.getLog(Plugin.class).debug( contextBean + "," + xpath + "->" + result ); 492 } catch( ClassNotFoundException e ) { 493 494 } catch( NoSuchMethodException e ) { 495 496 } catch( SecurityException e ) { 497 498 } catch( IllegalAccessException e ) { 499 500 } catch( IllegalArgumentException e ) { 501 502 } catch( InvocationTargetException e ) { 503 e.printStackTrace(); 504 } catch( LogConfigurationException e ) { 505 506 } 507 return result; 508 } 509 510 // The default implementation of MetadataProvider will just delegate 511 // to the MetadataProvider hooked onto the XDoclet object. 512 513 public final Collection AcreateMetadataCollection() throws XDocletException { 514 // return getXDocletMetadataProvider().createMetadataCollection(); 515 return null; 516 } 517 518 public final String AgetFilenameSubstitutionValue(Object o) throws XDocletException { 519 // return getXDocletMetadataProvider().getFilenameSubstitutionValue(o); 520 return null; 521 } 522 523 public final String AgetPackageName(Object o) { 524 // return getXDocletMetadataProvider().getPackageName(o); 525 return null; 526 } 527 528 public final void Acleanup() throws XDocletException { 529 // getXDocletMetadataProvider().cleanup(); 530 } 531 532 public final void AsetClasspath( String classpath ) throws XDocletException { 533 // getXDocletMetadataProvider().setClasspath( classpath ); 534 } 535 }

This page was automatically generated by Maven