001 /* 002 * Copyright (C) Christian Schulte, 2005-206 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without 006 * modification, are permitted provided that the following conditions 007 * are met: 008 * 009 * o Redistributions of source code must retain the above copyright 010 * notice, this list of conditions and the following disclaimer. 011 * 012 * o Redistributions in binary form must reproduce the above copyright 013 * notice, this list of conditions and the following disclaimer in 014 * the documentation and/or other materials provided with the 015 * distribution. 016 * 017 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 018 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 019 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 020 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, 021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 022 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 023 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 024 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 026 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 027 * 028 * $JOMC: JomcTool.java 4539 2012-05-23 03:04:49Z schulte2005 $ 029 * 030 */ 031 package org.jomc.tools; 032 033 import java.io.BufferedReader; 034 import java.io.ByteArrayInputStream; 035 import java.io.ByteArrayOutputStream; 036 import java.io.FileNotFoundException; 037 import java.io.IOException; 038 import java.io.InputStream; 039 import java.io.InputStreamReader; 040 import java.io.OutputStreamWriter; 041 import java.io.Reader; 042 import java.io.StringReader; 043 import java.lang.ref.Reference; 044 import java.lang.ref.SoftReference; 045 import java.lang.reflect.InvocationTargetException; 046 import java.net.URL; 047 import java.text.DateFormat; 048 import java.text.Format; 049 import java.text.MessageFormat; 050 import java.text.SimpleDateFormat; 051 import java.util.ArrayList; 052 import java.util.Calendar; 053 import java.util.Collections; 054 import java.util.Enumeration; 055 import java.util.HashMap; 056 import java.util.List; 057 import java.util.Locale; 058 import java.util.Map; 059 import java.util.ResourceBundle; 060 import java.util.Set; 061 import java.util.concurrent.ConcurrentHashMap; 062 import java.util.concurrent.CopyOnWriteArrayList; 063 import java.util.concurrent.CopyOnWriteArraySet; 064 import java.util.logging.Level; 065 import javax.activation.MimeType; 066 import javax.activation.MimeTypeParseException; 067 import org.apache.commons.io.IOUtils; 068 import org.apache.commons.lang.StringEscapeUtils; 069 import org.apache.commons.lang.StringUtils; 070 import org.apache.velocity.Template; 071 import org.apache.velocity.VelocityContext; 072 import org.apache.velocity.app.VelocityEngine; 073 import org.apache.velocity.exception.ParseErrorException; 074 import org.apache.velocity.exception.ResourceNotFoundException; 075 import org.apache.velocity.exception.VelocityException; 076 import org.apache.velocity.runtime.RuntimeConstants; 077 import org.apache.velocity.runtime.RuntimeServices; 078 import org.apache.velocity.runtime.log.LogChute; 079 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; 080 import org.apache.velocity.runtime.resource.loader.URLResourceLoader; 081 import org.jomc.model.Argument; 082 import org.jomc.model.ArgumentType; 083 import org.jomc.model.Dependency; 084 import org.jomc.model.Implementation; 085 import org.jomc.model.InheritanceModel; 086 import org.jomc.model.Message; 087 import org.jomc.model.ModelObject; 088 import org.jomc.model.Modules; 089 import org.jomc.model.Multiplicity; 090 import org.jomc.model.Properties; 091 import org.jomc.model.Property; 092 import org.jomc.model.Specification; 093 import org.jomc.model.SpecificationReference; 094 import org.jomc.model.Specifications; 095 import org.jomc.model.Text; 096 import org.jomc.model.Texts; 097 import org.jomc.model.modlet.ModelHelper; 098 import org.jomc.modlet.Model; 099 100 /** 101 * Base tool class. 102 * 103 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a> 104 * @version $JOMC: JomcTool.java 4539 2012-05-23 03:04:49Z schulte2005 $ 105 */ 106 public class JomcTool 107 { 108 109 /** Listener interface. */ 110 public abstract static class Listener 111 { 112 113 /** Creates a new {@code Listener} instance. */ 114 public Listener() 115 { 116 super(); 117 } 118 119 /** 120 * Gets called on logging. 121 * 122 * @param level The level of the event. 123 * @param message The message of the event or {@code null}. 124 * @param throwable The throwable of the event or {@code null}. 125 * 126 * @throws NullPointerException if {@code level} is {@code null}. 127 */ 128 public void onLog( final Level level, final String message, final Throwable throwable ) 129 { 130 if ( level == null ) 131 { 132 throw new NullPointerException( "level" ); 133 } 134 } 135 136 } 137 138 /** Empty byte array. */ 139 private static final byte[] NO_BYTES = 140 { 141 }; 142 143 /** The prefix of the template location. */ 144 private static final String TEMPLATE_PREFIX = 145 JomcTool.class.getPackage().getName().replace( '.', '/' ) + "/templates/"; 146 147 /** Constant for the default template profile. */ 148 private static final String DEFAULT_TEMPLATE_PROFILE = "jomc-java"; 149 150 /** The default template profile. */ 151 private static volatile String defaultTemplateProfile; 152 153 /** 154 * The log level events are logged at by default. 155 * @see #getDefaultLogLevel() 156 */ 157 private static final Level DEFAULT_LOG_LEVEL = Level.WARNING; 158 159 /** The default log level. */ 160 private static volatile Level defaultLogLevel; 161 162 /** The model of the instance. */ 163 private Model model; 164 165 /** The {@code VelocityEngine} of the instance. */ 166 private VelocityEngine velocityEngine; 167 168 /** 169 * Flag indicating the default {@code VelocityEngine}. 170 * @since 1.2.4 171 */ 172 private boolean defaultVelocityEngine; 173 174 /** The encoding to use for reading templates. */ 175 private String templateEncoding; 176 177 /** 178 * The location to search for templates in addition to searching the class path. 179 * @since 1.2 180 */ 181 private URL templateLocation; 182 183 /** The encoding to use for reading files. */ 184 private String inputEncoding; 185 186 /** The encoding to use for writing files. */ 187 private String outputEncoding; 188 189 /** 190 * The template parameters. 191 * @since 1.2 192 */ 193 private Map<String, Object> templateParameters; 194 195 /** The template profile of the instance. */ 196 private String templateProfile; 197 198 /** The indentation string of the instance. */ 199 private String indentation; 200 201 /** The line separator of the instance. */ 202 private String lineSeparator; 203 204 /** The listeners of the instance. */ 205 private List<Listener> listeners; 206 207 /** The log level of the instance. */ 208 private Level logLevel; 209 210 /** 211 * The locale of the instance. 212 * @since 1.2 213 */ 214 private Locale locale; 215 216 /** Cached indentation strings. */ 217 private volatile Reference<Map<String, String>> indentationCache; 218 219 /** Cached template locations. */ 220 private volatile Reference<Map<String, String>> templateLocationsCache; 221 222 /** Cached template profile properties. */ 223 private volatile Reference<Map<String, java.util.Properties>> templateProfilePropertiesCache; 224 225 /** Cached Java keywords. */ 226 private volatile Reference<Set<String>> javaKeywordsCache; 227 228 /** Creates a new {@code JomcTool} instance. */ 229 public JomcTool() 230 { 231 super(); 232 } 233 234 /** 235 * Creates a new {@code JomcTool} instance taking a {@code JomcTool} instance to initialize the new instance with. 236 * 237 * @param tool The instance to initialize the new instance with. 238 * 239 * @throws NullPointerException if {@code tool} is {@code null}. 240 * @throws IOException if copying {@code tool} fails. 241 */ 242 public JomcTool( final JomcTool tool ) throws IOException 243 { 244 this(); 245 246 if ( tool == null ) 247 { 248 throw new NullPointerException( "tool" ); 249 } 250 251 this.indentation = tool.indentation; 252 this.inputEncoding = tool.inputEncoding; 253 this.lineSeparator = tool.lineSeparator; 254 this.listeners = tool.listeners != null ? new CopyOnWriteArrayList<Listener>( tool.listeners ) : null; 255 this.logLevel = tool.logLevel; 256 this.model = tool.model != null ? tool.model.clone() : null; 257 this.outputEncoding = tool.outputEncoding; 258 this.templateEncoding = tool.templateEncoding; 259 this.templateProfile = tool.templateProfile; 260 this.velocityEngine = tool.velocityEngine; 261 this.defaultVelocityEngine = tool.defaultVelocityEngine; 262 this.locale = tool.locale; 263 this.templateParameters = 264 tool.templateParameters != null 265 ? Collections.synchronizedMap( new HashMap<String, Object>( tool.templateParameters ) ) 266 : null; 267 268 this.templateLocation = 269 tool.templateLocation != null ? new URL( tool.templateLocation.toExternalForm() ) : null; 270 271 } 272 273 /** 274 * Gets the list of registered listeners. 275 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 276 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 277 * listeners property.</p> 278 * 279 * @return The list of registered listeners. 280 * 281 * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable) 282 */ 283 public List<Listener> getListeners() 284 { 285 if ( this.listeners == null ) 286 { 287 this.listeners = new CopyOnWriteArrayList<Listener>(); 288 } 289 290 return this.listeners; 291 } 292 293 /** 294 * Gets the default log level events are logged at. 295 * <p>The default log level is controlled by system property {@code org.jomc.tools.JomcTool.defaultLogLevel} holding 296 * the log level to log events at by default. If that property is not set, the {@code WARNING} default is 297 * returned.</p> 298 * 299 * @return The log level events are logged at by default. 300 * 301 * @see #getLogLevel() 302 * @see Level#parse(java.lang.String) 303 */ 304 public static Level getDefaultLogLevel() 305 { 306 if ( defaultLogLevel == null ) 307 { 308 defaultLogLevel = Level.parse( System.getProperty( "org.jomc.tools.JomcTool.defaultLogLevel", 309 DEFAULT_LOG_LEVEL.getName() ) ); 310 311 } 312 313 return defaultLogLevel; 314 } 315 316 /** 317 * Sets the default log level events are logged at. 318 * 319 * @param value The new default level events are logged at or {@code null}. 320 * 321 * @see #getDefaultLogLevel() 322 */ 323 public static void setDefaultLogLevel( final Level value ) 324 { 325 defaultLogLevel = value; 326 } 327 328 /** 329 * Gets the log level of the instance. 330 * 331 * @return The log level of the instance. 332 * 333 * @see #getDefaultLogLevel() 334 * @see #setLogLevel(java.util.logging.Level) 335 * @see #isLoggable(java.util.logging.Level) 336 */ 337 public final Level getLogLevel() 338 { 339 if ( this.logLevel == null ) 340 { 341 this.logLevel = getDefaultLogLevel(); 342 343 if ( this.isLoggable( Level.CONFIG ) ) 344 { 345 this.log( Level.CONFIG, getMessage( "defaultLogLevelInfo", this.logLevel.getLocalizedName() ), null ); 346 } 347 } 348 349 return this.logLevel; 350 } 351 352 /** 353 * Sets the log level of the instance. 354 * 355 * @param value The new log level of the instance or {@code null}. 356 * 357 * @see #getLogLevel() 358 * @see #isLoggable(java.util.logging.Level) 359 */ 360 public final void setLogLevel( final Level value ) 361 { 362 this.logLevel = value; 363 } 364 365 /** 366 * Checks if a message at a given level is provided to the listeners of the instance. 367 * 368 * @param level The level to test. 369 * 370 * @return {@code true}, if messages at {@code level} are provided to the listeners of the instance; 371 * {@code false}, if messages at {@code level} are not provided to the listeners of the instance. 372 * 373 * @throws NullPointerException if {@code level} is {@code null}. 374 * 375 * @see #getLogLevel() 376 * @see #setLogLevel(java.util.logging.Level) 377 * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable) 378 */ 379 public boolean isLoggable( final Level level ) 380 { 381 if ( level == null ) 382 { 383 throw new NullPointerException( "level" ); 384 } 385 386 return level.intValue() >= this.getLogLevel().intValue(); 387 } 388 389 /** 390 * Gets the Java package name of a specification. 391 * 392 * @param specification The specification to get the Java package name of. 393 * 394 * @return The Java package name of {@code specification} or {@code null}. 395 * 396 * @throws NullPointerException if {@code specification} is {@code null}. 397 */ 398 public String getJavaPackageName( final Specification specification ) 399 { 400 if ( specification == null ) 401 { 402 throw new NullPointerException( "specification" ); 403 } 404 405 return specification.getClazz() != null ? this.getJavaPackageName( specification.getClazz() ) : null; 406 } 407 408 /** 409 * Gets the Java type name of a specification. 410 * 411 * @param specification The specification to get the Java type name of. 412 * @param qualified {@code true}, to return the fully qualified type name (with package name prepended); 413 * {@code false}, to return the short type name (without package name prepended). 414 * 415 * @return The Java type name of {@code specification} or {@code null}. 416 * 417 * @throws NullPointerException if {@code specification} is {@code null}. 418 * 419 * @see #getJavaPackageName(org.jomc.model.Specification) 420 */ 421 public String getJavaTypeName( final Specification specification, final boolean qualified ) 422 { 423 if ( specification == null ) 424 { 425 throw new NullPointerException( "specification" ); 426 } 427 428 if ( specification.getClazz() != null ) 429 { 430 final StringBuilder typeName = new StringBuilder( specification.getClazz().length() ); 431 final String javaPackageName = this.getJavaPackageName( specification ); 432 433 if ( qualified && javaPackageName.length() > 0 ) 434 { 435 typeName.append( javaPackageName ).append( '.' ); 436 } 437 438 typeName.append( javaPackageName.length() > 0 439 ? specification.getClazz().substring( javaPackageName.length() + 1 ) 440 : specification.getClazz() ); 441 442 return typeName.toString(); 443 } 444 445 return null; 446 } 447 448 /** 449 * Gets the Java class path location of a specification. 450 * 451 * @param specification The specification to return the Java class path location of. 452 * 453 * @return The Java class path location of {@code specification} or {@code null}. 454 * 455 * @throws NullPointerException if {@code specification} is {@code null}. 456 * 457 * @see #getJavaTypeName(org.jomc.model.Specification, boolean) 458 */ 459 public String getJavaClasspathLocation( final Specification specification ) 460 { 461 if ( specification == null ) 462 { 463 throw new NullPointerException( "specification" ); 464 } 465 466 return specification.getClazz() != null 467 ? ( this.getJavaTypeName( specification, true ) ).replace( '.', '/' ) 468 : null; 469 470 } 471 472 /** 473 * Gets the Java package name of a specification reference. 474 * 475 * @param reference The specification reference to get the Java package name of. 476 * 477 * @return The Java package name of {@code reference} or {@code null}. 478 * 479 * @throws NullPointerException if {@code reference} is {@code null}. 480 * 481 * @see #getJavaPackageName(org.jomc.model.Specification) 482 */ 483 public String getJavaPackageName( final SpecificationReference reference ) 484 { 485 if ( reference == null ) 486 { 487 throw new NullPointerException( "reference" ); 488 } 489 490 final Specification s = this.getModules().getSpecification( reference.getIdentifier() ); 491 assert s != null : "Specification '" + reference.getIdentifier() + "' not found."; 492 return s.getClazz() != null ? this.getJavaPackageName( s ) : null; 493 } 494 495 /** 496 * Gets the name of a Java type of a given specification reference. 497 * 498 * @param reference The specification reference to get a Java type name of. 499 * @param qualified {@code true}, to return the fully qualified type name (with package name prepended); 500 * {@code false}, to return the short type name (without package name prepended). 501 * 502 * @return The Java type name of {@code reference} or {@code null}. 503 * 504 * @throws NullPointerException if {@code reference} is {@code null}. 505 * 506 * @see #getJavaTypeName(org.jomc.model.Specification, boolean) 507 */ 508 public String getJavaTypeName( final SpecificationReference reference, final boolean qualified ) 509 { 510 if ( reference == null ) 511 { 512 throw new NullPointerException( "reference" ); 513 } 514 515 final Specification s = this.getModules().getSpecification( reference.getIdentifier() ); 516 assert s != null : "Specification '" + reference.getIdentifier() + "' not found."; 517 return s.getClazz() != null ? this.getJavaTypeName( s, qualified ) : null; 518 } 519 520 /** 521 * Gets the Java package name of an implementation. 522 * 523 * @param implementation The implementation to get the Java package name of. 524 * 525 * @return The Java package name of {@code implementation} or {@code null}. 526 * 527 * @throws NullPointerException if {@code implementation} is {@code null}. 528 */ 529 public String getJavaPackageName( final Implementation implementation ) 530 { 531 if ( implementation == null ) 532 { 533 throw new NullPointerException( "implementation" ); 534 } 535 536 return implementation.getClazz() != null ? this.getJavaPackageName( implementation.getClazz() ) : null; 537 } 538 539 /** 540 * Gets the Java type name of an implementation. 541 * 542 * @param implementation The implementation to get the Java type name of. 543 * @param qualified {@code true}, to return the fully qualified type name (with package name prepended); 544 * {@code false}, to return the short type name (without package name prepended). 545 * 546 * @return The Java type name of {@code implementation} or {@code null}. 547 * 548 * @throws NullPointerException if {@code implementation} is {@code null}. 549 * 550 * @see #getJavaPackageName(org.jomc.model.Implementation) 551 */ 552 public String getJavaTypeName( final Implementation implementation, final boolean qualified ) 553 { 554 if ( implementation == null ) 555 { 556 throw new NullPointerException( "implementation" ); 557 } 558 559 if ( implementation.getClazz() != null ) 560 { 561 final StringBuilder typeName = new StringBuilder( implementation.getClazz().length() ); 562 final String javaPackageName = this.getJavaPackageName( implementation ); 563 564 if ( qualified && javaPackageName.length() > 0 ) 565 { 566 typeName.append( javaPackageName ).append( '.' ); 567 } 568 569 typeName.append( javaPackageName.length() > 0 570 ? implementation.getClazz().substring( javaPackageName.length() + 1 ) 571 : implementation.getClazz() ); 572 573 return typeName.toString(); 574 } 575 576 return null; 577 } 578 579 /** 580 * Gets the Java class path location of an implementation. 581 * 582 * @param implementation The implementation to return the Java class path location of. 583 * 584 * @return The Java class path location of {@code implementation} or {@code null}. 585 * 586 * @throws NullPointerException if {@code implementation} is {@code null}. 587 */ 588 public String getJavaClasspathLocation( final Implementation implementation ) 589 { 590 if ( implementation == null ) 591 { 592 throw new NullPointerException( "implementation" ); 593 } 594 595 return implementation.getClazz() != null 596 ? ( this.getJavaTypeName( implementation, true ) ).replace( '.', '/' ) 597 : null; 598 599 } 600 601 /** 602 * Gets a list of names of all Java types an implementation implements. 603 * 604 * @param implementation The implementation to get names of all implemented Java types of. 605 * @param qualified {@code true}, to return the fully qualified type names (with package name prepended); 606 * {@code false}, to return the short type names (without package name prepended). 607 * 608 * @return An unmodifiable list of names of all Java types implemented by {@code implementation}. 609 * 610 * @throws NullPointerException if {@code implementation} is {@code null}. 611 * 612 * @deprecated As of JOMC 1.2, replaced by method {@link #getImplementedJavaTypeNames(org.jomc.model.Implementation, boolean)}. 613 * This method will be removed in version 2.0. 614 */ 615 @Deprecated 616 public List<String> getJavaInterfaceNames( final Implementation implementation, final boolean qualified ) 617 { 618 if ( implementation == null ) 619 { 620 throw new NullPointerException( "implementation" ); 621 } 622 623 return this.getImplementedJavaTypeNames( implementation, qualified ); 624 } 625 626 /** 627 * Gets a list of names of all Java types an implementation implements. 628 * 629 * @param implementation The implementation to get names of all implemented Java types of. 630 * @param qualified {@code true}, to return the fully qualified type names (with package name prepended); 631 * {@code false}, to return the short type names (without package name prepended). 632 * 633 * @return An unmodifiable list of names of all Java types implemented by {@code implementation}. 634 * 635 * @throws NullPointerException if {@code implementation} is {@code null}. 636 * 637 * @since 1.2 638 * 639 * @see #getJavaTypeName(org.jomc.model.Specification, boolean) 640 */ 641 public List<String> getImplementedJavaTypeNames( final Implementation implementation, final boolean qualified ) 642 { 643 if ( implementation == null ) 644 { 645 throw new NullPointerException( "implementation" ); 646 } 647 648 final Specifications specs = this.getModules().getSpecifications( implementation.getIdentifier() ); 649 final List<String> col = new ArrayList<String>( specs == null ? 0 : specs.getSpecification().size() ); 650 651 if ( specs != null ) 652 { 653 for ( int i = 0, s0 = specs.getSpecification().size(); i < s0; i++ ) 654 { 655 final Specification s = specs.getSpecification().get( i ); 656 657 if ( s.getClazz() != null ) 658 { 659 final String typeName = this.getJavaTypeName( s, qualified ); 660 if ( !col.contains( typeName ) ) 661 { 662 col.add( typeName ); 663 } 664 } 665 } 666 } 667 668 return Collections.unmodifiableList( col ); 669 } 670 671 /** 672 * Gets the Java type name of an argument. 673 * 674 * @param argument The argument to get the Java type name of. 675 * 676 * @return The Java type name of {@code argument}. 677 * 678 * @throws NullPointerException if {@code argument} is {@code null}. 679 */ 680 public String getJavaTypeName( final Argument argument ) 681 { 682 if ( argument == null ) 683 { 684 throw new NullPointerException( "argument" ); 685 } 686 687 String javaTypeName = "java.lang.String"; 688 689 if ( argument.getType() == ArgumentType.DATE || argument.getType() == ArgumentType.TIME ) 690 { 691 javaTypeName = "java.util.Date"; 692 } 693 else if ( argument.getType() == ArgumentType.NUMBER ) 694 { 695 javaTypeName = "java.lang.Number"; 696 } 697 698 return javaTypeName; 699 } 700 701 /** 702 * Gets a Java method parameter name of an argument. 703 * 704 * @param argument The argument to get the Java method parameter name of. 705 * 706 * @return The Java method parameter name of {@code argument}. 707 * 708 * @throws NullPointerException if {@code argument} is {@code null}. 709 * 710 * @since 1.2 711 */ 712 public String getJavaMethodParameterName( final Argument argument ) 713 { 714 if ( argument == null ) 715 { 716 throw new NullPointerException( "argument" ); 717 } 718 719 return this.getJavaMethodParameterName( argument.getName() ); 720 } 721 722 /** 723 * Gets the Java type name of a property. 724 * 725 * @param property The property to get the Java type name of. 726 * @param boxify {@code true}, to return the name of the Java wrapper class when the type is a Java primitive type; 727 * {@code false}, to return the exact binary name (unboxed name) of the Java type. 728 * 729 * @return The Java type name of {@code property}. 730 * 731 * @throws NullPointerException if {@code property} is {@code null}. 732 */ 733 public String getJavaTypeName( final Property property, final boolean boxify ) 734 { 735 if ( property == null ) 736 { 737 throw new NullPointerException( "property" ); 738 } 739 740 if ( property.getType() != null ) 741 { 742 final String typeName = property.getType(); 743 744 if ( boxify ) 745 { 746 if ( Boolean.TYPE.getName().equals( typeName ) ) 747 { 748 return Boolean.class.getName(); 749 } 750 if ( Byte.TYPE.getName().equals( typeName ) ) 751 { 752 return Byte.class.getName(); 753 } 754 if ( Character.TYPE.getName().equals( typeName ) ) 755 { 756 return Character.class.getName(); 757 } 758 if ( Double.TYPE.getName().equals( typeName ) ) 759 { 760 return Double.class.getName(); 761 } 762 if ( Float.TYPE.getName().equals( typeName ) ) 763 { 764 return Float.class.getName(); 765 } 766 if ( Integer.TYPE.getName().equals( typeName ) ) 767 { 768 return Integer.class.getName(); 769 } 770 if ( Long.TYPE.getName().equals( typeName ) ) 771 { 772 return Long.class.getName(); 773 } 774 if ( Short.TYPE.getName().equals( typeName ) ) 775 { 776 return Short.class.getName(); 777 } 778 } 779 780 return typeName; 781 } 782 783 return property.getAny() != null ? Object.class.getName() : String.class.getName(); 784 } 785 786 /** 787 * Gets a flag indicating the type of a given property is a Java primitive. 788 * 789 * @param property The property to query. 790 * 791 * @return {@code true}, if the Java type of {@code property} is primitive; {@code false}, if not. 792 * 793 * @throws NullPointerException if {@code property} is {@code null}. 794 * 795 * @see #getJavaTypeName(org.jomc.model.Property, boolean) 796 */ 797 public boolean isJavaPrimitiveType( final Property property ) 798 { 799 if ( property == null ) 800 { 801 throw new NullPointerException( "property" ); 802 } 803 804 return !this.getJavaTypeName( property, false ).equals( this.getJavaTypeName( property, true ) ); 805 } 806 807 /** 808 * Gets the name of a Java getter method of a given property. 809 * 810 * @param property The property to get a Java getter method name of. 811 * 812 * @return The Java getter method name of {@code property}. 813 * 814 * @throws NullPointerException if {@code property} is {@code null}. 815 * 816 * @see #getJavaIdentifier(java.lang.String, boolean) 817 */ 818 public String getJavaGetterMethodName( final Property property ) 819 { 820 if ( property == null ) 821 { 822 throw new NullPointerException( "property" ); 823 } 824 825 String prefix = "get"; 826 827 final String javaTypeName = this.getJavaTypeName( property, true ); 828 if ( Boolean.class.getName().equals( javaTypeName ) ) 829 { 830 prefix = "is"; 831 } 832 833 return prefix + this.getJavaIdentifier( property.getName(), true ); 834 } 835 836 /** 837 * Gets the name of a Java setter method of a given property. 838 * 839 * @param property The property to get a Java setter method name of. 840 * 841 * @return The Java setter method name of {@code property}. 842 * 843 * @throws NullPointerException if {@code property} is {@code null}. 844 * 845 * @see #getJavaIdentifier(java.lang.String, boolean) 846 * 847 * @since 1.2 848 */ 849 public String getJavaSetterMethodName( final Property property ) 850 { 851 if ( property == null ) 852 { 853 throw new NullPointerException( "property" ); 854 } 855 856 return "set" + this.getJavaIdentifier( property.getName(), true ); 857 } 858 859 /** 860 * Gets a Java method parameter name of a property. 861 * 862 * @param property The property to get the Java method parameter name of. 863 * 864 * @return The Java method parameter name of {@code property}. 865 * 866 * @throws NullPointerException if {@code property} is {@code null}. 867 * 868 * @since 1.2 869 */ 870 public String getJavaMethodParameterName( final Property property ) 871 { 872 if ( property == null ) 873 { 874 throw new NullPointerException( "property" ); 875 } 876 877 return this.getJavaMethodParameterName( property.getName() ); 878 } 879 880 /** 881 * Gets the name of a Java type of a given dependency. 882 * 883 * @param dependency The dependency to get a dependency Java type name of. 884 * 885 * @return The Java type name of {@code dependency} or {@code null}. 886 * 887 * @throws NullPointerException if {@code dependency} is {@code null}. 888 * 889 * @see #getJavaTypeName(org.jomc.model.Specification, boolean) 890 */ 891 public String getJavaTypeName( final Dependency dependency ) 892 { 893 if ( dependency == null ) 894 { 895 throw new NullPointerException( "dependency" ); 896 } 897 898 final Specification s = this.getModules().getSpecification( dependency.getIdentifier() ); 899 900 if ( s != null && s.getClazz() != null ) 901 { 902 final StringBuilder typeName = new StringBuilder( s.getClazz().length() ); 903 typeName.append( this.getJavaTypeName( s, true ) ); 904 if ( s.getMultiplicity() == Multiplicity.MANY && dependency.getImplementationName() == null ) 905 { 906 typeName.append( "[]" ); 907 } 908 909 return typeName.toString(); 910 } 911 912 return null; 913 } 914 915 /** 916 * Gets the name of a Java getter method of a given dependency. 917 * 918 * @param dependency The dependency to get a Java getter method name of. 919 * 920 * @return The Java getter method name of {@code dependency}. 921 * 922 * @throws NullPointerException if {@code dependency} is {@code null}. 923 * 924 * @see #getJavaIdentifier(java.lang.String, boolean) 925 */ 926 public String getJavaGetterMethodName( final Dependency dependency ) 927 { 928 if ( dependency == null ) 929 { 930 throw new NullPointerException( "dependency" ); 931 } 932 933 return "get" + this.getJavaIdentifier( dependency.getName(), true ); 934 } 935 936 /** 937 * Gets the name of a Java setter method of a given dependency. 938 * 939 * @param dependency The dependency to get a Java setter method name of. 940 * 941 * @return The Java setter method name of {@code dependency}. 942 * 943 * @throws NullPointerException if {@code dependency} is {@code null}. 944 * 945 * @see #getJavaIdentifier(java.lang.String, boolean) 946 * 947 * @since 1.2 948 */ 949 public String getJavaSetterMethodName( final Dependency dependency ) 950 { 951 if ( dependency == null ) 952 { 953 throw new NullPointerException( "dependency" ); 954 } 955 956 return "set" + this.getJavaIdentifier( dependency.getName(), true ); 957 } 958 959 /** 960 * Gets a Java method parameter name of a dependency. 961 * 962 * @param dependency The dependency to get the Java method parameter name of. 963 * 964 * @return The Java method parameter name of {@code dependency}. 965 * 966 * @throws NullPointerException if {@code dependency} is {@code null}. 967 * 968 * @since 1.2 969 */ 970 public String getJavaMethodParameterName( final Dependency dependency ) 971 { 972 if ( dependency == null ) 973 { 974 throw new NullPointerException( "dependency" ); 975 } 976 977 return this.getJavaMethodParameterName( dependency.getName() ); 978 } 979 980 /** 981 * Gets the name of a Java getter method of a given message. 982 * 983 * @param message The message to get a Java getter method name of. 984 * 985 * @return The Java getter method name of {@code message}. 986 * 987 * @throws NullPointerException if {@code message} is {@code null}. 988 * 989 * @see #getJavaIdentifier(java.lang.String, boolean) 990 */ 991 public String getJavaGetterMethodName( final Message message ) 992 { 993 if ( message == null ) 994 { 995 throw new NullPointerException( "message" ); 996 } 997 998 return "get" + this.getJavaIdentifier( message.getName(), true ); 999 } 1000 1001 /** 1002 * Gets the name of a Java setter method of a given message. 1003 * 1004 * @param message The message to get a Java setter method name of. 1005 * 1006 * @return The Java setter method name of {@code message}. 1007 * 1008 * @throws NullPointerException if {@code message} is {@code null}. 1009 * 1010 * @see #getJavaIdentifier(java.lang.String, boolean) 1011 * 1012 * @since 1.2 1013 */ 1014 public String getJavaSetterMethodName( final Message message ) 1015 { 1016 if ( message == null ) 1017 { 1018 throw new NullPointerException( "message" ); 1019 } 1020 1021 return "set" + this.getJavaIdentifier( message.getName(), true ); 1022 } 1023 1024 /** 1025 * Gets a Java method parameter name of a message. 1026 * 1027 * @param message The message to get the Java method parameter name of. 1028 * 1029 * @return The Java method parameter name of {@code message}. 1030 * 1031 * @throws NullPointerException if {@code message} is {@code null}. 1032 * 1033 * @since 1.2 1034 */ 1035 public String getJavaMethodParameterName( final Message message ) 1036 { 1037 if ( message == null ) 1038 { 1039 throw new NullPointerException( "message" ); 1040 } 1041 1042 return this.getJavaMethodParameterName( message.getName() ); 1043 } 1044 1045 /** 1046 * Gets the Java modifier name of a dependency of a given implementation. 1047 * 1048 * @param implementation The implementation declaring the dependency to get a Java modifier name of. 1049 * @param dependency The dependency to get a Java modifier name of. 1050 * 1051 * @return The Java modifier name of {@code dependency} of {@code implementation}. 1052 * 1053 * @throws NullPointerException if {@code implementation} or {@code dependency} is {@code null}. 1054 */ 1055 public String getJavaModifierName( final Implementation implementation, final Dependency dependency ) 1056 { 1057 if ( implementation == null ) 1058 { 1059 throw new NullPointerException( "implementation" ); 1060 } 1061 if ( dependency == null ) 1062 { 1063 throw new NullPointerException( "dependency" ); 1064 } 1065 1066 return "private"; 1067 } 1068 1069 /** 1070 * Gets the Java modifier name of a message of a given implementation. 1071 * 1072 * @param implementation The implementation declaring the message to get a Java modifier name of. 1073 * @param message The message to get a Java modifier name of. 1074 * 1075 * @return The Java modifier name of {@code message} of {@code implementation}. 1076 * 1077 * @throws NullPointerException if {@code implementation} or {@code message} is {@code null}. 1078 */ 1079 public String getJavaModifierName( final Implementation implementation, final Message message ) 1080 { 1081 if ( implementation == null ) 1082 { 1083 throw new NullPointerException( "implementation" ); 1084 } 1085 if ( message == null ) 1086 { 1087 throw new NullPointerException( "message" ); 1088 } 1089 1090 return "private"; 1091 } 1092 1093 /** 1094 * Gets the Java modifier name of a property of a given implementation. 1095 * 1096 * @param implementation The implementation declaring the property to get a Java modifier name of. 1097 * @param property The property to get a Java modifier name of. 1098 * 1099 * @return The Java modifier name of {@code property} of {@code implementation}. 1100 * 1101 * @throws NullPointerException if {@code implementation} or {@code property} is {@code null}. 1102 */ 1103 public String getJavaModifierName( final Implementation implementation, final Property property ) 1104 { 1105 if ( implementation == null ) 1106 { 1107 throw new NullPointerException( "implementation" ); 1108 } 1109 if ( property == null ) 1110 { 1111 throw new NullPointerException( "property" ); 1112 } 1113 1114 String modifier = "private"; 1115 final Properties specified = this.getModules().getSpecifiedProperties( implementation.getIdentifier() ); 1116 1117 if ( specified != null && specified.getProperty( property.getName() ) != null ) 1118 { 1119 modifier = "public"; 1120 } 1121 1122 return modifier; 1123 } 1124 1125 /** 1126 * Formats a text to a Javadoc comment. 1127 * 1128 * @param text The text to format to a Javadoc comment. 1129 * @param indentationLevel The indentation level of the comment. 1130 * @param linePrefix The text to prepend lines with. 1131 * 1132 * @return {@code text} formatted to a Javadoc comment. 1133 * 1134 * @throws NullPointerException if {@code text} or {@code linePrefix} is {@code null}. 1135 * @throws IllegalArgumentException if {@code indentationLevel} is negative. 1136 */ 1137 public String getJavadocComment( final Text text, final int indentationLevel, final String linePrefix ) 1138 { 1139 if ( text == null ) 1140 { 1141 throw new NullPointerException( "text" ); 1142 } 1143 if ( linePrefix == null ) 1144 { 1145 throw new NullPointerException( "linePrefix" ); 1146 } 1147 if ( indentationLevel < 0 ) 1148 { 1149 throw new IllegalArgumentException( Integer.toString( indentationLevel ) ); 1150 } 1151 1152 BufferedReader reader = null; 1153 boolean suppressExceptionOnClose = true; 1154 1155 try 1156 { 1157 String javadoc = ""; 1158 1159 if ( text.getValue() != null ) 1160 { 1161 final String indent = this.getIndentation( indentationLevel ); 1162 reader = new BufferedReader( new StringReader( text.getValue() ) ); 1163 final StringBuilder builder = new StringBuilder( text.getValue().length() ); 1164 1165 String line; 1166 while ( ( line = reader.readLine() ) != null ) 1167 { 1168 builder.append( this.getLineSeparator() ).append( indent ).append( linePrefix ). 1169 append( line.replaceAll( "\\/\\*\\*", "/*" ).replaceAll( "\\*/", "/" ) ); 1170 1171 } 1172 1173 if ( builder.length() > 0 ) 1174 { 1175 javadoc = 1176 builder.substring( this.getLineSeparator().length() + indent.length() + linePrefix.length() ); 1177 1178 if ( !new MimeType( text.getType() ).match( "text/html" ) ) 1179 { 1180 javadoc = StringEscapeUtils.escapeHtml( javadoc ); 1181 } 1182 } 1183 } 1184 1185 suppressExceptionOnClose = false; 1186 return javadoc; 1187 } 1188 catch ( final MimeTypeParseException e ) 1189 { 1190 throw new AssertionError( e ); 1191 } 1192 catch ( final IOException e ) 1193 { 1194 throw new AssertionError( e ); 1195 } 1196 finally 1197 { 1198 try 1199 { 1200 if ( reader != null ) 1201 { 1202 reader.close(); 1203 } 1204 } 1205 catch ( final IOException e ) 1206 { 1207 if ( suppressExceptionOnClose ) 1208 { 1209 this.log( Level.SEVERE, getMessage( e ), e ); 1210 } 1211 else 1212 { 1213 throw new AssertionError( e ); 1214 } 1215 } 1216 } 1217 } 1218 1219 /** 1220 * Formats a text from a list of texts to a Javadoc comment. 1221 * 1222 * @param texts The list of texts to format to a Javadoc comment. 1223 * @param indentationLevel The indentation level of the comment. 1224 * @param linePrefix The text to prepend lines with. 1225 * 1226 * @return The text corresponding to the locale of the instance from the list of texts formatted to a Javadoc 1227 * comment. 1228 * 1229 * @throws NullPointerException if {@code texts} or {@code linePrefix} is {@code null}. 1230 * @throws IllegalArgumentException if {@code indentationLevel} is negative. 1231 * 1232 * @see #getLocale() 1233 * 1234 * @since 1.2 1235 */ 1236 public String getJavadocComment( final Texts texts, final int indentationLevel, final String linePrefix ) 1237 { 1238 if ( texts == null ) 1239 { 1240 throw new NullPointerException( "texts" ); 1241 } 1242 if ( linePrefix == null ) 1243 { 1244 throw new NullPointerException( "linePrefix" ); 1245 } 1246 if ( indentationLevel < 0 ) 1247 { 1248 throw new IllegalArgumentException( Integer.toString( indentationLevel ) ); 1249 } 1250 1251 return this.getJavadocComment( texts.getText( this.getLocale().getLanguage() ), indentationLevel, linePrefix ); 1252 } 1253 1254 /** 1255 * Formats a string to a Java string with unicode escapes. 1256 * 1257 * @param str The string to format to a Java string or {@code null}. 1258 * 1259 * @return {@code str} formatted to a Java string or {@code null}. 1260 * 1261 * @see StringEscapeUtils#escapeJava(java.lang.String) 1262 */ 1263 public String getJavaString( final String str ) 1264 { 1265 return StringEscapeUtils.escapeJava( str ); 1266 } 1267 1268 /** 1269 * Formats a string to a Java identifier. 1270 * 1271 * @param str The string to format or {@code null}. 1272 * @param capitalize {@code true}, to return an identifier with the first character upper cased; {@code false}, to 1273 * return an identifier with the first character lower cased. 1274 * 1275 * @return {@code str} formatted to a Java identifier or {@code null}. 1276 * 1277 * @since 1.2 1278 */ 1279 public String getJavaIdentifier( final String str, final boolean capitalize ) 1280 { 1281 String identifier = null; 1282 1283 if ( str != null ) 1284 { 1285 final int len = str.length(); 1286 final StringBuilder builder = new StringBuilder( len ); 1287 boolean uc = capitalize; 1288 1289 for ( int i = 0; i < len; i++ ) 1290 { 1291 final char c = str.charAt( i ); 1292 final String charString = Character.toString( c ); 1293 1294 if ( builder.length() > 0 ) 1295 { 1296 if ( Character.isJavaIdentifierPart( c ) ) 1297 { 1298 builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString ); 1299 uc = false; 1300 } 1301 else 1302 { 1303 uc = true; 1304 } 1305 } 1306 else 1307 { 1308 if ( Character.isJavaIdentifierStart( c ) ) 1309 { 1310 builder.append( uc ? charString.toUpperCase( this.getLocale() ) 1311 : charString.toLowerCase( this.getLocale() ) ); 1312 1313 uc = false; 1314 } 1315 else 1316 { 1317 uc = capitalize; 1318 } 1319 } 1320 } 1321 1322 identifier = builder.toString(); 1323 1324 if ( identifier.length() <= 0 && this.isLoggable( Level.WARNING ) ) 1325 { 1326 this.log( Level.WARNING, getMessage( "invalidJavaIdentifier", str ), null ); 1327 } 1328 } 1329 1330 return identifier; 1331 } 1332 1333 /** 1334 * Formats a string to a Java method parameter name. 1335 * 1336 * @param str The string to format or {@code null}. 1337 * 1338 * @return {@code str} formatted to a Java method parameter name or {@code null}. 1339 * 1340 * @since 1.3 1341 */ 1342 private String getJavaMethodParameterName( final String str ) 1343 { 1344 String methodParameterName = null; 1345 1346 if ( str != null ) 1347 { 1348 final int len = str.length(); 1349 final StringBuilder builder = new StringBuilder( len ); 1350 boolean uc = false; 1351 1352 for ( int i = 0; i < len; i++ ) 1353 { 1354 final char c = str.charAt( i ); 1355 final String charString = Character.toString( c ); 1356 1357 if ( builder.length() > 0 ) 1358 { 1359 if ( Character.isJavaIdentifierPart( c ) ) 1360 { 1361 builder.append( uc ? charString.toUpperCase( this.getLocale() ) : charString ); 1362 uc = false; 1363 } 1364 else 1365 { 1366 uc = true; 1367 } 1368 } 1369 else if ( Character.isJavaIdentifierStart( c ) ) 1370 { 1371 builder.append( charString.toLowerCase( this.getLocale() ) ); 1372 } 1373 } 1374 1375 methodParameterName = builder.toString(); 1376 1377 if ( methodParameterName.length() <= 0 && this.isLoggable( Level.WARNING ) ) 1378 { 1379 this.log( Level.WARNING, getMessage( "invalidJavaMethodParameterName", str ), null ); 1380 } 1381 1382 if ( this.getJavaKeywords().contains( methodParameterName ) ) 1383 { 1384 methodParameterName = "_" + methodParameterName; 1385 } 1386 } 1387 1388 return methodParameterName; 1389 } 1390 1391 /** 1392 * Gets a flag indicating the class of a given specification is located in the Java default package. 1393 * 1394 * @param specification The specification to query. 1395 * 1396 * @return {@code true}, if the class of {@code specification} is located in the Java default package; 1397 * {@code false}, else. 1398 * 1399 * @throws NullPointerException if {@code specification} is {@code null}. 1400 */ 1401 public boolean isJavaDefaultPackage( final Specification specification ) 1402 { 1403 if ( specification == null ) 1404 { 1405 throw new NullPointerException( "specification" ); 1406 } 1407 1408 return specification.getClazz() != null && this.getJavaPackageName( specification ).length() == 0; 1409 } 1410 1411 /** 1412 * Gets a flag indicating the class of a given implementation is located in the Java default package. 1413 * 1414 * @param implementation The implementation to query. 1415 * 1416 * @return {@code true}, if the class of {@code implementation} is located in the Java default package; 1417 * {@code false}, else. 1418 * 1419 * @throws NullPointerException if {@code implementation} is {@code null}. 1420 */ 1421 public boolean isJavaDefaultPackage( final Implementation implementation ) 1422 { 1423 if ( implementation == null ) 1424 { 1425 throw new NullPointerException( "implementation" ); 1426 } 1427 1428 return implementation.getClazz() != null && this.getJavaPackageName( implementation ).length() == 0; 1429 } 1430 1431 /** 1432 * Formats a string to a HTML string with HTML entities. 1433 * 1434 * @param str The string to format to a HTML string with HTML entities or {@code null}. 1435 * 1436 * @return {@code str} formatted to a HTML string with HTML entities or {@code null}. 1437 * 1438 * @see StringEscapeUtils#escapeHtml(java.lang.String) 1439 * 1440 * @since 1.2 1441 */ 1442 public String getHtmlString( final String str ) 1443 { 1444 return StringEscapeUtils.escapeHtml( str ); 1445 } 1446 1447 /** 1448 * Formats a string to a XML string with XML entities. 1449 * 1450 * @param str The string to format to a XML string with XML entities or {@code null}. 1451 * 1452 * @return {@code str} formatted to a XML string with XML entities or {@code null}. 1453 * 1454 * @see StringEscapeUtils#escapeXml(java.lang.String) 1455 * 1456 * @since 1.2 1457 */ 1458 public String getXmlString( final String str ) 1459 { 1460 return StringEscapeUtils.escapeXml( str ); 1461 } 1462 1463 /** 1464 * Formats a string to a JavaScript string applying JavaScript string rules. 1465 * 1466 * @param str The string to format to a JavaScript string by applying JavaScript string rules or {@code null}. 1467 * 1468 * @return {@code str} formatted to a JavaScript string with JavaScript string rules applied or {@code null}. 1469 * 1470 * @see StringEscapeUtils#escapeJavaScript(java.lang.String) 1471 * 1472 * @since 1.2 1473 */ 1474 public String getJavaScriptString( final String str ) 1475 { 1476 return StringEscapeUtils.escapeJavaScript( str ); 1477 } 1478 1479 /** 1480 * Formats a string to a SQL string. 1481 * 1482 * @param str The string to format to a SQL string or {@code null}. 1483 * 1484 * @return {@code str} formatted to a SQL string or {@code null}. 1485 * 1486 * @see StringEscapeUtils#escapeSql(java.lang.String) 1487 * 1488 * @since 1.2 1489 */ 1490 public String getSqlString( final String str ) 1491 { 1492 return StringEscapeUtils.escapeSql( str ); 1493 } 1494 1495 /** 1496 * Formats a string to a CSV string. 1497 * 1498 * @param str The string to format to a CSV string or {@code null}. 1499 * 1500 * @return {@code str} formatted to a CSV string or {@code null}. 1501 * 1502 * @see StringEscapeUtils#escapeCsv(java.lang.String) 1503 * 1504 * @since 1.2 1505 */ 1506 public String getCsvString( final String str ) 1507 { 1508 return StringEscapeUtils.escapeCsv( str ); 1509 } 1510 1511 /** 1512 * Formats a {@code Boolean} to a string. 1513 * 1514 * @param b The {@code Boolean} to format to a string or {@code null}. 1515 * 1516 * @return {@code b} formatted to a string. 1517 * 1518 * @see #getLocale() 1519 * 1520 * @since 1.2 1521 */ 1522 public String getBooleanString( final Boolean b ) 1523 { 1524 final MessageFormat messageFormat = new MessageFormat( ResourceBundle.getBundle( 1525 JomcTool.class.getName().replace( '.', '/' ), this.getLocale() ). 1526 getString( b ? "booleanStringTrue" : "booleanStringFalse" ), this.getLocale() ); 1527 1528 return messageFormat.format( null ); 1529 } 1530 1531 /** 1532 * Gets the display language of a given language code. 1533 * 1534 * @param language The language code to get the display language of. 1535 * 1536 * @return The display language of {@code language}. 1537 * 1538 * @throws NullPointerException if {@code language} is {@code null}. 1539 */ 1540 public String getDisplayLanguage( final String language ) 1541 { 1542 if ( language == null ) 1543 { 1544 throw new NullPointerException( "language" ); 1545 } 1546 1547 final Locale l = new Locale( language ); 1548 return l.getDisplayLanguage( l ); 1549 } 1550 1551 /** 1552 * Formats a calendar instance to a string. 1553 * 1554 * @param calendar The calendar to format to a string. 1555 * 1556 * @return The date of {@code calendar} formatted using a short format style pattern. 1557 * 1558 * @throws NullPointerException if {@code calendar} is {@code null}. 1559 * 1560 * @see DateFormat#SHORT 1561 */ 1562 public String getShortDate( final Calendar calendar ) 1563 { 1564 if ( calendar == null ) 1565 { 1566 throw new NullPointerException( "calendar" ); 1567 } 1568 1569 return DateFormat.getDateInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() ); 1570 } 1571 1572 /** 1573 * Formats a calendar instance to a string. 1574 * 1575 * @param calendar The calendar to format to a string. 1576 * 1577 * @return The date of {@code calendar} formatted using a medium format style pattern. 1578 * 1579 * @throws NullPointerException if {@code calendar} is {@code null}. 1580 * 1581 * @see DateFormat#MEDIUM 1582 * 1583 * @since 1.2 1584 */ 1585 public String getMediumDate( final Calendar calendar ) 1586 { 1587 if ( calendar == null ) 1588 { 1589 throw new NullPointerException( "calendar" ); 1590 } 1591 1592 return DateFormat.getDateInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() ); 1593 } 1594 1595 /** 1596 * Formats a calendar instance to a string. 1597 * 1598 * @param calendar The calendar to format to a string. 1599 * 1600 * @return The date of {@code calendar} formatted using a long format style pattern. 1601 * 1602 * @throws NullPointerException if {@code calendar} is {@code null}. 1603 * 1604 * @see DateFormat#LONG 1605 */ 1606 public String getLongDate( final Calendar calendar ) 1607 { 1608 if ( calendar == null ) 1609 { 1610 throw new NullPointerException( "calendar" ); 1611 } 1612 1613 return DateFormat.getDateInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() ); 1614 } 1615 1616 /** 1617 * Formats a calendar instance to a string. 1618 * 1619 * @param calendar The calendar to format to a string. 1620 * 1621 * @return The date of {@code calendar} formatted using an ISO-8601 format style. 1622 * 1623 * @throws NullPointerException if {@code calendar} is {@code null}. 1624 * 1625 * @see SimpleDateFormat yyyy-DDD 1626 * 1627 * @since 1.2 1628 */ 1629 public String getIsoDate( final Calendar calendar ) 1630 { 1631 if ( calendar == null ) 1632 { 1633 throw new NullPointerException( "calendar" ); 1634 } 1635 1636 return new SimpleDateFormat( "yyyy-DDD", this.getLocale() ).format( calendar.getTime() ); 1637 } 1638 1639 /** 1640 * Formats a calendar instance to a string. 1641 * 1642 * @param calendar The calendar to format to a string. 1643 * 1644 * @return The time of {@code calendar} formatted using a short format style pattern. 1645 * 1646 * @throws NullPointerException if {@code calendar} is {@code null}. 1647 * 1648 * @see DateFormat#SHORT 1649 */ 1650 public String getShortTime( final Calendar calendar ) 1651 { 1652 if ( calendar == null ) 1653 { 1654 throw new NullPointerException( "calendar" ); 1655 } 1656 1657 return DateFormat.getTimeInstance( DateFormat.SHORT, this.getLocale() ).format( calendar.getTime() ); 1658 } 1659 1660 /** 1661 * Formats a calendar instance to a string. 1662 * 1663 * @param calendar The calendar to format to a string. 1664 * 1665 * @return The time of {@code calendar} formatted using a medium format style pattern. 1666 * 1667 * @throws NullPointerException if {@code calendar} is {@code null}. 1668 * 1669 * @see DateFormat#MEDIUM 1670 * 1671 * @since 1.2 1672 */ 1673 public String getMediumTime( final Calendar calendar ) 1674 { 1675 if ( calendar == null ) 1676 { 1677 throw new NullPointerException( "calendar" ); 1678 } 1679 1680 return DateFormat.getTimeInstance( DateFormat.MEDIUM, this.getLocale() ).format( calendar.getTime() ); 1681 } 1682 1683 /** 1684 * Formats a calendar instance to a string. 1685 * 1686 * @param calendar The calendar to format to a string. 1687 * 1688 * @return The time of {@code calendar} formatted using a long format style pattern. 1689 * 1690 * @throws NullPointerException if {@code calendar} is {@code null}. 1691 * 1692 * @see DateFormat#LONG 1693 */ 1694 public String getLongTime( final Calendar calendar ) 1695 { 1696 if ( calendar == null ) 1697 { 1698 throw new NullPointerException( "calendar" ); 1699 } 1700 1701 return DateFormat.getTimeInstance( DateFormat.LONG, this.getLocale() ).format( calendar.getTime() ); 1702 } 1703 1704 /** 1705 * Formats a calendar instance to a string. 1706 * 1707 * @param calendar The calendar to format to a string. 1708 * 1709 * @return The time of {@code calendar} formatted using an ISO-8601 format style. 1710 * 1711 * @throws NullPointerException if {@code calendar} is {@code null}. 1712 * 1713 * @see SimpleDateFormat HH:mm 1714 * 1715 * @since 1.2 1716 */ 1717 public String getIsoTime( final Calendar calendar ) 1718 { 1719 if ( calendar == null ) 1720 { 1721 throw new NullPointerException( "calendar" ); 1722 } 1723 1724 return new SimpleDateFormat( "HH:mm", this.getLocale() ).format( calendar.getTime() ); 1725 } 1726 1727 /** 1728 * Formats a calendar instance to a string. 1729 * 1730 * @param calendar The calendar to format to a string. 1731 * 1732 * @return The date and time of {@code calendar} formatted using a short format style pattern. 1733 * 1734 * @throws NullPointerException if {@code calendar} is {@code null}. 1735 * 1736 * @see DateFormat#SHORT 1737 */ 1738 public String getShortDateTime( final Calendar calendar ) 1739 { 1740 if ( calendar == null ) 1741 { 1742 throw new NullPointerException( "calendar" ); 1743 } 1744 1745 return DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT, this.getLocale() ). 1746 format( calendar.getTime() ); 1747 1748 } 1749 1750 /** 1751 * Formats a calendar instance to a string. 1752 * 1753 * @param calendar The calendar to format to a string. 1754 * 1755 * @return The date and time of {@code calendar} formatted using a medium format style pattern. 1756 * 1757 * @throws NullPointerException if {@code calendar} is {@code null}. 1758 * 1759 * @see DateFormat#MEDIUM 1760 * 1761 * @since 1.2 1762 */ 1763 public String getMediumDateTime( final Calendar calendar ) 1764 { 1765 if ( calendar == null ) 1766 { 1767 throw new NullPointerException( "calendar" ); 1768 } 1769 1770 return DateFormat.getDateTimeInstance( DateFormat.MEDIUM, DateFormat.MEDIUM, this.getLocale() ). 1771 format( calendar.getTime() ); 1772 1773 } 1774 1775 /** 1776 * Formats a calendar instance to a string. 1777 * 1778 * @param calendar The calendar to format to a string. 1779 * 1780 * @return The date and time of {@code calendar} formatted using a long format style pattern. 1781 * 1782 * @throws NullPointerException if {@code calendar} is {@code null}. 1783 * 1784 * @see DateFormat#LONG 1785 */ 1786 public String getLongDateTime( final Calendar calendar ) 1787 { 1788 if ( calendar == null ) 1789 { 1790 throw new NullPointerException( "calendar" ); 1791 } 1792 1793 return DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG, this.getLocale() ). 1794 format( calendar.getTime() ); 1795 1796 } 1797 1798 /** 1799 * Formats a calendar instance to a string. 1800 * 1801 * @param calendar The calendar to format to a string. 1802 * 1803 * @return The date and time of {@code calendar} formatted using a ISO-8601 format style. 1804 * 1805 * @throws NullPointerException if {@code calendar} is {@code null}. 1806 * 1807 * @see SimpleDateFormat yyyy-MM-dd'T'HH:mm:ssZ 1808 * 1809 * @since 1.2 1810 */ 1811 public String getIsoDateTime( final Calendar calendar ) 1812 { 1813 if ( calendar == null ) 1814 { 1815 throw new NullPointerException( "calendar" ); 1816 } 1817 1818 // JDK: As of JDK 7, "yyyy-MM-dd'T'HH:mm:ssXXX". 1819 return new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssZ", this.getLocale() ).format( calendar.getTime() ); 1820 } 1821 1822 /** 1823 * Gets a string describing the range of years for given calendars. 1824 * 1825 * @param start The start of the range. 1826 * @param end The end of the range. 1827 * 1828 * @return Formatted range of the years of {@code start} and {@code end} (e.g. {@code "start - end"}). 1829 * 1830 * @throws NullPointerException if {@code start} or {@code end} is {@code null}. 1831 */ 1832 public String getYears( final Calendar start, final Calendar end ) 1833 { 1834 if ( start == null ) 1835 { 1836 throw new NullPointerException( "start" ); 1837 } 1838 if ( end == null ) 1839 { 1840 throw new NullPointerException( "end" ); 1841 } 1842 1843 final Format yearFormat = new SimpleDateFormat( "yyyy", this.getLocale() ); 1844 final int s = start.get( Calendar.YEAR ); 1845 final int e = end.get( Calendar.YEAR ); 1846 final StringBuilder years = new StringBuilder(); 1847 1848 if ( s != e ) 1849 { 1850 if ( s < e ) 1851 { 1852 years.append( yearFormat.format( start.getTime() ) ).append( " - " ). 1853 append( yearFormat.format( end.getTime() ) ); 1854 1855 } 1856 else 1857 { 1858 years.append( yearFormat.format( end.getTime() ) ).append( " - " ). 1859 append( yearFormat.format( start.getTime() ) ); 1860 1861 } 1862 } 1863 else 1864 { 1865 years.append( yearFormat.format( start.getTime() ) ); 1866 } 1867 1868 return years.toString(); 1869 } 1870 1871 /** 1872 * Gets the model of the instance. 1873 * 1874 * @return The model of the instance. 1875 * 1876 * @see #getModules() 1877 * @see #setModel(org.jomc.modlet.Model) 1878 */ 1879 public final Model getModel() 1880 { 1881 if ( this.model == null ) 1882 { 1883 this.model = new Model(); 1884 this.model.setIdentifier( ModelObject.MODEL_PUBLIC_ID ); 1885 } 1886 1887 return this.model; 1888 } 1889 1890 /** 1891 * Sets the model of the instance. 1892 * 1893 * @param value The new model of the instance or {@code null}. 1894 * 1895 * @see #getModel() 1896 */ 1897 public final void setModel( final Model value ) 1898 { 1899 this.model = value; 1900 } 1901 1902 /** 1903 * Gets the modules of the instance. 1904 * 1905 * @return The modules of the instance. 1906 * 1907 * @see #getModel() 1908 * @see #setModel(org.jomc.modlet.Model) 1909 * 1910 * @deprecated As of JOMC 1.2, please use method {@link #getModel()} and {@link ModelHelper#getModules(org.jomc.modlet.Model)}. 1911 * This method will be removed in version 2.0. 1912 */ 1913 @Deprecated 1914 public Modules getModules() 1915 { 1916 Modules modules = ModelHelper.getModules( this.getModel() ); 1917 1918 if ( modules == null ) 1919 { 1920 modules = new Modules(); 1921 ModelHelper.setModules( this.getModel(), modules ); 1922 } 1923 1924 return modules; 1925 } 1926 1927 /** 1928 * Gets the {@code VelocityEngine} of the instance. 1929 * 1930 * @return The {@code VelocityEngine} of the instance. 1931 * 1932 * @throws IOException if initializing a new velocity engine fails. 1933 * 1934 * @see #setVelocityEngine(org.apache.velocity.app.VelocityEngine) 1935 */ 1936 public final VelocityEngine getVelocityEngine() throws IOException 1937 { 1938 if ( this.velocityEngine == null ) 1939 { 1940 /** {@code LogChute} logging to the listeners of the tool. */ 1941 class JomcLogChute implements LogChute 1942 { 1943 1944 JomcLogChute() 1945 { 1946 super(); 1947 } 1948 1949 public void init( final RuntimeServices runtimeServices ) throws Exception 1950 { 1951 } 1952 1953 public void log( final int level, final String message ) 1954 { 1955 this.log( level, message, null ); 1956 } 1957 1958 public void log( final int level, final String message, final Throwable throwable ) 1959 { 1960 JomcTool.this.log( Level.FINEST, message, throwable ); 1961 } 1962 1963 public boolean isLevelEnabled( final int level ) 1964 { 1965 return isLoggable( Level.FINEST ); 1966 } 1967 1968 } 1969 1970 final VelocityEngine engine = new VelocityEngine(); 1971 engine.setProperty( RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE.toString() ); 1972 engine.setProperty( RuntimeConstants.VM_ARGUMENTS_STRICT, Boolean.TRUE.toString() ); 1973 engine.setProperty( RuntimeConstants.STRICT_MATH, Boolean.TRUE.toString() ); 1974 engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new JomcLogChute() ); 1975 1976 engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class" ); 1977 engine.setProperty( "class.resource.loader.class", ClasspathResourceLoader.class.getName() ); 1978 engine.setProperty( "class.resource.loader.cache", Boolean.TRUE.toString() ); 1979 1980 if ( this.getTemplateLocation() != null ) 1981 { 1982 engine.setProperty( RuntimeConstants.RESOURCE_LOADER, "class,url" ); 1983 engine.setProperty( "url.resource.loader.class", URLResourceLoader.class.getName() ); 1984 engine.setProperty( "url.resource.loader.cache", Boolean.TRUE.toString() ); 1985 engine.setProperty( "url.resource.loader.root", this.getTemplateLocation().toExternalForm() ); 1986 engine.setProperty( "url.resource.loader.timeout", Integer.toString( 60000 ) ); 1987 } 1988 1989 this.velocityEngine = engine; 1990 this.defaultVelocityEngine = true; 1991 } 1992 1993 return this.velocityEngine; 1994 } 1995 1996 /** 1997 * Sets the {@code VelocityEngine} of the instance. 1998 * 1999 * @param value The new {@code VelocityEngine} of the instance or {@code null}. 2000 * 2001 * @see #getVelocityEngine() 2002 */ 2003 public final void setVelocityEngine( final VelocityEngine value ) 2004 { 2005 this.velocityEngine = value; 2006 this.defaultVelocityEngine = false; 2007 } 2008 2009 /** 2010 * Gets a new velocity context used for merging templates. 2011 * 2012 * @return A new velocity context used for merging templates. 2013 * 2014 * @see #getTemplateParameters() 2015 */ 2016 public VelocityContext getVelocityContext() 2017 { 2018 final Calendar now = Calendar.getInstance(); 2019 final VelocityContext ctx = 2020 new VelocityContext( new HashMap<String, Object>( this.getTemplateParameters() ) ); 2021 2022 this.mergeTemplateProfileProperties( this.getTemplateProfile(), this.getLocale().getLanguage(), ctx ); 2023 this.mergeTemplateProfileProperties( this.getTemplateProfile(), null, ctx ); 2024 this.mergeTemplateProfileProperties( getDefaultTemplateProfile(), this.getLocale().getLanguage(), ctx ); 2025 this.mergeTemplateProfileProperties( getDefaultTemplateProfile(), null, ctx ); 2026 2027 this.getModules(); // Initialization prior to cloning. 2028 final Model clonedModel = this.getModel().clone(); 2029 final Modules clonedModules = ModelHelper.getModules( clonedModel ); 2030 assert clonedModules != null : "Unexpected missing modules for model '" + clonedModel.getIdentifier() + "'."; 2031 2032 ctx.put( "model", clonedModel ); 2033 ctx.put( "modules", clonedModules ); 2034 ctx.put( "imodel", new InheritanceModel( this.getModules() ) ); 2035 ctx.put( "tool", this ); 2036 ctx.put( "toolName", this.getClass().getName() ); 2037 ctx.put( "toolVersion", getMessage( "projectVersion" ) ); 2038 ctx.put( "toolUrl", getMessage( "projectUrl" ) ); 2039 ctx.put( "calendar", now.getTime() ); 2040 2041 // JDK: As of JDK 7, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX". 2042 ctx.put( "now", 2043 new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ", this.getLocale() ).format( now.getTime() ) ); 2044 2045 ctx.put( "year", new SimpleDateFormat( "yyyy", this.getLocale() ).format( now.getTime() ) ); 2046 ctx.put( "month", new SimpleDateFormat( "MM", this.getLocale() ).format( now.getTime() ) ); 2047 ctx.put( "day", new SimpleDateFormat( "dd", this.getLocale() ).format( now.getTime() ) ); 2048 ctx.put( "hour", new SimpleDateFormat( "HH", this.getLocale() ).format( now.getTime() ) ); 2049 ctx.put( "minute", new SimpleDateFormat( "mm", this.getLocale() ).format( now.getTime() ) ); 2050 ctx.put( "second", new SimpleDateFormat( "ss", this.getLocale() ).format( now.getTime() ) ); 2051 ctx.put( "timezone", new SimpleDateFormat( "Z", this.getLocale() ).format( now.getTime() ) ); 2052 ctx.put( "shortDate", this.getShortDate( now ) ); 2053 ctx.put( "mediumDate", this.getMediumDate( now ) ); 2054 ctx.put( "longDate", this.getLongDate( now ) ); 2055 ctx.put( "isoDate", this.getIsoDate( now ) ); 2056 ctx.put( "shortTime", this.getShortTime( now ) ); 2057 ctx.put( "mediumTime", this.getMediumTime( now ) ); 2058 ctx.put( "longTime", this.getLongTime( now ) ); 2059 ctx.put( "isoTime", this.getIsoTime( now ) ); 2060 ctx.put( "shortDateTime", this.getShortDateTime( now ) ); 2061 ctx.put( "mediumDateTime", this.getMediumDateTime( now ) ); 2062 ctx.put( "longDateTime", this.getLongDateTime( now ) ); 2063 ctx.put( "isoDateTime", this.getIsoDateTime( now ) ); 2064 2065 return ctx; 2066 } 2067 2068 /** 2069 * Gets the template parameters of the instance. 2070 * <p>This accessor method returns a reference to the live map, not a snapshot. Therefore any modification you make 2071 * to the returned map will be present inside the object. This is why there is no {@code set} method for the 2072 * template parameters property.</p> 2073 * 2074 * @return The template parameters of the instance. 2075 * 2076 * @see #getVelocityContext() 2077 * 2078 * @since 1.2 2079 */ 2080 public final Map<String, Object> getTemplateParameters() 2081 { 2082 if ( this.templateParameters == null ) 2083 { 2084 this.templateParameters = Collections.synchronizedMap( new HashMap<String, Object>() ); 2085 } 2086 2087 return this.templateParameters; 2088 } 2089 2090 /** 2091 * Gets the location to search for templates in addition to searching the class path. 2092 * 2093 * @return The location to search for templates in addition to searching the class path or {@code null}. 2094 * 2095 * @see #setTemplateLocation(java.net.URL) 2096 * 2097 * @since 1.2 2098 */ 2099 public final URL getTemplateLocation() 2100 { 2101 return this.templateLocation; 2102 } 2103 2104 /** 2105 * Sets the location to search for templates in addition to searching the class path. 2106 * 2107 * @param value The new location to search for templates in addition to searching the class path or {@code null}. 2108 * 2109 * @see #getTemplateLocation() 2110 * 2111 * @since 1.2 2112 */ 2113 public final void setTemplateLocation( final URL value ) 2114 { 2115 this.templateLocation = value; 2116 this.templateProfilePropertiesCache = null; 2117 2118 if ( this.defaultVelocityEngine ) 2119 { 2120 this.setVelocityEngine( null ); 2121 } 2122 } 2123 2124 /** 2125 * Gets the encoding to use for reading templates. 2126 * 2127 * @return The encoding to use for reading templates. 2128 * 2129 * @see #setTemplateEncoding(java.lang.String) 2130 */ 2131 public final String getTemplateEncoding() 2132 { 2133 if ( this.templateEncoding == null ) 2134 { 2135 this.templateEncoding = getMessage( "buildSourceEncoding" ); 2136 2137 if ( this.isLoggable( Level.CONFIG ) ) 2138 { 2139 this.log( Level.CONFIG, getMessage( "defaultTemplateEncoding", this.templateEncoding ), null ); 2140 } 2141 } 2142 2143 return this.templateEncoding; 2144 } 2145 2146 /** 2147 * Sets the encoding to use for reading templates. 2148 * 2149 * @param value The new encoding to use for reading templates or {@code null}. 2150 * 2151 * @see #getTemplateEncoding() 2152 */ 2153 public final void setTemplateEncoding( final String value ) 2154 { 2155 this.templateEncoding = value; 2156 } 2157 2158 /** 2159 * Gets the encoding to use for reading files. 2160 * 2161 * @return The encoding to use for reading files. 2162 * 2163 * @see #setInputEncoding(java.lang.String) 2164 */ 2165 public final String getInputEncoding() 2166 { 2167 if ( this.inputEncoding == null ) 2168 { 2169 this.inputEncoding = new InputStreamReader( new ByteArrayInputStream( NO_BYTES ) ).getEncoding(); 2170 2171 if ( this.isLoggable( Level.CONFIG ) ) 2172 { 2173 this.log( Level.CONFIG, getMessage( "defaultInputEncoding", this.inputEncoding ), null ); 2174 } 2175 } 2176 2177 return this.inputEncoding; 2178 } 2179 2180 /** 2181 * Sets the encoding to use for reading files. 2182 * 2183 * @param value The new encoding to use for reading files or {@code null}. 2184 * 2185 * @see #getInputEncoding() 2186 */ 2187 public final void setInputEncoding( final String value ) 2188 { 2189 this.inputEncoding = value; 2190 } 2191 2192 /** 2193 * Gets the encoding to use for writing files. 2194 * 2195 * @return The encoding to use for writing files. 2196 * 2197 * @see #setOutputEncoding(java.lang.String) 2198 */ 2199 public final String getOutputEncoding() 2200 { 2201 if ( this.outputEncoding == null ) 2202 { 2203 this.outputEncoding = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding(); 2204 2205 if ( this.isLoggable( Level.CONFIG ) ) 2206 { 2207 this.log( Level.CONFIG, getMessage( "defaultOutputEncoding", this.outputEncoding ), null ); 2208 } 2209 } 2210 2211 return this.outputEncoding; 2212 } 2213 2214 /** 2215 * Sets the encoding to use for writing files. 2216 * 2217 * @param value The encoding to use for writing files or {@code null}. 2218 * 2219 * @see #getOutputEncoding() 2220 */ 2221 public final void setOutputEncoding( final String value ) 2222 { 2223 this.outputEncoding = value; 2224 } 2225 2226 /** 2227 * Gets the default template profile. 2228 * <p>The default template profile is controlled by system property 2229 * {@code org.jomc.tools.JomcTool.defaultTemplateProfile} holding the name of the template profile to use by 2230 * default. If that property is not set, the {@code jomc-java} default is returned.</p> 2231 * 2232 * @return The default template profile. 2233 * 2234 * @see #setDefaultTemplateProfile(java.lang.String) 2235 * 2236 * @deprecated The {@code static} modifier of this method and support to setup the default template profile using 2237 * a system property will be removed in version 2.0. 2238 */ 2239 @Deprecated 2240 public static String getDefaultTemplateProfile() 2241 { 2242 if ( defaultTemplateProfile == null ) 2243 { 2244 defaultTemplateProfile = System.getProperty( "org.jomc.tools.JomcTool.defaultTemplateProfile", 2245 DEFAULT_TEMPLATE_PROFILE ); 2246 2247 } 2248 2249 return defaultTemplateProfile; 2250 } 2251 2252 /** 2253 * Sets the default template profile. 2254 * 2255 * @param value The new default template profile or {@code null}. 2256 * 2257 * @see #getDefaultTemplateProfile() 2258 * 2259 * @deprecated The {@code static} modifier of this method will be removed in version 2.0. 2260 */ 2261 @Deprecated 2262 public static void setDefaultTemplateProfile( final String value ) 2263 { 2264 defaultTemplateProfile = value; 2265 } 2266 2267 /** 2268 * Gets the template profile of the instance. 2269 * 2270 * @return The template profile of the instance. 2271 * 2272 * @see #getDefaultTemplateProfile() 2273 * @see #setTemplateProfile(java.lang.String) 2274 */ 2275 public final String getTemplateProfile() 2276 { 2277 if ( this.templateProfile == null ) 2278 { 2279 this.templateProfile = getDefaultTemplateProfile(); 2280 2281 if ( this.isLoggable( Level.CONFIG ) ) 2282 { 2283 this.log( Level.CONFIG, getMessage( "defaultTemplateProfile", this.templateProfile ), null ); 2284 } 2285 } 2286 2287 return this.templateProfile; 2288 } 2289 2290 /** 2291 * Sets the template profile of the instance. 2292 * 2293 * @param value The new template profile of the instance or {@code null}. 2294 * 2295 * @see #getTemplateProfile() 2296 */ 2297 public final void setTemplateProfile( final String value ) 2298 { 2299 this.templateProfile = value; 2300 } 2301 2302 /** 2303 * Gets the indentation string of the instance. 2304 * 2305 * @return The indentation string of the instance. 2306 * 2307 * @see #setIndentation(java.lang.String) 2308 */ 2309 public final String getIndentation() 2310 { 2311 if ( this.indentation == null ) 2312 { 2313 this.indentation = " "; 2314 2315 if ( this.isLoggable( Level.CONFIG ) ) 2316 { 2317 this.log( Level.CONFIG, getMessage( "defaultIndentation", 2318 StringEscapeUtils.escapeJava( this.indentation ) ), null ); 2319 2320 } 2321 } 2322 2323 return this.indentation; 2324 } 2325 2326 /** 2327 * Gets an indentation string for a given indentation level. 2328 * 2329 * @param level The indentation level to get an indentation string for. 2330 * 2331 * @return The indentation string for {@code level}. 2332 * 2333 * @throws IllegalArgumentException if {@code level} is negative. 2334 * 2335 * @see #getIndentation() 2336 */ 2337 public final String getIndentation( final int level ) 2338 { 2339 if ( level < 0 ) 2340 { 2341 throw new IllegalArgumentException( Integer.toString( level ) ); 2342 } 2343 2344 Map<String, String> map = this.indentationCache == null ? null : this.indentationCache.get(); 2345 2346 if ( map == null ) 2347 { 2348 map = new ConcurrentHashMap<String, String>( 8 ); 2349 this.indentationCache = new SoftReference<Map<String, String>>( map ); 2350 } 2351 2352 final String key = this.getIndentation() + "|" + level; 2353 String idt = map.get( key ); 2354 2355 if ( idt == null ) 2356 { 2357 final StringBuilder b = new StringBuilder( this.getIndentation().length() * level ); 2358 2359 for ( int i = level; i > 0; i-- ) 2360 { 2361 b.append( this.getIndentation() ); 2362 } 2363 2364 idt = b.toString(); 2365 map.put( key, idt ); 2366 } 2367 2368 return idt; 2369 } 2370 2371 /** 2372 * Sets the indentation string of the instance. 2373 * 2374 * @param value The new indentation string of the instance or {@code null}. 2375 * 2376 * @see #getIndentation() 2377 */ 2378 public final void setIndentation( final String value ) 2379 { 2380 this.indentation = value; 2381 } 2382 2383 /** 2384 * Gets the line separator of the instance. 2385 * 2386 * @return The line separator of the instance. 2387 * 2388 * @see #setLineSeparator(java.lang.String) 2389 */ 2390 public final String getLineSeparator() 2391 { 2392 if ( this.lineSeparator == null ) 2393 { 2394 this.lineSeparator = System.getProperty( "line.separator", "\n" ); 2395 2396 if ( this.isLoggable( Level.CONFIG ) ) 2397 { 2398 this.log( Level.CONFIG, getMessage( "defaultLineSeparator", 2399 StringEscapeUtils.escapeJava( this.lineSeparator ) ), null ); 2400 2401 } 2402 } 2403 2404 return this.lineSeparator; 2405 } 2406 2407 /** 2408 * Sets the line separator of the instance. 2409 * 2410 * @param value The new line separator of the instance or {@code null}. 2411 * 2412 * @see #getLineSeparator() 2413 */ 2414 public final void setLineSeparator( final String value ) 2415 { 2416 this.lineSeparator = value; 2417 } 2418 2419 /** 2420 * Gets the locale of the instance. 2421 * 2422 * @return The locale of the instance. 2423 * 2424 * @see #setLocale(java.util.Locale) 2425 * 2426 * @since 1.2 2427 */ 2428 public final Locale getLocale() 2429 { 2430 if ( this.locale == null ) 2431 { 2432 this.locale = Locale.ENGLISH; 2433 2434 if ( this.isLoggable( Level.CONFIG ) ) 2435 { 2436 this.log( Level.CONFIG, getMessage( "defaultLocale", this.locale ), null ); 2437 } 2438 } 2439 2440 return this.locale; 2441 } 2442 2443 /** 2444 * Sets the locale of the instance. 2445 * 2446 * @param value The new locale of the instance or {@code null}. 2447 * 2448 * @see #getLocale() 2449 * 2450 * @since 1.2 2451 */ 2452 public final void setLocale( final Locale value ) 2453 { 2454 this.locale = value; 2455 } 2456 2457 /** 2458 * Gets a velocity template for a given name. 2459 * <p>This method searches templates at the following locations in the shown order. 2460 * <ol> 2461 * <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/{@link #getLocale() language}/<i>templateName</i></code></li> 2462 * <li><code>org/jomc/tools/templates/{@link #getTemplateProfile() profile}/<i>templateName</i></code></li> 2463 * <li><code>org/jomc/tools/templates/{@link #getDefaultTemplateProfile() default profile}/{@link #getLocale() language}/<i>templateName</i></code></li> 2464 * <li><code>org/jomc/tools/templates/{@link #getDefaultTemplateProfile() default profile}/<i>templateName</i></code></li> 2465 * </ol></p> 2466 * 2467 * @param templateName The name of the template to get. 2468 * 2469 * @return The template matching {@code templateName}. 2470 * 2471 * @throws NullPointerException if {@code templateName} is {@code null}. 2472 * @throws IOException if getting the template fails. 2473 * 2474 * @see #getLocale() 2475 * @see #getTemplateProfile() 2476 * @see #getTemplateEncoding() 2477 * @see #getVelocityEngine() 2478 */ 2479 public Template getVelocityTemplate( final String templateName ) throws IOException 2480 { 2481 if ( templateName == null ) 2482 { 2483 throw new NullPointerException( "templateName" ); 2484 } 2485 2486 String location = null; 2487 Template template = null; 2488 final String key = this.getLocale() + "|" + this.getTemplateProfile() + "|" + getDefaultTemplateProfile() 2489 + "|" + templateName; 2490 2491 Map<String, String> map = this.templateLocationsCache == null ? null : this.templateLocationsCache.get(); 2492 2493 if ( map == null ) 2494 { 2495 map = Collections.synchronizedMap( new HashMap<String, String>( 32 ) ); 2496 this.templateLocationsCache = new SoftReference<Map<String, String>>( map ); 2497 } 2498 2499 location = map.get( key ); 2500 2501 if ( location == null && !map.containsKey( key ) ) 2502 { 2503 if ( !StringUtils.EMPTY.equals( this.getLocale().getLanguage() ) ) 2504 { 2505 location = TEMPLATE_PREFIX + this.getTemplateProfile() + "/" + this.getLocale().getLanguage() + "/" 2506 + templateName; 2507 2508 template = this.findVelocityTemplate( location ); 2509 } 2510 2511 if ( template == null ) 2512 { 2513 location = TEMPLATE_PREFIX + this.getTemplateProfile() + "/" + templateName; 2514 template = this.findVelocityTemplate( location ); 2515 } 2516 2517 if ( template == null && !StringUtils.EMPTY.equals( this.getLocale().getLanguage() ) ) 2518 { 2519 location = TEMPLATE_PREFIX + getDefaultTemplateProfile() + "/" + this.getLocale().getLanguage() + "/" 2520 + templateName; 2521 2522 template = this.findVelocityTemplate( location ); 2523 } 2524 2525 if ( template == null ) 2526 { 2527 location = TEMPLATE_PREFIX + getDefaultTemplateProfile() + "/" + templateName; 2528 template = this.findVelocityTemplate( location ); 2529 } 2530 2531 map.put( key, location ); 2532 } 2533 else if ( location != null ) 2534 { 2535 template = this.findVelocityTemplate( location ); 2536 } 2537 2538 if ( template == null ) 2539 { 2540 throw new IOException( getMessage( "noSuchTemplate", templateName ) ); 2541 } 2542 2543 if ( this.isLoggable( Level.FINER ) ) 2544 { 2545 this.log( Level.FINER, getMessage( "templateInfo", templateName, location ), null ); 2546 } 2547 2548 return template; 2549 } 2550 2551 /** 2552 * Notifies registered listeners. 2553 * 2554 * @param level The level of the event. 2555 * @param message The message of the event or {@code null}. 2556 * @param throwable The throwable of the event or {@code null}. 2557 * 2558 * @throws NullPointerException if {@code level} is {@code null}. 2559 * 2560 * @see #getListeners() 2561 * @see #isLoggable(java.util.logging.Level) 2562 */ 2563 public void log( final Level level, final String message, final Throwable throwable ) 2564 { 2565 if ( level == null ) 2566 { 2567 throw new NullPointerException( "level" ); 2568 } 2569 2570 if ( this.isLoggable( level ) ) 2571 { 2572 for ( int i = this.getListeners().size() - 1; i >= 0; i-- ) 2573 { 2574 this.getListeners().get( i ).onLog( level, message, throwable ); 2575 } 2576 } 2577 } 2578 2579 private String getJavaPackageName( final String identifier ) 2580 { 2581 if ( identifier == null ) 2582 { 2583 throw new NullPointerException( "identifier" ); 2584 } 2585 2586 final int idx = identifier.lastIndexOf( '.' ); 2587 return idx != -1 ? identifier.substring( 0, idx ) : ""; 2588 } 2589 2590 private Template findVelocityTemplate( final String location ) throws IOException 2591 { 2592 try 2593 { 2594 return this.getVelocityEngine().getTemplate( location, this.getTemplateEncoding() ); 2595 } 2596 catch ( final ResourceNotFoundException e ) 2597 { 2598 if ( this.isLoggable( Level.FINER ) ) 2599 { 2600 this.log( Level.FINER, getMessage( "templateNotFound", location ), null ); 2601 } 2602 2603 return null; 2604 } 2605 catch ( final ParseErrorException e ) 2606 { 2607 String m = getMessage( e ); 2608 m = m == null ? "" : " " + m; 2609 2610 // JDK: As of JDK 6, "new IOException( message, cause )". 2611 throw (IOException) new IOException( getMessage( "invalidTemplate", location, m ) ).initCause( e ); 2612 } 2613 catch ( final VelocityException e ) 2614 { 2615 String m = getMessage( e ); 2616 m = m == null ? "" : " " + m; 2617 2618 // JDK: As of JDK 6, "new IOException( message, cause )". 2619 throw (IOException) new IOException( getMessage( "velocityException", location, m ) ).initCause( e ); 2620 } 2621 } 2622 2623 private java.util.Properties getTemplateProfileProperties( final String profileName, final String language ) 2624 { 2625 Map<String, java.util.Properties> map = this.templateProfilePropertiesCache == null 2626 ? null : this.templateProfilePropertiesCache.get(); 2627 2628 if ( map == null ) 2629 { 2630 map = new ConcurrentHashMap<String, java.util.Properties>(); 2631 this.templateProfilePropertiesCache = new SoftReference<Map<String, java.util.Properties>>( map ); 2632 } 2633 2634 final String key = profileName + "|" + language; 2635 java.util.Properties profileProperties = map.get( key ); 2636 2637 if ( profileProperties == null ) 2638 { 2639 InputStream in = null; 2640 URL url = null; 2641 profileProperties = new java.util.Properties(); 2642 map.put( key, profileProperties ); 2643 2644 final String resourceName = TEMPLATE_PREFIX + profileName + ( language == null ? "" : "/" + language ) 2645 + "/context.properties"; 2646 2647 try 2648 { 2649 url = this.getClass().getResource( "/" + resourceName ); 2650 2651 if ( url != null ) 2652 { 2653 in = url.openStream(); 2654 2655 if ( this.isLoggable( Level.CONFIG ) ) 2656 { 2657 this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null ); 2658 } 2659 2660 profileProperties.load( in ); 2661 } 2662 else if ( this.getTemplateLocation() != null ) 2663 { 2664 if ( this.isLoggable( Level.CONFIG ) ) 2665 { 2666 this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null ); 2667 } 2668 2669 url = new URL( this.getTemplateLocation(), resourceName ); 2670 in = url.openStream(); 2671 2672 if ( this.isLoggable( Level.CONFIG ) ) 2673 { 2674 this.log( Level.CONFIG, getMessage( "contextPropertiesFound", url.toExternalForm() ), null ); 2675 } 2676 2677 profileProperties.load( in ); 2678 } 2679 else if ( this.isLoggable( Level.CONFIG ) ) 2680 { 2681 this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", resourceName ), null ); 2682 } 2683 } 2684 catch ( final FileNotFoundException e ) 2685 { 2686 if ( this.isLoggable( Level.CONFIG ) ) 2687 { 2688 this.log( Level.CONFIG, getMessage( "contextPropertiesNotFound", url.toExternalForm() ), null ); 2689 } 2690 } 2691 catch ( final IOException e ) 2692 { 2693 map.remove( key ); 2694 this.log( Level.SEVERE, getMessage( e ), e ); 2695 } 2696 finally 2697 { 2698 try 2699 { 2700 if ( in != null ) 2701 { 2702 in.close(); 2703 } 2704 } 2705 catch ( final IOException e ) 2706 { 2707 this.log( Level.SEVERE, getMessage( e ), e ); 2708 } 2709 } 2710 } 2711 2712 return profileProperties; 2713 } 2714 2715 private void mergeTemplateProfileProperties( final String profileName, final String language, 2716 final VelocityContext velocityContext ) 2717 { 2718 final java.util.Properties templateProfileProperties = 2719 this.getTemplateProfileProperties( profileName, language ); 2720 2721 for ( final Enumeration<?> e = templateProfileProperties.propertyNames(); e.hasMoreElements(); ) 2722 { 2723 final String name = e.nextElement().toString(); 2724 final String value = templateProfileProperties.getProperty( name ); 2725 final String[] values = value.split( "\\|" ); 2726 2727 if ( !velocityContext.containsKey( name ) ) 2728 { 2729 try 2730 { 2731 if ( values.length > 1 ) 2732 { 2733 final Class<?> valueClass = Class.forName( values[0] ); 2734 velocityContext.put( name, valueClass.getConstructor( String.class ).newInstance( values[1] ) ); 2735 } 2736 else if ( value.contains( "|" ) ) 2737 { 2738 velocityContext.put( name, Class.forName( values[0] ).newInstance() ); 2739 } 2740 else 2741 { 2742 velocityContext.put( name, value ); 2743 } 2744 } 2745 catch ( final InstantiationException ex ) 2746 { 2747 this.log( Level.SEVERE, getMessage( ex ), ex ); 2748 } 2749 catch ( final IllegalAccessException ex ) 2750 { 2751 this.log( Level.SEVERE, getMessage( ex ), ex ); 2752 } 2753 catch ( final InvocationTargetException ex ) 2754 { 2755 this.log( Level.SEVERE, getMessage( ex ), ex ); 2756 } 2757 catch ( final NoSuchMethodException ex ) 2758 { 2759 this.log( Level.SEVERE, getMessage( ex ), ex ); 2760 } 2761 catch ( final ClassNotFoundException ex ) 2762 { 2763 this.log( Level.SEVERE, getMessage( ex ), ex ); 2764 } 2765 } 2766 } 2767 } 2768 2769 private Set<String> getJavaKeywords() 2770 { 2771 Reader in = null; 2772 Set<String> set = this.javaKeywordsCache == null ? null : this.javaKeywordsCache.get(); 2773 2774 try 2775 { 2776 if ( set == null ) 2777 { 2778 in = new InputStreamReader( this.getClass().getResourceAsStream( 2779 "/" + this.getClass().getPackage().getName().replace( ".", "/" ) + "/JavaKeywords.txt" ), "UTF-8" ); 2780 2781 set = new CopyOnWriteArraySet<String>( IOUtils.readLines( in ) ); 2782 2783 this.javaKeywordsCache = new SoftReference<Set<String>>( set ); 2784 } 2785 } 2786 catch ( final IOException e ) 2787 { 2788 throw new IllegalStateException( getMessage( e ), e ); 2789 } 2790 finally 2791 { 2792 try 2793 { 2794 if ( in != null ) 2795 { 2796 in.close(); 2797 } 2798 } 2799 catch ( final IOException e ) 2800 { 2801 throw new IllegalStateException( getMessage( e ), e ); 2802 } 2803 } 2804 2805 return set; 2806 } 2807 2808 private static String getMessage( final String key, final Object... arguments ) 2809 { 2810 return MessageFormat.format( ResourceBundle.getBundle( 2811 JomcTool.class.getName().replace( '.', '/' ) ).getString( key ), arguments ); 2812 2813 } 2814 2815 private static String getMessage( final Throwable t ) 2816 { 2817 return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null; 2818 } 2819 2820 }