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