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 }