001 /*
002 * Copyright (C) Christian Schulte, 2005-206
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without
006 * modification, are permitted provided that the following conditions
007 * are met:
008 *
009 * o Redistributions of source code must retain the above copyright
010 * notice, this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright
013 * notice, this list of conditions and the following disclaimer in
014 * the documentation and/or other materials provided with the
015 * distribution.
016 *
017 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 * $JOMC: ResourceFileProcessor.java 3868 2011-10-14 13:23:09Z schulte2005 $
029 *
030 */
031 package org.jomc.tools;
032
033 import java.io.File;
034 import java.io.FileOutputStream;
035 import java.io.IOException;
036 import java.io.OutputStream;
037 import java.text.MessageFormat;
038 import java.util.HashMap;
039 import java.util.Locale;
040 import java.util.Map;
041 import java.util.Properties;
042 import java.util.ResourceBundle;
043 import java.util.logging.Level;
044 import org.apache.velocity.VelocityContext;
045 import org.jomc.model.Implementation;
046 import org.jomc.model.Message;
047 import org.jomc.model.Messages;
048 import org.jomc.model.Module;
049 import org.jomc.model.Specification;
050 import org.jomc.model.Text;
051
052 /**
053 * Processes resource files.
054 *
055 * <p><b>Use Cases:</b><br/><ul>
056 * <li>{@link #writeResourceBundleResourceFiles(File) }</li>
057 * <li>{@link #writeResourceBundleResourceFiles(Module, File) }</li>
058 * <li>{@link #writeResourceBundleResourceFiles(Specification, File) }</li>
059 * <li>{@link #writeResourceBundleResourceFiles(Implementation, File) }</li>
060 * </ul></p>
061 *
062 * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
063 * @version $JOMC: ResourceFileProcessor.java 3868 2011-10-14 13:23:09Z schulte2005 $
064 *
065 * @see #getModules()
066 */
067 public class ResourceFileProcessor extends JomcTool
068 {
069
070 /** The language of the default language properties file of generated resource bundle resources. */
071 private Locale resourceBundleDefaultLocale;
072
073 /** Creates a new {@code ResourceFileProcessor} instance. */
074 public ResourceFileProcessor()
075 {
076 super();
077 }
078
079 /**
080 * Creates a new {@code ResourceFileProcessor} instance taking a {@code ResourceFileProcessor} instance to
081 * initialize the instance with.
082 *
083 * @param tool The instance to initialize the new instance with.
084 *
085 * @throws NullPointerException if {@code tool} is {@code null}.
086 * @throws IOException if copying {@code tool} fails.
087 */
088 public ResourceFileProcessor( final ResourceFileProcessor tool ) throws IOException
089 {
090 super( tool );
091 this.resourceBundleDefaultLocale = tool.resourceBundleDefaultLocale;
092 }
093
094 /**
095 * Gets the language of the default language properties file of generated resource bundle resource files.
096 *
097 * @return The language of the default language properties file of generated resource bundle resource files.
098 *
099 * @see #setResourceBundleDefaultLocale(java.util.Locale)
100 */
101 public final Locale getResourceBundleDefaultLocale()
102 {
103 if ( this.resourceBundleDefaultLocale == null )
104 {
105 this.resourceBundleDefaultLocale = Locale.ENGLISH;
106
107 if ( this.isLoggable( Level.CONFIG ) )
108 {
109 this.log( Level.CONFIG,
110 getMessage( "defaultResourceBundleDefaultLocale", this.resourceBundleDefaultLocale ), null );
111
112 }
113 }
114
115 return this.resourceBundleDefaultLocale;
116 }
117
118 /**
119 * Sets the language of the default language properties file of generated resource bundle resource files.
120 *
121 * @param value The language of the default language properties file of generated resource bundle resource files.
122 *
123 * @see #getResourceBundleDefaultLocale()
124 */
125 public final void setResourceBundleDefaultLocale( final Locale value )
126 {
127 this.resourceBundleDefaultLocale = value;
128 }
129
130 /**
131 * Writes resource bundle resource files of the modules of the instance to a given directory.
132 *
133 * @param resourcesDirectory The directory to write resource bundle resource files to.
134 *
135 * @throws NullPointerException if {@code resourcesDirectory} is {@code null}.
136 * @throws IOException if writing resource bundle resource files fails.
137 *
138 * @see #writeResourceBundleResourceFiles(org.jomc.model.Module, java.io.File)
139 */
140 public void writeResourceBundleResourceFiles( final File resourcesDirectory ) throws IOException
141 {
142 if ( resourcesDirectory == null )
143 {
144 throw new NullPointerException( "resourcesDirectory" );
145 }
146
147 for ( int i = 0, s0 = this.getModules().getModule().size(); i < s0; i++ )
148 {
149 this.writeResourceBundleResourceFiles( this.getModules().getModule().get( i ), resourcesDirectory );
150 }
151 }
152
153 /**
154 * Writes resource bundle resource files of a given module from the modules of the instance to a given directory.
155 *
156 * @param module The module to process.
157 * @param resourcesDirectory The directory to write resource bundle resource files to.
158 *
159 * @throws NullPointerException if {@code module} or {@code resourcesDirectory} is {@code null}.
160 * @throws IOException if writing resource bundle resource files fails.
161 *
162 * @see #writeResourceBundleResourceFiles(org.jomc.model.Specification, java.io.File)
163 * @see #writeResourceBundleResourceFiles(org.jomc.model.Implementation, java.io.File)
164 */
165 public void writeResourceBundleResourceFiles( final Module module, final File resourcesDirectory )
166 throws IOException
167 {
168 if ( module == null )
169 {
170 throw new NullPointerException( "module" );
171 }
172 if ( resourcesDirectory == null )
173 {
174 throw new NullPointerException( "resourcesDirectory" );
175 }
176
177 assert this.getModules().getModule( module.getName() ) != null : "Module '" + module.getName() + "' not found.";
178
179 if ( module.getSpecifications() != null )
180 {
181 for ( int i = 0, s0 = module.getSpecifications().getSpecification().size(); i < s0; i++ )
182 {
183 this.writeResourceBundleResourceFiles( module.getSpecifications().getSpecification().get( i ),
184 resourcesDirectory );
185
186 }
187 }
188
189 if ( module.getImplementations() != null )
190 {
191 for ( int i = 0, s0 = module.getImplementations().getImplementation().size(); i < s0; i++ )
192 {
193 this.writeResourceBundleResourceFiles( module.getImplementations().getImplementation().get( i ),
194 resourcesDirectory );
195
196 }
197 }
198 }
199
200 /**
201 * Writes resource bundle resource files of a given specification from the modules of the instance to a directory.
202 *
203 * @param specification The specification to process.
204 * @param resourcesDirectory The directory to write resource bundle resource files to.
205 *
206 * @throws NullPointerException if {@code specification} or {@code resourcesDirectory} is {@code null}.
207 * @throws IOException if writing resource bundle resource files fails.
208 *
209 * @see #getResourceBundleResources(org.jomc.model.Specification)
210 */
211 public void writeResourceBundleResourceFiles( final Specification specification, final File resourcesDirectory )
212 throws IOException
213 {
214 if ( specification == null )
215 {
216 throw new NullPointerException( "implementation" );
217 }
218 if ( resourcesDirectory == null )
219 {
220 throw new NullPointerException( "resourcesDirectory" );
221 }
222
223 assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
224 "Specification '" + specification.getIdentifier() + "' not found.";
225
226 if ( specification.isClassDeclaration() )
227 {
228 if ( !resourcesDirectory.isDirectory() )
229 {
230 throw new IOException( getMessage( "directoryNotFound", resourcesDirectory.getAbsolutePath() ) );
231 }
232
233 this.assertValidTemplates( specification );
234
235 final String bundlePath =
236 this.getJavaTypeName( specification, true ).replace( '.', File.separatorChar );
237
238 this.writeResourceBundleResourceFiles(
239 this.getResourceBundleResources( specification ), resourcesDirectory, bundlePath );
240
241 }
242 }
243
244 /**
245 * Writes resource bundle resource files of a given implementation from the modules of the instance to a directory.
246 *
247 * @param implementation The implementation to process.
248 * @param resourcesDirectory The directory to write resource bundle resource files to.
249 *
250 * @throws NullPointerException if {@code implementation} or {@code resourcesDirectory} is {@code null}.
251 * @throws IOException if writing resource bundle resource files fails.
252 *
253 * @see #getResourceBundleResources(org.jomc.model.Implementation)
254 */
255 public void writeResourceBundleResourceFiles( final Implementation implementation, final File resourcesDirectory )
256 throws IOException
257 {
258 if ( implementation == null )
259 {
260 throw new NullPointerException( "implementation" );
261 }
262 if ( resourcesDirectory == null )
263 {
264 throw new NullPointerException( "resourcesDirectory" );
265 }
266
267 assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
268 "Implementation '" + implementation.getIdentifier() + "' not found.";
269
270 if ( implementation.isClassDeclaration() )
271 {
272 if ( !resourcesDirectory.isDirectory() )
273 {
274 throw new IOException( getMessage( "directoryNotFound", resourcesDirectory.getAbsolutePath() ) );
275 }
276
277 this.assertValidTemplates( implementation );
278
279 final String bundlePath =
280 this.getJavaTypeName( implementation, true ).replace( '.', File.separatorChar );
281
282 this.writeResourceBundleResourceFiles(
283 this.getResourceBundleResources( implementation ), resourcesDirectory, bundlePath );
284
285 }
286 }
287
288 /**
289 * Gets resource bundle properties resources of a given specification.
290 *
291 * @param specification The specification to get resource bundle properties resources of.
292 *
293 * @return Resource bundle properties resources of {@code specification}.
294 *
295 * @throws NullPointerException if {@code specification} is {@code null}.
296 * @throws IOException if getting the resource bundle properties resources fails.
297 */
298 public Map<Locale, Properties> getResourceBundleResources( final Specification specification )
299 throws IOException
300 {
301 if ( specification == null )
302 {
303 throw new NullPointerException( "specification" );
304 }
305
306 assert this.getModules().getSpecification( specification.getIdentifier() ) != null :
307 "Specification '" + specification.getIdentifier() + "' not found.";
308
309 return new HashMap<Locale, Properties>();
310 }
311
312 /**
313 * Gets resource bundle properties resources of a given implementation.
314 *
315 * @param implementation The implementation to get resource bundle properties resources of.
316 *
317 * @return Resource bundle properties resources of {@code implementation}.
318 *
319 * @throws NullPointerException if {@code implementation} is {@code null}.
320 * @throws IOException if getting the resource bundle properties resources fails.
321 */
322 public Map<Locale, Properties> getResourceBundleResources( final Implementation implementation )
323 throws IOException
324 {
325 if ( implementation == null )
326 {
327 throw new NullPointerException( "implementation" );
328 }
329
330 assert this.getModules().getImplementation( implementation.getIdentifier() ) != null :
331 "Implementation '" + implementation.getIdentifier() + "' not found.";
332
333 final Map<Locale, java.util.Properties> properties = new HashMap<Locale, java.util.Properties>( 10 );
334 final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
335
336 if ( messages != null )
337 {
338 for ( int i = 0, s0 = messages.getMessage().size(); i < s0; i++ )
339 {
340 final Message message = messages.getMessage().get( i );
341
342 if ( message.getTemplate() != null )
343 {
344 for ( int j = 0, s1 = message.getTemplate().getText().size(); j < s1; j++ )
345 {
346 final Text text = message.getTemplate().getText().get( j );
347 final Locale locale = new Locale( text.getLanguage().toLowerCase() );
348 Properties bundleProperties = properties.get( locale );
349
350 if ( bundleProperties == null )
351 {
352 bundleProperties = new Properties();
353 properties.put( locale, bundleProperties );
354 }
355
356 bundleProperties.setProperty( message.getName(), text.getValue() );
357 }
358 }
359 }
360 }
361
362 return properties;
363 }
364
365 private void writeResourceBundleResourceFiles( final Map<Locale, Properties> resources,
366 final File resourcesDirectory, final String bundlePath )
367 throws IOException
368 {
369 if ( resources == null )
370 {
371 throw new NullPointerException( "resources" );
372 }
373 if ( resourcesDirectory == null )
374 {
375 throw new NullPointerException( "resourcesDirectory" );
376 }
377 if ( bundlePath == null )
378 {
379 throw new NullPointerException( "bundlePath" );
380 }
381
382 Properties defProperties = null;
383 Properties fallbackProperties = null;
384
385 final VelocityContext ctx = this.getVelocityContext();
386 final String toolName = ctx.get( "toolName" ).toString();
387 final String toolVersion = ctx.get( "toolVersion" ).toString();
388 final String toolUrl = ctx.get( "toolUrl" ).toString();
389
390 for ( Map.Entry<Locale, Properties> e : resources.entrySet() )
391 {
392 final String language = e.getKey().getLanguage().toLowerCase();
393 final Properties p = e.getValue();
394 final File file = new File( resourcesDirectory, bundlePath + "_" + language + ".properties" );
395
396 if ( this.getResourceBundleDefaultLocale().getLanguage().equalsIgnoreCase( language ) )
397 {
398 defProperties = p;
399 }
400
401 fallbackProperties = p;
402
403 if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() )
404 {
405 throw new IOException( getMessage( "failedCreatingDirectory",
406 file.getParentFile().getAbsolutePath() ) );
407
408 }
409
410 if ( this.isLoggable( Level.INFO ) )
411 {
412 this.log( Level.INFO, getMessage( "writing", file.getCanonicalPath() ), null );
413 }
414
415 OutputStream out = null;
416 boolean suppressExceptionOnClose = true;
417 try
418 {
419 out = new FileOutputStream( file );
420 p.store( out, toolName + ' ' + toolVersion + " - See " + toolUrl );
421 suppressExceptionOnClose = false;
422 }
423 finally
424 {
425 try
426 {
427 if ( out != null )
428 {
429 out.close();
430 }
431 }
432 catch ( final IOException ex )
433 {
434 if ( suppressExceptionOnClose )
435 {
436 this.log( Level.SEVERE, getMessage( ex ), ex );
437 }
438 else
439 {
440 throw ex;
441 }
442 }
443 }
444 }
445
446 if ( defProperties == null )
447 {
448 defProperties = fallbackProperties;
449 }
450
451 if ( defProperties != null )
452 {
453 final File file = new File( resourcesDirectory, bundlePath + ".properties" );
454 if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() )
455 {
456 throw new IOException( getMessage( "failedCreatingDirectory",
457 file.getParentFile().getAbsolutePath() ) );
458
459 }
460
461 if ( this.isLoggable( Level.INFO ) )
462 {
463 this.log( Level.INFO, getMessage( "writing", file.getCanonicalPath() ), null );
464 }
465
466 OutputStream out = null;
467 boolean suppressExceptionOnClose = true;
468 try
469 {
470 out = new FileOutputStream( file );
471 defProperties.store( out, toolName + ' ' + toolVersion + " - See " + toolUrl );
472 suppressExceptionOnClose = false;
473 }
474 finally
475 {
476 try
477 {
478 if ( out != null )
479 {
480 out.close();
481 }
482 }
483 catch ( final IOException e )
484 {
485 if ( suppressExceptionOnClose )
486 {
487 this.log( Level.SEVERE, getMessage( e ), e );
488 }
489 else
490 {
491 throw e;
492 }
493 }
494 }
495 }
496 }
497
498 private void assertValidTemplates( final Specification specification )
499 {
500 if ( specification == null )
501 {
502 throw new NullPointerException( "specification" );
503 }
504 }
505
506 private void assertValidTemplates( final Implementation implementation )
507 {
508 if ( implementation == null )
509 {
510 throw new NullPointerException( "implementation" );
511 }
512
513 final Messages messages = this.getModules().getMessages( implementation.getIdentifier() );
514
515 if ( messages != null )
516 {
517 for ( int i = messages.getMessage().size() - 1; i >= 0; i-- )
518 {
519 final Message m = messages.getMessage().get( i );
520
521 if ( m.getTemplate() != null )
522 {
523 for ( int j = m.getTemplate().getText().size() - 1; j >= 0; j-- )
524 {
525 new MessageFormat( m.getTemplate().getText().get( j ).getValue() );
526 }
527 }
528 }
529 }
530 }
531
532 private static String getMessage( final String key, final Object... arguments )
533 {
534 if ( key == null )
535 {
536 throw new NullPointerException( "key" );
537 }
538
539 return MessageFormat.format( ResourceBundle.getBundle(
540 ResourceFileProcessor.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
541
542 }
543
544 private static String getMessage( final Throwable t )
545 {
546 return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null;
547 }
548
549 }