001 /*
002 * Copyright (c) 2009 The JOMC Project
003 * Copyright (c) 2005 Christian Schulte <schulte2005@users.sourceforge.net>
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions
008 * are met:
009 *
010 * o Redistributions of source code must retain the above copyright
011 * notice, this list of conditions and the following disclaimer.
012 *
013 * o Redistributions in binary form must reproduce the above copyright
014 * notice, this list of conditions and the following disclaimer in
015 * the documentation and/or other materials provided with the
016 * distribution.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
027 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
028 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 *
030 * $Id: JomcTool.java 2179 2010-06-28 17:49:49Z schulte2005 $
031 *
032 */
033 package org.jomc.tools;
034
035 import java.io.BufferedReader;
036 import java.io.ByteArrayInputStream;
037 import java.io.ByteArrayOutputStream;
038 import java.io.IOException;
039 import java.io.InputStreamReader;
040 import java.io.OutputStreamWriter;
041 import java.io.StringReader;
042 import java.text.DateFormat;
043 import java.text.Format;
044 import java.text.MessageFormat;
045 import java.text.SimpleDateFormat;
046 import java.util.ArrayList;
047 import java.util.Calendar;
048 import java.util.Collections;
049 import java.util.Date;
050 import java.util.HashMap;
051 import java.util.LinkedList;
052 import java.util.List;
053 import java.util.Locale;
054 import java.util.Map;
055 import java.util.ResourceBundle;
056 import java.util.logging.Level;
057 import org.apache.commons.lang.StringEscapeUtils;
058 import org.apache.velocity.Template;
059 import org.apache.velocity.VelocityContext;
060 import org.apache.velocity.app.VelocityEngine;
061 import org.apache.velocity.exception.ResourceNotFoundException;
062 import org.apache.velocity.runtime.RuntimeConstants;
063 import org.apache.velocity.runtime.RuntimeServices;
064 import org.apache.velocity.runtime.log.LogChute;
065 import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
066 import org.jomc.model.Argument;
067 import org.jomc.model.ArgumentType;
068 import org.jomc.model.Dependency;
069 import org.jomc.model.Implementation;
070 import org.jomc.model.Message;
071 import org.jomc.model.ModelObject;
072 import org.jomc.model.Modules;
073 import org.jomc.model.Multiplicity;
074 import org.jomc.model.Properties;
075 import org.jomc.model.Property;
076 import org.jomc.model.Specification;
077 import org.jomc.model.SpecificationReference;
078 import org.jomc.model.Specifications;
079 import org.jomc.model.Text;
080 import org.jomc.model.modlet.ModelHelper;
081 import org.jomc.modlet.Model;
082
083 /**
084 * Base tool class.
085 *
086 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
087 * @version $Id: JomcTool.java 2179 2010-06-28 17:49:49Z schulte2005 $
088 */
089 public abstract class JomcTool
090 {
091
092 /** Listener interface. */
093 public abstract static class Listener
094 {
095
096 /**
097 * Get called on logging.
098 *
099 * @param level The level of the event.
100 * @param message The message of the event or {@code null}.
101 * @param throwable The throwable of the event or {@code null}.
102 *
103 * @throws NullPointerException if {@code level} is {@code null}.
104 */
105 public abstract void onLog( Level level, String message, Throwable throwable );
106
107 }
108
109 /** Empty byte array. */
110 private static final byte[] NO_BYTES =
111 {
112 };
113
114 /** The prefix of the template location. */
115 private static final String TEMPLATE_PREFIX =
116 JomcTool.class.getPackage().getName().replace( '.', '/' ) + "/templates/";
117
118 /** Name of the velocity classpath resource loader implementation. */
119 private static final String VELOCITY_RESOURCE_LOADER = ClasspathResourceLoader.class.getName();
120
121 /** Constant for the default template profile. */
122 private static final String DEFAULT_TEMPLATE_PROFILE = "jomc-java";
123
124 /** Default template profile. */
125 private static volatile String defaultTemplateProfile;
126
127 /**
128 * Log level events are logged at by default.
129 * @see #getDefaultLogLevel()
130 */
131 private static final Level DEFAULT_LOG_LEVEL = Level.WARNING;
132
133 /** Default log level. */
134 private static volatile Level defaultLogLevel;
135
136 /** The model of the instance. */
137 private Model model;
138
139 /** {@code VelocityEngine} of the generator. */
140 private VelocityEngine velocityEngine;
141
142 /** The encoding to use for reading templates. */
143 private String templateEncoding;
144
145 /** The encoding to use for reading files. */
146 private String inputEncoding;
147
148 /** The encoding to use for writing files. */
149 private String outputEncoding;
150
151 /** The template profile of the instance. */
152 private String templateProfile;
153
154 /** The indentation string of the instance. */
155 private String indentation;
156
157 /** The line separator of the instance. */
158 private String lineSeparator;
159
160 /** The listeners of the instance. */
161 private List<Listener> listeners;
162
163 /** Log level of the instance. */
164 private Level logLevel;
165
166 /** Cached indentation strings. */
167 private final Map<Integer, String> indentationCache = new HashMap<Integer, String>();
168
169 /** Creates a new {@code JomcTool} instance. */
170 public JomcTool()
171 {
172 super();
173 }
174
175 /**
176 * Creates a new {@code JomcTool} instance taking a {@code JomcTool} instance to initialize the new instance with.
177 *
178 * @param tool The instance to initialize the new instance with.
179 *
180 * @throws NullPointerException if {@code tool} is {@code null}.
181 * @throws IOException if copying {@code tool} fails.
182 */
183 public JomcTool( final JomcTool tool ) throws IOException
184 {
185 this();
186
187 if ( tool == null )
188 {
189 throw new NullPointerException( "tool" );
190 }
191
192 this.indentation = tool.indentation;
193 this.inputEncoding = tool.inputEncoding;
194 this.lineSeparator = tool.lineSeparator;
195 this.listeners = tool.listeners != null ? new LinkedList<Listener>( tool.listeners ) : null;
196 this.logLevel = tool.logLevel;
197 this.model = tool.model != null ? new Model( tool.model ) : null;
198 this.outputEncoding = tool.outputEncoding;
199 this.templateEncoding = tool.templateEncoding;
200 this.templateProfile = tool.templateProfile;
201 this.velocityEngine = tool.velocityEngine;
202 }
203
204 /**
205 * Gets the list of registered listeners.
206 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
207 * to the returned list will be present inside the object. This is why there is no {@code set} method for the
208 * listeners property.</p>
209 *
210 * @return The list of registered listeners.
211 *
212 * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
213 */
214 public List<Listener> getListeners()
215 {
216 if ( this.listeners == null )
217 {
218 this.listeners = new LinkedList<Listener>();
219 }
220
221 return this.listeners;
222 }
223
224 /**
225 * Gets the default log level events are logged at.
226 * <p>The default log level is controlled by system property {@code org.jomc.tools.JomcTool.defaultLogLevel} holding
227 * the log level to log events at by default. If that property is not set, the {@code WARNING} default is
228 * returned.</p>
229 *
230 * @return The log level events are logged at by default.
231 *
232 * @see #getLogLevel()
233 * @see Level#parse(java.lang.String)
234 */
235 public static Level getDefaultLogLevel()
236 {
237 if ( defaultLogLevel == null )
238 {
239 defaultLogLevel = Level.parse( System.getProperty( "org.jomc.tools.JomcTool.defaultLogLevel",
240 DEFAULT_LOG_LEVEL.getName() ) );
241
242 }
243
244 return defaultLogLevel;
245 }
246
247 /**
248 * Sets the default log level events are logged at.
249 *
250 * @param value The new default level events are logged at or {@code null}.
251 *
252 * @see #getDefaultLogLevel()
253 */
254 public static void setDefaultLogLevel( final Level value )
255 {
256 defaultLogLevel = value;
257 }
258
259 /**
260 * Gets the log level of the instance.
261 *
262 * @return The log level of the instance.
263 *
264 * @see #getDefaultLogLevel()
265 * @see #setLogLevel(java.util.logging.Level)
266 * @see #isLoggable(java.util.logging.Level)
267 */
268 public final Level getLogLevel()
269 {
270 if ( this.logLevel == null )
271 {
272 this.logLevel = getDefaultLogLevel();
273 this.log( Level.CONFIG, getMessage( "defaultLogLevelInfo", this.getClass().getName(),
274 this.logLevel.getLocalizedName() ), null );
275
276 }
277
278 return this.logLevel;
279 }
280
281 /**
282 * Sets the log level of the instance.
283 *
284 * @param value The new log level of the instance or {@code null}.
285 *
286 * @see #getLogLevel()
287 * @see #isLoggable(java.util.logging.Level)
288 */
289 public final void setLogLevel( final Level value )
290 {
291 this.logLevel = value;
292 }
293
294 /**
295 * Checks if a message at a given level is provided to the listeners of the instance.
296 *
297 * @param level The level to test.
298 *
299 * @return {@code true} if messages at {@code level} are provided to the listeners of the instance;
300 * {@code false} if messages at {@code level} are not provided to the listeners of the instance.
301 *
302 * @throws NullPointerException if {@code level} is {@code null}.
303 *
304 * @see #getLogLevel()
305 * @see #setLogLevel(java.util.logging.Level)
306 * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
307 */
308 public boolean isLoggable( final Level level )
309 {
310 if ( level == null )
311 {
312 throw new NullPointerException( "level" );
313 }
314
315 return level.intValue() >= this.getLogLevel().intValue();
316 }
317
318 /**
319 * Gets the Java package name of a specification.
320 *
321 * @param specification The specification to get the Java package name of.
322 *
323 * @return The Java package name of {@code specification} or {@code null}.
324 *
325 * @throws NullPointerException if {@code specification} is {@code null}.
326 */
327 public String getJavaPackageName( final Specification specification )
328 {
329 if ( specification == null )
330 {
331 throw new NullPointerException( "specification" );
332 }
333
334 return specification.getClazz() != null ? this.getJavaPackageName( specification.getClazz() ) : null;
335 }
336
337 /**
338 * Gets the Java type name of a specification.
339 *
340 * @param specification The specification to get the Java type name of.
341 * @param qualified {@code true} to return the fully qualified type name (with package name prepended);
342 * {@code false} to return the short type name (without package name prepended).
343 *
344 * @return The Java type name of {@code specification} or {@code null}.
345 *
346 * @throws NullPointerException if {@code specification} is {@code null}.
347 */
348 public String getJavaTypeName( final Specification specification, final boolean qualified )
349 {
350 if ( specification == null )
351 {
352 throw new NullPointerException( "specification" );
353 }
354
355 if ( specification.getClazz() != null )
356 {
357 final StringBuilder typeName = new StringBuilder();
358 final String javaPackageName = this.getJavaPackageName( specification );
359
360 if ( qualified && javaPackageName.length() > 0 )
361 {
362 typeName.append( javaPackageName ).append( '.' );
363 }
364
365 typeName.append( javaPackageName.length() > 0
366 ? specification.getClazz().substring( javaPackageName.length() + 1 )
367 : specification.getClazz() );
368
369 return typeName.toString();
370 }
371
372 return null;
373 }
374
375 /**
376 * Gets the Java class path location of a specification.
377 *
378 * @param specification The specification to return the Java class path location of.
379 *
380 * @return The Java class path location of {@code specification} or {@code null}.
381 *
382 * @throws NullPointerException if {@code specification} is {@code null}.
383 */
384 public String getJavaClasspathLocation( final Specification specification )
385 {
386 if ( specification == null )
387 {
388 throw new NullPointerException( "specification" );
389 }
390
391 return specification.getClazz() != null
392 ? ( this.getJavaTypeName( specification, true ) ).replace( '.', '/' )
393 : null;
394
395 }
396
397 /**
398 * Gets the Java package name of a specification reference.
399 *
400 * @param reference The specification reference to get the Java package name of.
401 *
402 * @return The Java package name of {@code reference} or {@code null}.
403 *
404 * @throws NullPointerException if {@code reference} is {@code null}.
405 */
406 public String getJavaPackageName( final SpecificationReference reference )
407 {
408 if ( reference == null )
409 {
410 throw new NullPointerException( "reference" );
411 }
412
413 final Specification s = this.getModules().getSpecification( reference.getIdentifier() );
414 assert s != null : "Specification '" + reference.getIdentifier() + "' not found.";
415 return s.getClazz() != null ? this.getJavaPackageName( s ) : null;
416 }
417
418 /**
419 * Gets the name of a Java type of a given specification reference.
420 *
421 * @param reference The specification reference to get a Java type name of.
422 * @param qualified {@code true} to return the fully qualified type name (with package name prepended);
423 * {@code false} to return the short type name (without package name prepended).
424 *
425 * @return The Java type name of {@code reference} or {@code null}.
426 *
427 * @throws NullPointerException if {@code reference} is {@code null}.
428 */
429 public String getJavaTypeName( final SpecificationReference reference, final boolean qualified )
430 {
431 if ( reference == null )
432 {
433 throw new NullPointerException( "reference" );
434 }
435
436 final Specification s = this.getModules().getSpecification( reference.getIdentifier() );
437 assert s != null : "Specification '" + reference.getIdentifier() + "' not found.";
438 return s.getClazz() != null ? this.getJavaTypeName( s, qualified ) : null;
439 }
440
441 /**
442 * Gets the Java package name of an implementation.
443 *
444 * @param implementation The implementation to get the Java package name of.
445 *
446 * @return The Java package name of {@code implementation} or {@code null}.
447 *
448 * @throws NullPointerException if {@code implementation} is {@code null}.
449 */
450 public String getJavaPackageName( final Implementation implementation )
451 {
452 if ( implementation == null )
453 {
454 throw new NullPointerException( "implementation" );
455 }
456
457 return implementation.getClazz() != null ? this.getJavaPackageName( implementation.getClazz() ) : null;
458 }
459
460 /**
461 * Gets the Java type name of an implementation.
462 *
463 * @param implementation The implementation to get the Java type name of.
464 * @param qualified {@code true} to return the fully qualified type name (with package name prepended);
465 * {@code false} to return the short type name (without package name prepended).
466 *
467 * @return The Java type name of {@code implementation} or {@code null}.
468 *
469 * @throws NullPointerException if {@code implementation} is {@code null}.
470 */
471 public String getJavaTypeName( final Implementation implementation, final boolean qualified )
472 {
473 if ( implementation == null )
474 {
475 throw new NullPointerException( "implementation" );
476 }
477
478 if ( implementation.getClazz() != null )
479 {
480 final StringBuilder typeName = new StringBuilder();
481 final String javaPackageName = this.getJavaPackageName( implementation );
482
483 if ( qualified && javaPackageName.length() > 0 )
484 {
485 typeName.append( javaPackageName ).append( '.' );
486 }
487
488 typeName.append( javaPackageName.length() > 0
489 ? implementation.getClazz().substring( javaPackageName.length() + 1 )
490 : implementation.getClazz() );
491
492 return typeName.toString();
493 }
494
495 return null;
496 }
497
498 /**
499 * Gets the Java class path location of an implementation.
500 *
501 * @param implementation The implementation to return the Java class path location of.
502 *
503 * @return The Java class path location of {@code implementation} or {@code null}.
504 *
505 * @throws NullPointerException if {@code implementation} is {@code null}.
506 */
507 public String getJavaClasspathLocation( final Implementation implementation )
508 {
509 if ( implementation == null )
510 {
511 throw new NullPointerException( "implementation" );
512 }
513
514 return implementation.getClazz() != null
515 ? ( this.getJavaTypeName( implementation, true ) ).replace( '.', '/' )
516 : null;
517
518 }
519
520 /**
521 * Gets all Java interfaces an implementation implements.
522 *
523 * @param implementation The implementation to get all implemented Java interfaces of.
524 * @param qualified {@code true} to return the fully qualified type names (with package name prepended);
525 * {@code false} to return the short type names (without package name prepended).
526 *
527 * @return Unmodifiable list contaning all Java interfaces implemented by {@code implementation}.
528 *
529 * @throws NullPointerException if {@code implementation} is {@code null}.
530 */
531 public List<String> getJavaInterfaceNames( final Implementation implementation, final boolean qualified )
532 {
533 if ( implementation == null )
534 {
535 throw new NullPointerException( "implementation" );
536 }
537
538 final Specifications specs = this.getModules().getSpecifications( implementation.getIdentifier() );
539 final List<String> col = new ArrayList<String>( specs == null ? 0 : specs.getSpecification().size() );
540
541 if ( specs != null )
542 {
543 for ( Specification s : specs.getSpecification() )
544 {
545 if ( s.getClazz() != null )
546 {
547 final String typeName = this.getJavaTypeName( s, qualified );
548 if ( !col.contains( typeName ) )
549 {
550 col.add( typeName );
551 }
552 }
553 }
554 }
555
556 return Collections.unmodifiableList( col );
557 }
558
559 /**
560 * Gets the Java type name of an argument.
561 *
562 * @param argument The argument to get the Java type name of.
563 *
564 * @return The Java type name of {@code argument}.
565 *
566 * @throws NullPointerException if {@code argument} is {@code null}.
567 */
568 public String getJavaTypeName( final Argument argument )
569 {
570 if ( argument == null )
571 {
572 throw new NullPointerException( "argument" );
573 }
574
575 String javaTypeName = "java.lang.String";
576
577 if ( argument.getType() == ArgumentType.DATE || argument.getType() == ArgumentType.TIME )
578 {
579 javaTypeName = "java.util.Date";
580 }
581 else if ( argument.getType() == ArgumentType.NUMBER )
582 {
583 javaTypeName = "java.lang.Number";
584 }
585
586 return javaTypeName;
587 }
588
589 /**
590 * Gets the Java type name of a property.
591 *
592 * @param property The property to get the Java type name of.
593 * @param boxify {@code true} to return the name of the Java wrapper class when the type is a Java primitive type;
594 * {@code false} to return the exact binary name (unboxed name) of the Java type.
595 *
596 * @return The Java type name of {@code property}.
597 *
598 * @throws NullPointerException if {@code property} is {@code null}.
599 */
600 public String getJavaTypeName( final Property property, final boolean boxify )
601 {
602 if ( property == null )
603 {
604 throw new NullPointerException( "property" );
605 }
606
607 if ( property.getType() != null )
608 {
609 final String typeName = property.getType();
610
611 if ( boxify )
612 {
613 if ( Boolean.TYPE.getName().equals( typeName ) )
614 {
615 return Boolean.class.getName();
616 }
617 if ( Byte.TYPE.getName().equals( typeName ) )
618 {
619 return Byte.class.getName();
620 }
621 if ( Character.TYPE.getName().equals( typeName ) )
622 {
623 return Character.class.getName();
624 }
625 if ( Double.TYPE.getName().equals( typeName ) )
626 {
627 return Double.class.getName();
628 }
629 if ( Float.TYPE.getName().equals( typeName ) )
630 {
631 return Float.class.getName();
632 }
633 if ( Integer.TYPE.getName().equals( typeName ) )
634 {
635 return Integer.class.getName();
636 }
637 if ( Long.TYPE.getName().equals( typeName ) )
638 {
639 return Long.class.getName();
640 }
641 if ( Short.TYPE.getName().equals( typeName ) )
642 {
643 return Short.class.getName();
644 }
645 }
646
647 return typeName;
648 }
649
650 return property.getAny() != null ? Object.class.getName() : String.class.getName();
651 }
652
653 /**
654 * Gets a flag indicating if the type of a given property is a Java primitive.
655 *
656 * @param property The property to query.
657 *
658 * @return {@code true} if the type of {@code property} is a Java primitive; {@code false} if not.
659 *
660 * @throws NullPointerException if {@code property} is {@code null}.
661 */
662 public boolean isJavaPrimitiveType( final Property property )
663 {
664 if ( property == null )
665 {
666 throw new NullPointerException( "property" );
667 }
668
669 return !this.getJavaTypeName( property, false ).equals( this.getJavaTypeName( property, true ) );
670 }
671
672 /**
673 * Gets the name of a Java accessor method of a given property.
674 *
675 * @param property The property to get a Java accessor method name of.
676 *
677 * @return The Java accessor method name of {@code property}.
678 *
679 * @throws NullPointerException if {@code property} is {@code null}.
680 */
681 public String getJavaGetterMethodName( final Property property )
682 {
683 if ( property == null )
684 {
685 throw new NullPointerException( "property" );
686 }
687
688 String prefix = "get";
689
690 final String javaTypeName = this.getJavaTypeName( property, true );
691 if ( Boolean.class.getName().equals( javaTypeName ) )
692 {
693 prefix = "is";
694 }
695
696 return prefix + this.getJavaIdentifier( property.getName() );
697 }
698
699 /**
700 * Gets the name of a Java type of a given dependency.
701 *
702 * @param dependency The dependency to get a dependency Java type name of.
703 *
704 * @return The Java type name of {@code dependency} or {@code null}.
705 *
706 * @throws NullPointerException if {@code dependency} is {@code null}.
707 */
708 public String getJavaTypeName( final Dependency dependency )
709 {
710 if ( dependency == null )
711 {
712 throw new NullPointerException( "dependency" );
713 }
714
715 final Specification s = this.getModules().getSpecification( dependency.getIdentifier() );
716
717 if ( s != null && s.getClazz() != null )
718 {
719 final StringBuilder typeName = new StringBuilder();
720 typeName.append( this.getJavaTypeName( s, true ) );
721 if ( s.getMultiplicity() == Multiplicity.MANY && dependency.getImplementationName() == null )
722 {
723 typeName.append( "[]" );
724 }
725
726 return typeName.toString();
727 }
728
729 return null;
730 }
731
732 /**
733 * Gets the name of a Java accessor method of a given dependency.
734 *
735 * @param dependency The dependency to get a Java accessor method name of.
736 *
737 * @return The Java accessor method name of {@code dependency}.
738 *
739 * @throws NullPointerException if {@code dependency} is {@code null}.
740 */
741 public String getJavaGetterMethodName( final Dependency dependency )
742 {
743 if ( dependency == null )
744 {
745 throw new NullPointerException( "dependency" );
746 }
747
748 return "get" + this.getJavaIdentifier( dependency.getName() );
749 }
750
751 /**
752 * Gets the name of a Java accessor method of a given message.
753 *
754 * @param message The message to get a Java accessor method name of.
755 *
756 * @return The Java accessor method name of {@code message}.
757 *
758 * @throws NullPointerException if {@code message} is {@code null}.
759 */
760 public String getJavaGetterMethodName( final Message message )
761 {
762 if ( message == null )
763 {
764 throw new NullPointerException( "message" );
765 }
766
767 return "get" + this.getJavaIdentifier( message.getName() );
768 }
769
770 /**
771 * Gets the name of a Java modifier of a dependency of a given implementation.
772 *
773 * @param implementation The implementation to get a dependency Java modifier name of.
774 * @param dependency The dependency to get a Java modifier name of.
775 *
776 * @return The Java modifier name of {@code dependency} of {@code implementation}.
777 *
778 * @throws NullPointerException if {@code implementation} or {@code dependency} is {@code null}.
779 */
780 public String getJavaModifierName( final Implementation implementation, final Dependency dependency )
781 {
782 if ( implementation == null )
783 {
784 throw new NullPointerException( "implementation" );
785 }
786 if ( dependency == null )
787 {
788 throw new NullPointerException( "dependency" );
789 }
790
791 return "private";
792 }
793
794 /**
795 * Gets the name of a Java modifier of a message of a given implementation.
796 *
797 * @param implementation The implementation to get a message Java modifier name of.
798 * @param message The message to get a Java modifier name of.
799 *
800 * @return The Java modifier name of {@code message} of {@code implementation}.
801 *
802 * @throws NullPointerException if {@code implementation} or {@code message} is {@code null}.
803 */
804 public String getJavaModifierName( final Implementation implementation, final Message message )
805 {
806 if ( implementation == null )
807 {
808 throw new NullPointerException( "implementation" );
809 }
810 if ( message == null )
811 {
812 throw new NullPointerException( "message" );
813 }
814
815 return "private";
816 }
817
818 /**
819 * Gets the name of a Java modifier for a given property of a given implementation.
820 *
821 * @param implementation The implementation declaring {@code property}.
822 * @param property The property to get a Java modifier name for.
823 *
824 * @return The Java modifier name for {@code property} of {@code implementation}.
825 *
826 * @throws NullPointerException if {@code implementation} or {@code property} is {@code null}.
827 */
828 public String getJavaModifierName( final Implementation implementation, final Property property )
829 {
830 if ( implementation == null )
831 {
832 throw new NullPointerException( "implementation" );
833 }
834 if ( property == null )
835 {
836 throw new NullPointerException( "property" );
837 }
838
839 String modifier = "private";
840 final Properties specified = this.getModules().getSpecifiedProperties( implementation.getIdentifier() );
841
842 if ( specified != null && specified.getProperty( property.getName() ) != null )
843 {
844 modifier = "public";
845 }
846
847 return modifier;
848 }
849
850 /**
851 * Formats a text to a Javadoc comment.
852 *
853 * @param text The text to format to a Javadoc comment.
854 * @param indentationLevel The indentation level of the comment.
855 * @param suffix The text to append to any line breaks.
856 *
857 * @return {@code text} formatted as a Javadoc comment.
858 *
859 * @throws NullPointerException if {@code text} or {@code suffix} is {@code null}.
860 * @throws IllegalArgumentException if {@code indentationLevel} is negative.
861 */
862 public String getJavadocComment( final Text text, final int indentationLevel, final String suffix )
863 {
864 if ( text == null )
865 {
866 throw new NullPointerException( "text" );
867 }
868 if ( suffix == null )
869 {
870 throw new NullPointerException( "suffix" );
871 }
872 if ( indentationLevel < 0 )
873 {
874 throw new IllegalArgumentException( Integer.toString( indentationLevel ) );
875 }
876
877 try
878 {
879 String javadoc = text.getValue();
880
881 if ( javadoc != null )
882 {
883 final String indent = this.getIndentation( indentationLevel );
884 final BufferedReader reader = new BufferedReader( new StringReader( javadoc ) );
885 final StringBuilder builder = new StringBuilder( javadoc.length() );
886
887 String line;
888 while ( ( line = reader.readLine() ) != null )
889 {
890 builder.append( this.getLineSeparator() ).append( indent ).append( suffix ).
891 append( line.replaceAll( "\\/\\*\\*", "/*" ).replaceAll( "\\*/", "/" ) );
892
893 }
894
895 javadoc = builder.length() == 0 ? "" : StringEscapeUtils.escapeHtml(
896 builder.substring( this.getLineSeparator().length() + indent.length() + suffix.length() ) );
897
898 }
899
900 return javadoc;
901 }
902 catch ( final IOException e )
903 {
904 throw new AssertionError( e );
905 }
906 }
907
908 /**
909 * Formats a string to a Java string with unicode escapes.
910 *
911 * @param str The string to format to a Java string or {@code null}.
912 *
913 * @return {@code str} formatted as a Java string or {@code null}.
914 */
915 public String getJavaString( final String str )
916 {
917 return StringEscapeUtils.escapeJava( str );
918 }
919
920 /**
921 * Gets a flag indicating if the class of a given specification is located in the Java default package.
922 *
923 * @param specification The specification to test.
924 *
925 * @return {@code true} if the class of {@code specification} is located in the Java default package; {@code false}
926 * if not.
927 *
928 * @throws NullPointerException if {@code specification} is {@code null}.
929 */
930 public boolean isJavaDefaultPackage( final Specification specification )
931 {
932 if ( specification == null )
933 {
934 throw new NullPointerException( "specification" );
935 }
936
937 return specification.getClazz() != null && this.getJavaPackageName( specification ).length() == 0;
938 }
939
940 /**
941 * Gets a flag indicating if the class of a given implementation is located in the Java default package.
942 *
943 * @param implementation The implementation to test.
944 *
945 * @return {@code true} if the class of {@code implementation} is located in the Java default package; {@code false}
946 * if not.
947 *
948 * @throws NullPointerException if {@code implementation} is {@code null}.
949 */
950 public boolean isJavaDefaultPackage( final Implementation implementation )
951 {
952 if ( implementation == null )
953 {
954 throw new NullPointerException( "implementation" );
955 }
956
957 return implementation.getClazz() != null && this.getJavaPackageName( implementation ).length() == 0;
958 }
959
960 /**
961 * Gets the display language of a given language code.
962 *
963 * @param language The language code to get the display language of.
964 *
965 * @return The display language of {@code language}.
966 *
967 * @throws NullPointerException if {@code language} is {@code null}.
968 */
969 public String getDisplayLanguage( final String language )
970 {
971 if ( language == null )
972 {
973 throw new NullPointerException( "language" );
974 }
975
976 final Locale locale = new Locale( language );
977 return locale.getDisplayLanguage( locale );
978 }
979
980 /**
981 * Formats a calendar instance to a string.
982 *
983 * @param calendar The calendar to format.
984 *
985 * @return Date of {@code calendar} formatted using a short format style pattern.
986 *
987 * @throws NullPointerException if {@code calendar} is {@code null}.
988 *
989 * @see DateFormat#SHORT
990 */
991 public String getShortDate( final Calendar calendar )
992 {
993 if ( calendar == null )
994 {
995 throw new NullPointerException( "calendar" );
996 }
997
998 return DateFormat.getDateInstance( DateFormat.SHORT ).format( calendar.getTime() );
999 }
1000
1001 /**
1002 * Formats a calendar instance to a string.
1003 *
1004 * @param calendar The calendar to format.
1005 *
1006 * @return Date of {@code calendar} formatted using a long format style pattern.
1007 *
1008 * @throws NullPointerException if {@code calendar} is {@code null}.
1009 *
1010 * @see DateFormat#LONG
1011 */
1012 public String getLongDate( final Calendar calendar )
1013 {
1014 if ( calendar == null )
1015 {
1016 throw new NullPointerException( "calendar" );
1017 }
1018
1019 return DateFormat.getDateInstance( DateFormat.LONG ).format( calendar.getTime() );
1020 }
1021
1022 /**
1023 * Formats a calendar instance to a string.
1024 *
1025 * @param calendar The calendar to format.
1026 *
1027 * @return Time of {@code calendar} formatted using a short format style pattern.
1028 *
1029 * @throws NullPointerException if {@code calendar} is {@code null}.
1030 *
1031 * @see DateFormat#SHORT
1032 */
1033 public String getShortTime( final Calendar calendar )
1034 {
1035 if ( calendar == null )
1036 {
1037 throw new NullPointerException( "calendar" );
1038 }
1039
1040 return DateFormat.getTimeInstance( DateFormat.SHORT ).format( calendar.getTime() );
1041 }
1042
1043 /**
1044 * Formats a calendar instance to a string.
1045 *
1046 * @param calendar The calendar to format.
1047 *
1048 * @return Time of {@code calendar} formatted using a long format style pattern.
1049 *
1050 * @throws NullPointerException if {@code calendar} is {@code null}.
1051 *
1052 * @see DateFormat#LONG
1053 */
1054 public String getLongTime( final Calendar calendar )
1055 {
1056 if ( calendar == null )
1057 {
1058 throw new NullPointerException( "calendar" );
1059 }
1060
1061 return DateFormat.getTimeInstance( DateFormat.LONG ).format( calendar.getTime() );
1062 }
1063
1064 /**
1065 * Formats a calendar instance to a string.
1066 *
1067 * @param calendar The calendar to format.
1068 *
1069 * @return Date and time of {@code calendar} formatted using a short format style pattern.
1070 *
1071 * @throws NullPointerException if {@code calendar} is {@code null}.
1072 *
1073 * @see DateFormat#SHORT
1074 */
1075 public String getShortDateTime( final Calendar calendar )
1076 {
1077 if ( calendar == null )
1078 {
1079 throw new NullPointerException( "calendar" );
1080 }
1081
1082 return DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT ).format( calendar.getTime() );
1083 }
1084
1085 /**
1086 * Formats a calendar instance to a string.
1087 *
1088 * @param calendar The calendar to format.
1089 *
1090 * @return Date and time of {@code calendar} formatted using a long format style pattern.
1091 *
1092 * @throws NullPointerException if {@code calendar} is {@code null}.
1093 *
1094 * @see DateFormat#LONG
1095 */
1096 public String getLongDateTime( final Calendar calendar )
1097 {
1098 if ( calendar == null )
1099 {
1100 throw new NullPointerException( "calendar" );
1101 }
1102
1103 return DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG ).format( calendar.getTime() );
1104 }
1105
1106 /**
1107 * Gets a string describing the range of years for given calendars.
1108 *
1109 * @param start The start of the range.
1110 * @param end The end of the range.
1111 *
1112 * @return Formatted range of the years of {@code start} and {@code end}.
1113 *
1114 * @throws NullPointerException if {@code start} or {@code end} is {@code null}.
1115 */
1116 public String getYears( final Calendar start, final Calendar end )
1117 {
1118 if ( start == null )
1119 {
1120 throw new NullPointerException( "start" );
1121 }
1122 if ( end == null )
1123 {
1124 throw new NullPointerException( "end" );
1125 }
1126
1127 final Format yearFormat = new SimpleDateFormat( "yyyy" );
1128 final int s = start.get( Calendar.YEAR );
1129 final int e = end.get( Calendar.YEAR );
1130 final StringBuilder years = new StringBuilder();
1131
1132 if ( s != e )
1133 {
1134 if ( s < e )
1135 {
1136 years.append( yearFormat.format( start.getTime() ) ).append( " - " ).
1137 append( yearFormat.format( end.getTime() ) );
1138
1139 }
1140 else
1141 {
1142 years.append( yearFormat.format( end.getTime() ) ).append( " - " ).
1143 append( yearFormat.format( start.getTime() ) );
1144
1145 }
1146 }
1147 else
1148 {
1149 years.append( yearFormat.format( start.getTime() ) );
1150 }
1151
1152 return years.toString();
1153 }
1154
1155 /**
1156 * Gets the model of the instance.
1157 *
1158 * @return The model of the instance.
1159 *
1160 * @see #getModules()
1161 * @see #setModel(org.jomc.modlet.Model)
1162 */
1163 public Model getModel()
1164 {
1165 if ( this.model == null )
1166 {
1167 this.model = new Model();
1168 this.model.setIdentifier( ModelObject.MODEL_PUBLIC_ID );
1169 }
1170
1171 return this.model;
1172 }
1173
1174 /**
1175 * Sets the model of the instance.
1176 *
1177 * @param value The new model of the instance.
1178 *
1179 * @see #getModel()
1180 */
1181 public void setModel( final Model value )
1182 {
1183 this.model = value;
1184 }
1185
1186 /**
1187 * Gets the modules of the instance.
1188 *
1189 * @return The modules of the instance.
1190 *
1191 * @see #getModel()
1192 * @see #setModel(org.jomc.modlet.Model)
1193 */
1194 public Modules getModules()
1195 {
1196 final Modules modules = ModelHelper.getModules( this.getModel() );
1197 return modules != null ? modules : new Modules();
1198 }
1199
1200 /**
1201 * Gets the {@code VelocityEngine} of the instance.
1202 *
1203 * @return The {@code VelocityEngine} of the instance.
1204 *
1205 * @throws IOException if initializing a new velocity engine fails.
1206 *
1207 * @see #setVelocityEngine(org.apache.velocity.app.VelocityEngine)
1208 */
1209 public VelocityEngine getVelocityEngine() throws IOException
1210 {
1211 if ( this.velocityEngine == null )
1212 {
1213 try
1214 {
1215 final String logPrefix = this.getClass().getName();
1216 final java.util.Properties props = new java.util.Properties();
1217 props.put( "resource.loader", "class" );
1218 props.put( "class.resource.loader.class", VELOCITY_RESOURCE_LOADER );
1219 props.put( "class.resource.loader.cache", Boolean.TRUE.toString() );
1220 props.put( "runtime.references.strict", Boolean.TRUE.toString() );
1221 props.put( "velocimacro.arguments.strict", Boolean.TRUE.toString() );
1222
1223 final VelocityEngine engine = new VelocityEngine();
1224 engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new LogChute()
1225 {
1226
1227 public void init( final RuntimeServices runtimeServices ) throws Exception
1228 {
1229 }
1230
1231 public void log( final int level, final String message )
1232 {
1233 this.log( level, message, null );
1234 }
1235
1236 public void log( final int level, final String message, final Throwable throwable )
1237 {
1238 final StringBuilder b = new StringBuilder();
1239 b.append( logPrefix ).append( ": " ).append( message );
1240 JomcTool.this.log( Level.FINE, b.toString(), throwable );
1241 }
1242
1243 public boolean isLevelEnabled( final int level )
1244 {
1245 return isLoggable( Level.FINE );
1246 }
1247
1248 } );
1249
1250 engine.init( props );
1251 this.velocityEngine = engine;
1252 }
1253 catch ( final Exception e )
1254 {
1255 throw (IOException) new IOException( e.getMessage() ).initCause( e );
1256 }
1257 }
1258
1259 return this.velocityEngine;
1260 }
1261
1262 /**
1263 * Sets the {@code VelocityEngine} of the instance.
1264 *
1265 * @param value The new {@code VelocityEngine} of the instance.
1266 *
1267 * @see #getVelocityEngine()
1268 */
1269 public void setVelocityEngine( final VelocityEngine value )
1270 {
1271 this.velocityEngine = value;
1272 }
1273
1274 /**
1275 * Gets the velocity context used for merging templates.
1276 *
1277 * @return The velocity context used for merging templates.
1278 */
1279 public VelocityContext getVelocityContext()
1280 {
1281 final Date now = new Date();
1282 final VelocityContext ctx = new VelocityContext();
1283 ctx.put( "model", this.getModel() );
1284 ctx.put( "modules", this.getModules() );
1285 ctx.put( "tool", this );
1286 ctx.put( "toolName", this.getClass().getName() );
1287 ctx.put( "toolVersion", getMessage( "projectVersion" ) );
1288 ctx.put( "toolUrl", getMessage( "projectUrl" ) );
1289 ctx.put( "calendar", Calendar.getInstance() );
1290 ctx.put( "now", new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" ).format( now ) );
1291 ctx.put( "year", new SimpleDateFormat( "yyyy" ).format( now ) );
1292 ctx.put( "month", new SimpleDateFormat( "MM" ).format( now ) );
1293 ctx.put( "day", new SimpleDateFormat( "dd" ).format( now ) );
1294 ctx.put( "hour", new SimpleDateFormat( "HH" ).format( now ) );
1295 ctx.put( "minute", new SimpleDateFormat( "mm" ).format( now ) );
1296 ctx.put( "second", new SimpleDateFormat( "ss" ).format( now ) );
1297 ctx.put( "timezone", new SimpleDateFormat( "Z" ).format( now ) );
1298 return ctx;
1299 }
1300
1301 /**
1302 * Gets the encoding to use for reading templates.
1303 *
1304 * @return The encoding to use for reading templates.
1305 *
1306 * @see #setTemplateEncoding(java.lang.String)
1307 */
1308 public final String getTemplateEncoding()
1309 {
1310 if ( this.templateEncoding == null )
1311 {
1312 this.templateEncoding = getMessage( "buildSourceEncoding" );
1313
1314 if ( this.isLoggable( Level.CONFIG ) )
1315 {
1316 this.log( Level.CONFIG, getMessage( "defaultTemplateEncoding", this.getClass().getName(),
1317 this.templateEncoding ), null );
1318
1319 }
1320 }
1321
1322 return this.templateEncoding;
1323 }
1324
1325 /**
1326 * Sets the encoding to use for reading templates.
1327 *
1328 * @param value The new encoding to use for reading templates or {@code null}.
1329 *
1330 * @see #getTemplateEncoding()
1331 */
1332 public final void setTemplateEncoding( final String value )
1333 {
1334 this.templateEncoding = value;
1335 this.velocityEngine = null;
1336 }
1337
1338 /**
1339 * Gets the encoding to use for reading files.
1340 *
1341 * @return The encoding to use for reading files.
1342 *
1343 * @see #setInputEncoding(java.lang.String)
1344 */
1345 public final String getInputEncoding()
1346 {
1347 if ( this.inputEncoding == null )
1348 {
1349 this.inputEncoding = new InputStreamReader( new ByteArrayInputStream( NO_BYTES ) ).getEncoding();
1350 if ( this.isLoggable( Level.CONFIG ) )
1351 {
1352 this.log( Level.CONFIG, getMessage( "defaultInputEncoding", this.getClass().getName(),
1353 this.inputEncoding ), null );
1354
1355 }
1356 }
1357
1358 return this.inputEncoding;
1359 }
1360
1361 /**
1362 * Sets the encoding to use for reading files.
1363 *
1364 * @param value The new encoding to use for reading files or {@code null}.
1365 *
1366 * @see #getInputEncoding()
1367 */
1368 public final void setInputEncoding( final String value )
1369 {
1370 this.inputEncoding = value;
1371 }
1372
1373 /**
1374 * Gets the encoding to use for writing files.
1375 *
1376 * @return The encoding to use for writing files.
1377 *
1378 * @see #setOutputEncoding(java.lang.String)
1379 */
1380 public final String getOutputEncoding()
1381 {
1382 if ( this.outputEncoding == null )
1383 {
1384 this.outputEncoding = new OutputStreamWriter( new ByteArrayOutputStream() ).getEncoding();
1385 if ( this.isLoggable( Level.CONFIG ) )
1386 {
1387 this.log( Level.CONFIG, getMessage( "defaultOutputEncoding", this.getClass().getName(),
1388 this.outputEncoding ), null );
1389
1390 }
1391 }
1392
1393 return this.outputEncoding;
1394 }
1395
1396 /**
1397 * Sets the encoding to use for writing files.
1398 *
1399 * @param value The encoding to use for writing files or {@code null}.
1400 *
1401 * @see #getOutputEncoding()
1402 */
1403 public final void setOutputEncoding( final String value )
1404 {
1405 this.outputEncoding = value;
1406 }
1407
1408 /**
1409 * Gets the default template profile.
1410 * <p>The default template profile is controlled by system property
1411 * {@code org.jomc.tools.JomcTool.defaultTemplateProfile} holding the name of the template profile to use by
1412 * default. If that property is not set, the {@code jomc-java} default is returned.</p>
1413 *
1414 * @return The default template profile.
1415 *
1416 * @see #setDefaultTemplateProfile(java.lang.String)
1417 */
1418 public static String getDefaultTemplateProfile()
1419 {
1420 if ( defaultTemplateProfile == null )
1421 {
1422 defaultTemplateProfile = System.getProperty( "org.jomc.tools.JomcTool.defaultTemplateProfile",
1423 DEFAULT_TEMPLATE_PROFILE );
1424
1425 }
1426
1427 return defaultTemplateProfile;
1428 }
1429
1430 /**
1431 * Sets the default template profile.
1432 *
1433 * @param value The new default template profile or {@code null}.
1434 *
1435 * @see #getDefaultTemplateProfile()
1436 */
1437 public static void setDefaultTemplateProfile( final String value )
1438 {
1439 defaultTemplateProfile = value;
1440 }
1441
1442 /**
1443 * Gets the template profile of the instance.
1444 *
1445 * @return The template profile of the instance.
1446 *
1447 * @see #getDefaultTemplateProfile()
1448 * @see #setTemplateProfile(java.lang.String)
1449 */
1450 public final String getTemplateProfile()
1451 {
1452 if ( this.templateProfile == null )
1453 {
1454 this.templateProfile = getDefaultTemplateProfile();
1455 if ( this.isLoggable( Level.CONFIG ) )
1456 {
1457 this.log( Level.CONFIG, getMessage( "defaultTemplateProfile", this.getClass().getName(),
1458 this.templateProfile ), null );
1459
1460 }
1461 }
1462
1463 return this.templateProfile;
1464 }
1465
1466 /**
1467 * Sets the template profile of the instance.
1468 *
1469 * @param value The new template profile of the instance or {@code null}.
1470 *
1471 * @see #getTemplateProfile()
1472 */
1473 public final void setTemplateProfile( final String value )
1474 {
1475 this.templateProfile = value;
1476 }
1477
1478 /**
1479 * Gets the indentation string of the instance.
1480 *
1481 * @return The indentation string of the instance.
1482 */
1483 public final String getIndentation()
1484 {
1485 if ( this.indentation == null )
1486 {
1487 this.indentation = " ";
1488 if ( this.isLoggable( Level.CONFIG ) )
1489 {
1490 this.log( Level.CONFIG, getMessage( "defaultIndentation", this.getClass().getName(),
1491 StringEscapeUtils.escapeJava( this.indentation ) ), null );
1492
1493 }
1494 }
1495
1496 return this.indentation;
1497 }
1498
1499 /**
1500 * Gets an indentation string for a given indentation level.
1501 *
1502 * @param level The indentation level to get an indentation string for.
1503 *
1504 * @return The indentation string for {@code level}.
1505 *
1506 * @throws IllegalArgumentException if {@code level} is negative.
1507 *
1508 * @see #getIndentation()
1509 */
1510 public final String getIndentation( final int level )
1511 {
1512 if ( level < 0 )
1513 {
1514 throw new IllegalArgumentException( Integer.toString( level ) );
1515 }
1516
1517 synchronized ( this.indentationCache )
1518 {
1519 String idt = this.indentationCache.get( level );
1520
1521 if ( idt == null )
1522 {
1523 final StringBuilder b = new StringBuilder( this.getIndentation().length() * level );
1524
1525 for ( int i = level; i > 0; i-- )
1526 {
1527 b.append( this.getIndentation() );
1528 }
1529
1530 idt = b.toString();
1531 this.indentationCache.put( level, idt );
1532 }
1533
1534 return idt;
1535 }
1536 }
1537
1538 /**
1539 * Sets the indentation string of the instance.
1540 *
1541 * @param value The new indentation string of the instance or {@code null}.
1542 *
1543 * @see #getIndentation()
1544 */
1545 public final void setIndentation( final String value )
1546 {
1547 synchronized ( this.indentationCache )
1548 {
1549 this.indentation = value;
1550 this.indentationCache.clear();
1551 }
1552 }
1553
1554 /**
1555 * Gets the line separator of the instance.
1556 *
1557 * @return The line separator of the instance.
1558 */
1559 public final String getLineSeparator()
1560 {
1561 if ( this.lineSeparator == null )
1562 {
1563 this.lineSeparator = System.getProperty( "line.separator", "\n" );
1564 if ( this.isLoggable( Level.CONFIG ) )
1565 {
1566 this.log( Level.CONFIG, getMessage( "defaultLineSeparator", this.getClass().getName(),
1567 StringEscapeUtils.escapeJava( this.lineSeparator ) ), null );
1568
1569 }
1570 }
1571
1572 return this.lineSeparator;
1573 }
1574
1575 /**
1576 * Sets the line separator of the instance.
1577 *
1578 * @param value The new line separator of the instance or {@code null}.
1579 *
1580 * @see #getLineSeparator()
1581 */
1582 public final void setLineSeparator( final String value )
1583 {
1584 this.lineSeparator = value;
1585 }
1586
1587 /**
1588 * Gets a velocity template for a given name.
1589 * <p>This method returns the template corresponding to the profile of the instance. If that template is not found,
1590 * the template of the default profile is returned so that only templates differing from the default templates need
1591 * to be provided when exchanging templates.</p>
1592 *
1593 * @param templateName The name of the template to get.
1594 *
1595 * @return The template matching {@code templateName}.
1596 *
1597 * @throws NullPointerException if {@code templateName} is {@code null}.
1598 * @throws IOException if getting the template fails.
1599 *
1600 * @see #getTemplateProfile()
1601 * @see #getTemplateEncoding()
1602 */
1603 public Template getVelocityTemplate( final String templateName ) throws IOException
1604 {
1605 if ( templateName == null )
1606 {
1607 throw new NullPointerException( "templateName" );
1608 }
1609
1610 try
1611 {
1612 final Template template = this.getVelocityEngine().getTemplate(
1613 TEMPLATE_PREFIX + this.getTemplateProfile() + "/" + templateName, this.getTemplateEncoding() );
1614
1615 if ( this.isLoggable( Level.FINE ) )
1616 {
1617 this.log( Level.FINE, getMessage( "templateInfo", this.getClass().getName(), templateName,
1618 this.getTemplateProfile() ), null );
1619
1620 }
1621
1622 return template;
1623 }
1624 catch ( final ResourceNotFoundException e )
1625 {
1626 if ( this.isLoggable( Level.FINE ) )
1627 {
1628 this.log( Level.FINE, getMessage( "templateNotFound", this.getClass().getName(), templateName,
1629 this.getTemplateProfile() ), null );
1630
1631 }
1632
1633 try
1634 {
1635 final Template template = this.getVelocityEngine().getTemplate(
1636 TEMPLATE_PREFIX + getDefaultTemplateProfile() + "/" + templateName, this.getTemplateEncoding() );
1637
1638 if ( this.isLoggable( Level.FINE ) )
1639 {
1640 this.log( Level.FINE, getMessage( "templateInfo", this.getClass().getName(), templateName,
1641 getDefaultTemplateProfile() ), null );
1642
1643 }
1644
1645 return template;
1646 }
1647 catch ( final Exception e2 )
1648 {
1649 throw (IOException) new IOException( getMessage( "failedGettingTemplate",
1650 templateName ) ).initCause( e2 );
1651
1652 }
1653 }
1654 catch ( final Exception e )
1655 {
1656 throw (IOException) new IOException( getMessage( "failedGettingTemplate", templateName ) ).initCause( e );
1657 }
1658 }
1659
1660 /**
1661 * Notifies registered listeners.
1662 *
1663 * @param level The level of the event.
1664 * @param message The message of the event or {@code null}.
1665 * @param throwable The throwable of the event or {@code null}.
1666 *
1667 * @throws NullPointerException if {@code level} is {@code null}.
1668 *
1669 * @see #getListeners()
1670 */
1671 protected void log( final Level level, final String message, final Throwable throwable )
1672 {
1673 if ( level == null )
1674 {
1675 throw new NullPointerException( "level" );
1676 }
1677
1678 if ( this.isLoggable( level ) )
1679 {
1680 for ( Listener l : this.getListeners() )
1681 {
1682 l.onLog( level, message, throwable );
1683 }
1684 }
1685 }
1686
1687 private String getJavaPackageName( final String identifier )
1688 {
1689 if ( identifier == null )
1690 {
1691 throw new NullPointerException( "identifier" );
1692 }
1693
1694 final int idx = identifier.lastIndexOf( '.' );
1695 return idx != -1 ? identifier.substring( 0, idx ) : "";
1696 }
1697
1698 private String getJavaIdentifier( final String identifier )
1699 {
1700 final StringBuilder builder = new StringBuilder();
1701 boolean capitalize = true;
1702
1703 for ( int i = 0; i < identifier.length(); i++ )
1704 {
1705 final char c = identifier.charAt( i );
1706
1707 if ( Character.isWhitespace( c ) )
1708 {
1709 capitalize = true;
1710 }
1711 else if ( i == 0 ? Character.isJavaIdentifierStart( c ) : Character.isJavaIdentifierPart( c ) )
1712 {
1713 builder.append( capitalize ? Character.toUpperCase( c ) : c );
1714 capitalize = false;
1715 }
1716 }
1717
1718 return builder.toString();
1719 }
1720
1721 private static String getMessage( final String key, final Object... arguments )
1722 {
1723 return MessageFormat.format( ResourceBundle.getBundle( JomcTool.class.getName().replace( '.', '/' ) ).
1724 getString( key ), arguments );
1725
1726 }
1727
1728 }