package de.axone.tools; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import de.axone.exception.Assert; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * A replacement and facade for <tt>Properties</tt> * * <p>Supports not any method of properties but although additional * stuff like direcly getting a <tt>File</tt> or building subsets. * * <p>This class is intentionally not subclassed from Properties * but a drop in replacement for most of the methods. * * <p>The purpose of this class is to make life more easy. * * <p>SuperProperties are backed by a normal <tt>Properties</tt> class * and encasulates access to its methods. * * Main features are: <ul> * <li>Usage of a prefix * <li>Subsets * <li>Multiple accessor methods * <li>Exceptions if needed * <li>File creation * <li>Class creation * <li>Lists * </ul> * * <em>Prefixes</em> * <p>Prefixes are prepended to the properties key and are set at class * creation time. Subsets prepend additional prefixes. * E.g. <tt>new SuperProperties().supset( "one" ).subset( "two" )</tt> * will prefix all keys in the new class with <tt>"one.two."</tt>. * In following if key is used it will be allways prefixed. * * <em>Accessors</em> * <p> Accessors are in multiple versions:<ul> * <li><tt>getXyz( key : String )</tt> returns <tt>null</tt> if missing. * <li><tt>getXyz( key : String, default : String )</tt> returns <em>default</em> if missing. * <li><tt>getXyzRequired( key : String )</tt> throws <em>Exception</em> if missing. * </ul> * * <em>Class creation</em> * <p>Classes can be directly created and eventually configured if they confirm * to the class syntax used by <tt>ClassConfigurator</tt><ul> * <li>Have empty contructor * <li>have setters (String) for configurable parameters * </ul> * * TODO: Check EMogul for Method usage because there is much duplicate stuff. * * @author flo */ public class SuperProperties { private final Properties backend; private final String prefix; private File rootDir; /** * Create Super Properties with empty Properties */ public SuperProperties() { this( new Properties() ); } /** * Create Super Properties using backend as properties * * @param backend */ private SuperProperties(Properties backend) { this( null, backend, null ); } /** * Create Super properties using backend, prefix and root-dir * * @param prefix * @param backend * @param rootDir */ private SuperProperties(String prefix, Properties backend, File rootDir ) { this.backend = backend; this.prefix = prefix; this.rootDir = rootDir; } public String getPrefix(){ return prefix; } public File getRootDir(){ return rootDir; } public Properties getBackend(){ return backend; } // BaseDir public void setRootDir( File rootDir ){ this.rootDir = rootDir; } public void addRootDir( String rootDirKey ){ this.rootDir = getFile( rootDirKey ); } public void addRootDir( File addDir ){ this.rootDir = new File( this.rootDir, addDir.getPath() ); } // From Properties public void load( InputStream in ) throws IOException{ backend.load( in ); } public void load( Reader reader ) throws IOException { backend.load( reader ); } public void store( OutputStream out ) throws IOException{ backend.store( out, null ); } public void store( Writer writer ) throws IOException { backend.store( writer, null ); } public void store( File file ) throws IOException { try( FileWriter out = new FileWriter( file ) ){ backend.store( out, null ); } } public void load( File file ) throws IOException { try( FileReader in = new FileReader( file ) ){ load( in ); } } public String getProperty( String key ) { return backend.getProperty( realKey( key ) ); } public void setProperty( String key, String value ) { backend.setProperty( realKey( key ), value ); } public void setProperty( String key, int value ) { backend.setProperty( realKey( key ), ""+value ); } public String getProperty( String key, String defaultValue ) { return backend.getProperty( realKey( key ), defaultValue ); } public String getPropertyRequired( String key ) throws PropertiesException { String result = getProperty( key ); if( result == null ) throw new PropertyNotFoundException( key ); if( result.length() == 0 ) throw new PropertyEmptyException( key ); return result; } public Object instantiate( String key ) throws SecurityException, IllegalArgumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { String className = getProperty( key ); if( className == null ) return null; return ClassConfigurator.create( className ); } public Object instantiateRequired( String key ) throws SecurityException, IllegalArgumentException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, PropertyInstantiationException { Object result = instantiate( key ); if( result == null ) throw new PropertyInstantiationException( key, getProperty( key ) ); return result; } public List<String> getList( String key ) { LinkedList<String> result = new LinkedList<String>(); String value = null; int i = 1; do { value = getProperty( key + '.' + i ); if( value != null ) result.addLast( value ); i++; } while( value != null ); if( result.size() > 0 ) return result; else return null; } public File getFile( String key ) { String filename = getProperty( key ); if( filename == null ) return null; return prependBaseDir( rootDir, new File( filename ) ); } public File getFile( File basedir, String key ) { String filename = getProperty( key ); if( filename == null ) return null; File result = prependBaseDir( basedir, new File( filename ) ); return prependBaseDir( rootDir, result ); } public File getFile( String basedirKey, String key ) { String basedirFilename = getProperty( basedirKey ); if( basedirFilename == null ){ return getFile( key ); } else { return getFile( new File( basedirFilename), key ); } } private File prependBaseDir( File baseDir, File file ){ if( file == null ) return null; if( ! file.isAbsolute() && baseDir != null ){ file = new File( baseDir, file.getPath() ); } return file; } public File getFileRequired( String key ) throws PropertiesException { File result = getFile( key ); if( result == null ) throw new PropertyNotFoundException( key ); return result; } public File getFileRequired( File basedir, String key ) throws PropertiesException { File result = getFile( basedir, key ); if( result == null ) throw new PropertyNotFoundException( key ); return result; } public File getFileRequired( String basedirKey, String key ) throws PropertiesException { File result = getFile( basedirKey, key ); if( result == null ) throw new PropertyNotFoundException( key ); return result; } public Integer getInt( String key ){ String value = getProperty( key ); if( value == null ) return null; return Integer.valueOf( value ); } public int getInt( String key, int defaultValue ){ Integer value = getInt( key ); if( value == null ) return defaultValue; return value; } public int getIntRequired( String key ) throws PropertiesException { String value = getPropertyRequired( key ); int result; try { result = Integer.parseInt( value ); } catch( NumberFormatException e ){ throw new PropertyConversationException( key, value, PropertyConversationException.Format.INT ); } return result; } public Long getLong( String key ){ String value = getProperty( key ); if( value == null ) return null; return Long.valueOf( value ); } public long getLong( String key, int defaultValue ){ Long value = getLong( key ); if( value == null ) return defaultValue; return value; } public long getLongRequired( String key ) throws PropertiesException { String value = getPropertyRequired( key ); long result; try { result = Long.parseLong( value ); } catch( NumberFormatException e ){ throw new PropertyConversationException( key, value, PropertyConversationException.Format.INT ); } return result; } public Double getDouble( String key ){ String value = getProperty( key ); if( value == null ) return null; return Double.valueOf( value ); } public double getDouble( String key, double defaultValue ){ Double value = getDouble( key ); if( value == null ) return defaultValue; return value; } public double getDoubleRequired( String key ) throws PropertiesException { String value = getPropertyRequired( key ); double result; try { result = Double.parseDouble( value ); } catch( NumberFormatException e ){ throw new PropertyConversationException( key, value, PropertyConversationException.Format.DOUBLE ); } return result; } @SuppressFBWarnings( value="NP_BOOLEAN_RETURN_NULL", justification="Api" ) public Boolean getBoolean( String key ){ String value = getProperty( key ); if( value == null ) return null; if( EasyParser.isYes( value ) ) return true; if( EasyParser.isNo( value ) ) return false; return null; } public boolean getBoolean( String key, boolean defaultValue ){ String value = getProperty( key ); if( value == null ) return defaultValue; if( EasyParser.isYes( value ) ) return true; if( EasyParser.isNo( value ) ) return false; return defaultValue; } public boolean getBooleanRequired( String key ) throws PropertiesException { String value = getPropertyRequired( key ); if( EasyParser.isYes( value ) ) return true; if( EasyParser.isNo( value ) ) return false; throw new PropertyConversationException( key, value, PropertyConversationException.Format.BOOL ); } @Deprecated public SuperProperties getSubset( String prefix ){ return new SuperProperties( prefix, this.backend, this.rootDir ); } public SuperProperties subset( String prefix ) { Assert.notNull( prefix, "prefix" ); if( this.prefix == null ){ return new SuperProperties( prefix, this.backend, this.rootDir ); } else { return new SuperProperties( this.prefix + '.' + prefix, this.backend, this.rootDir ); } } /* public SuperProperties getSuperset(){ return new SuperProperties( null, this.backend ); } */ @Override public String toString(){ StringBuilder result = new StringBuilder(); result .append( "SuperProperties.\n Prefix: " ).append( prefix ) .append( "\n baseDir: " ).append( rootDir != null ? rootDir.getPath() : "null" ) .append( "\n Data: " ); ; for( Map.Entry<Object,Object>entry : backend.entrySet() ){ String keyS = (String) entry.getKey(); if( prefix == null || keyS.startsWith( prefix ) ){ result .append( "\n " ).append( keyS ) .append( ": " ).append( entry.getValue() ); } } return result.toString(); } /* *************** private *************** */ private String realKey( String key ) { String result; if( prefix == null ){ result = key; } else { result = prefix + '.' + key; } return result; } /* *************** exceptions *************** */ public static class PropertiesException extends Exception { /** * */ private static final long serialVersionUID = -1399195266886800097L; private PropertiesException( String cause ){ super( cause ); } } public static class PropertyNotFoundException extends PropertiesException { /** * */ private static final long serialVersionUID = 7014470488286327371L; private PropertyNotFoundException( String propertyName ){ super( "Property not found: " + propertyName ); } } public static class PropertyEmptyException extends PropertiesException { /** * */ private static final long serialVersionUID = -8619269284604950197L; private PropertyEmptyException( String propertyName ){ super( "Property is empty: " + propertyName ); } } public static class PropertyConversationException extends PropertiesException { /** * */ private static final long serialVersionUID = 30336261348480483L; private enum Format {BOOL,INT,FLOAT,DOUBLE}; private PropertyConversationException( String propertyName, String value, Format format ){ super( "Cannot convert value of " + propertyName + " (" + value + ") to " + format ); } } public static class PropertyInstantiationException extends PropertiesException { /** * */ private static final long serialVersionUID = 7399765717886263434L; private PropertyInstantiationException( String propertyName, String value ){ super( "Cannot instantiate " + propertyName + ": '" + value + "'" ); } } /* private static class PropertyFileNotFoundException extends PropertiesException { private PropertyFileNotFoundException( String propertyName, File file ){ super( "File not found for " + propertyName + ": " + file.getAbsolutePath() ); } } */ }