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