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