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