001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.shared.ldap.util;
021
022
023
024 import java.io.File;
025 import java.io.InputStream;
026 import java.io.IOException;
027 import java.io.FileInputStream;
028 import java.util.Enumeration;
029 import java.util.Hashtable;
030 import java.util.Properties;
031
032 import org.apache.directory.shared.ldap.NotImplementedException;
033
034
035 /**
036 * A utility class used for accessing, finding, merging and macro expanding
037 * properties, on disk, via URLS or as resources.
038 *
039 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040 * @version $Rev: 686082 $
041 */
042 public class PropertiesUtils
043 {
044 /** default properties file extension */
045 private static final String DOTPROPERTIES = ".properties";
046
047
048 // ------------------------------------------------------------------------
049 // Utilities for discovering Properties
050 // ------------------------------------------------------------------------
051
052 /**
053 * Loads a properties object in a properties file if it exists relative to
054 * the filename ${user.home}. If the file ${user.home}/[filename] does not
055 * exist then one last attempt to find the file is made if filename does not
056 * have a .properties extension. If so and
057 * ${user.home}/[filename].properties exists then it is loaded.
058 *
059 * @param filename
060 * the properties file name with or without an extension
061 * @return the user properties object
062 */
063 public static Properties findUserProperties( String filename )
064 {
065 return findProperties( new File( System.getProperty( "user.home" ) ), filename );
066 }
067
068
069 /**
070 * Create a new properties object and load the properties file if it exists
071 * relative to [dir]/[filename] or [dir]/[filename].properties.
072 *
073 * @param dir
074 * the base directory
075 * @param filename
076 * the full fine name or the base name w/o the extension
077 * @return the loaded properties object
078 */
079 public static Properties findProperties( File dir, String filename )
080 {
081 final File asis = new File( dir, filename );
082
083 if ( asis.exists() )
084 {
085 return getProperties( asis );
086 }
087
088 if ( filename.endsWith( DOTPROPERTIES ) )
089 {
090 String noExt = filename.substring( 0, filename.length() - 11 );
091 if ( new File( dir, noExt ).exists() )
092 {
093 return getProperties( new File( dir, noExt ) );
094 }
095
096 return new Properties();
097 }
098
099 File withExt = new File( dir, filename + DOTPROPERTIES );
100 if ( withExt.exists() )
101 {
102 return getProperties( withExt );
103 }
104
105 return new Properties();
106 }
107
108
109 /**
110 * Load a properties from a resource relative to a supplied class. First an
111 * attempt is made to locate a property file colocated with the class with
112 * the name [class].properties. If this cannot be found or errors result an
113 * empty Properties file is returned.
114 *
115 * @param ref
116 * a class to use for relative path references
117 * @return the static properties
118 */
119 public static Properties getStaticProperties( Class<?> ref )
120 {
121 final Properties properties = new Properties();
122 final String address = ref.toString().replace( '.', '/' );
123 final String path = address + ".properties";
124 InputStream input = ref.getResourceAsStream( path );
125
126 if ( null != input )
127 {
128 try
129 {
130 properties.load( input );
131 }
132 catch ( IOException e )
133 {
134 return properties;
135 }
136 }
137
138 return properties;
139 }
140
141
142 /**
143 * Load properties from a resource relative to a supplied class and path.
144 *
145 * @param ref
146 * a class to use for relative path references
147 * @param path
148 * the relative path to the resoruce
149 * @return the static properties
150 */
151 public static Properties getStaticProperties( Class<?> ref, String path )
152 {
153 Properties properties = new Properties();
154 InputStream input = ref.getResourceAsStream( path );
155
156 if ( input == null )
157 {
158 return properties;
159 }
160
161 try
162 {
163 properties.load( input );
164 }
165 catch ( IOException e )
166 {
167 return properties;
168 }
169
170 return properties;
171 }
172
173
174 /**
175 * Creates a properties object and loads the properties in the file
176 * otherwise and empty property object will be returned.
177 *
178 * @param file
179 * the properties file
180 * @return the properties object
181 */
182 public static Properties getProperties( File file )
183 {
184 Properties properties = new Properties();
185
186 if ( null == file )
187 {
188 return properties;
189 }
190
191 if ( file.exists() )
192 {
193 try
194 {
195 final FileInputStream fis = new FileInputStream( file );
196 try
197 {
198 properties.load( fis );
199 }
200 finally
201 {
202 fis.close();
203 }
204 }
205 catch ( IOException e )
206 {
207 return properties;
208 }
209 }
210
211 return properties;
212 }
213
214
215 /**
216 * Loads a properties file as a CL resource if it exists and returns an
217 * empty Properties object otherwise.
218 *
219 * @param classloader
220 * the loader to use for the resources
221 * @param path
222 * the path to the resource
223 * @return the loaded or new Properties
224 */
225 public static Properties getProperties( ClassLoader classloader, String path )
226 {
227 Properties properties = new Properties();
228 InputStream input = classloader.getResourceAsStream( path );
229
230 if ( input != null )
231 {
232 try
233 {
234 properties.load( input );
235 }
236 catch ( IOException e )
237 {
238 return properties;
239 }
240 }
241
242 return properties;
243 }
244
245
246 /**
247 * Loads a properties file as a class resource if it exists and returns an
248 * empty Properties object otherwise.
249 *
250 * @param clazz
251 * the class to use for resolving the resources
252 * @param path
253 * the relative path to the resource
254 * @return the loaded or new Properties
255 */
256 public static Properties getProperties( Class<?> clazz, String path )
257 {
258 Properties properties = new Properties();
259 InputStream input = clazz.getResourceAsStream( path );
260
261 if ( input != null )
262 {
263 try
264 {
265 properties.load( input );
266 }
267 catch ( IOException e )
268 {
269 return properties;
270 }
271 }
272
273 return properties;
274 }
275
276
277 // ------------------------------------------------------------------------
278 // Utilities for operating on or setting Properties values
279 // ------------------------------------------------------------------------
280
281 /**
282 * Expands out a set of property key macros in the following format
283 * ${foo.bar} where foo.bar is a property key, by dereferencing the value of
284 * the key using the original source Properties and other optional
285 * Properties. If the original expanded Properties contain the value for the
286 * macro key, foo.bar, then dereferencing stops by using the value in the
287 * expanded Properties: the other optional Properties are NOT used at all.
288 * If the original expanded Properties do NOT contain the value for the
289 * macro key, then the optional Properties are used in order. The first of
290 * the optionals to contain the value for the macro key (foo.bar) shorts the
291 * search. Hence the first optional Properties in the array to contain a
292 * value for the macro key (foo.bar) is used to set the expanded value. If a
293 * macro cannot be expanded because it's key was not defined within the
294 * expanded Properties or one of the optional Properties then it is left as
295 * is.
296 *
297 * @param expanded
298 * the Properties to perform the macro expansion upon
299 * @param optionals
300 * null or an optional set of Properties to use for dereferencing
301 * macro keys (foo.bar)
302 */
303 public static void macroExpand( Properties expanded, Properties[] optionals )
304 {
305 // Handle null optionals
306 if ( null == optionals )
307 {
308 optionals = new Properties[0];
309 }
310
311 Enumeration<?> list = expanded.propertyNames();
312
313 while ( list.hasMoreElements() )
314 {
315 String key = ( String ) list.nextElement();
316 String macro = expanded.getProperty( key );
317
318 int n = macro.indexOf( "${" );
319 if ( n < 0 )
320 {
321 continue;
322 }
323
324 int m = macro.indexOf( "}", n + 2 );
325 if ( m < 0 )
326 {
327 continue;
328 }
329
330 final String symbol = macro.substring( n + 2, m );
331
332 if ( expanded.containsKey( symbol ) )
333 {
334 final String value = expanded.getProperty( symbol );
335 final String head = macro.substring( 0, n );
336 final String tail = macro.substring( m + 1 );
337 final String resolved = head + value + tail;
338 expanded.put( key, resolved );
339 continue;
340 }
341
342 /*
343 * Check if the macro key exists within the array of optional
344 * Properties. Set expanded value to first Properties with the key
345 * and break out of the loop.
346 */
347 for ( int ii = 0; ii < optionals.length; ii++ )
348 {
349 if ( optionals[ii].containsKey( symbol ) )
350 {
351 final String value = optionals[ii].getProperty( symbol );
352 final String head = macro.substring( 0, n );
353 final String tail = macro.substring( m + 1 );
354 final String resolved = head + value + tail;
355 expanded.put( key, resolved );
356 break;
357 }
358 }
359 }
360 }
361
362
363 /**
364 * Discovers a value within a set of Properties either halting on the first
365 * time the property is discovered or continuing on to take the last value
366 * found for the property key.
367 *
368 * @param key
369 * a property key
370 * @param sources
371 * a set of source Properties
372 * @param haltOnDiscovery
373 * true if we stop on finding a value, false otherwise
374 * @return the value found or null
375 */
376 public static String discover( String key, Properties[] sources, boolean haltOnDiscovery )
377 {
378 String retval = null;
379
380 for ( int ii = 0; ii < sources.length; ii++ )
381 {
382 if ( sources[ii].containsKey( key ) )
383 {
384 retval = sources[ii].getProperty( key );
385
386 if ( haltOnDiscovery )
387 {
388 break;
389 }
390 }
391 }
392
393 return retval;
394 }
395
396
397 /**
398 * Merges a set of properties from source Properties into a target
399 * properties instance containing keys. This method does not allow null
400 * overrides.
401 *
402 * @param keys
403 * the keys to discover values for
404 * @param sources
405 * the sources to search
406 * @param haltOnDiscovery
407 * true to halt on first find or false to continue to last find
408 */
409 public static void discover( Properties keys, Properties[] sources, boolean haltOnDiscovery )
410 {
411 if ( null == sources || null == keys )
412 {
413 return;
414 }
415
416 /*
417 * H A N D L E S I N G L E V A L U E D K E Y S
418 */
419 for ( Object key:keys.keySet() )
420 {
421 String value = discover( (String)key, sources, haltOnDiscovery );
422
423 if ( value != null )
424 {
425 keys.setProperty( (String)key, value );
426 }
427 }
428 }
429
430
431 // ------------------------------------------------------------------------
432 // Various Property Accessors
433 // ------------------------------------------------------------------------
434
435 /**
436 * Gets a String property as a boolean returning a defualt if the key is not
437 * present. In any case, true, on, 1 and yes strings return true and
438 * everything else returns
439 *
440 * @param props
441 * the properties to get the value from
442 * @param key
443 * the property key
444 * @param defaultValue
445 * the default value to return if key is not present
446 * @return true defaultValue if property does not exist, else return true if
447 * the String value is one of 'true', 'on', '1', 'yes', otherwise
448 * false is returned
449 */
450 public static boolean get( Properties props, String key, boolean defaultValue )
451 {
452 if ( props == null || !props.containsKey( key ) || props.getProperty( key ) == null )
453 {
454 return defaultValue;
455 }
456
457 String val = props.getProperty( key ).trim().toLowerCase();
458 return val.equals( "true" ) || val.equals( "on" ) || val.equals( "1" ) || val.equals( "yes" );
459 }
460
461
462 /**
463 * Gets a property or entry value from a hashtable and tries to transform
464 * whatever the value may be to an primitive integer.
465 *
466 * @param ht
467 * the hashtable to access for the value
468 * @param key
469 * the key to use when accessing the ht
470 * @param defval
471 * the default value to use if the key is not contained in ht or
472 * if the value cannot be represented as a primitive integer.
473 * @return the primitive integer representation of a hashtable value
474 */
475 public static int get( Hashtable<String, Object> ht, Object key, int defval )
476 {
477 if ( ht == null || !ht.containsKey( key ) || ht.get( key ) == null )
478 {
479 return defval;
480 }
481
482 Object obj = ht.get( key );
483
484 if ( obj instanceof Byte )
485 {
486 return ( ( Byte ) obj ).intValue();
487 }
488 if ( obj instanceof Short )
489 {
490 return ( ( Short ) obj ).intValue();
491 }
492 if ( obj instanceof Integer )
493 {
494 return ( ( Integer ) obj ).intValue();
495 }
496 if ( obj instanceof Long )
497 {
498 return ( ( Long ) obj ).intValue();
499 }
500 if ( obj instanceof String )
501 {
502 try
503 {
504 return Integer.parseInt( ( String ) obj );
505 }
506 catch ( NumberFormatException ne )
507 {
508 ne.printStackTrace();
509 return defval;
510 }
511 }
512
513 return defval;
514 }
515
516
517 public static long get( Properties props, String key, long defaultValue )
518 {
519 if ( props == null || !props.containsKey( key ) || props.getProperty( key ) == null )
520 {
521 return defaultValue;
522 }
523
524 throw new NotImplementedException();
525 }
526
527
528 public static byte get( Properties props, String key, byte defaultValue )
529 {
530 if ( props == null || !props.containsKey( key ) || props.getProperty( key ) == null )
531 {
532 return defaultValue;
533 }
534
535 throw new NotImplementedException();
536 }
537
538
539 public static char get( Properties props, String key, char defaultValue )
540 {
541 if ( props == null || !props.containsKey( key ) || props.getProperty( key ) == null )
542 {
543 return defaultValue;
544 }
545
546 throw new NotImplementedException();
547 }
548 }