package org.oddjob.values.properties; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import org.apache.log4j.Logger; import org.oddjob.arooa.ArooaSession; import org.oddjob.arooa.convert.ArooaConversionException; import org.oddjob.arooa.life.ArooaContextAware; import org.oddjob.arooa.parsing.ArooaContext; import org.oddjob.arooa.runtime.ExpressionParser; import org.oddjob.arooa.runtime.ParsedExpression; import org.oddjob.arooa.runtime.PropertyLookup; import org.oddjob.arooa.runtime.PropertySource; import org.oddjob.arooa.utils.ListSetterHelper; /** * Base class for things that load Properties. */ public class PropertiesBase implements ArooaContextAware { private static final Logger logger = Logger.getLogger(PropertiesBase.class); /** InputStream for properties. */ private InputStream input; /** If the input is in XML formst. */ private boolean fromXML; /** Properties to merge in. */ private List<Properties> list = new ArrayList<Properties>(); /** Adhoc properties. */ private Map<String, String> values = new LinkedHashMap<String, String>(); /** Substitute within property values. */ private boolean substitute; /** Extract this prefix from the beginning of property names. */ private String extract; /** Add this prefix to the beginning of property names. */ private String prefix; /** The session. */ private ArooaSession session; private PropertySource source; @Override public void setArooaContext(final ArooaContext context) { session = context.getSession(); final PropertyLookup propertyLookup = new PropertyLookup() { @Override public String lookup(String propertyName) { String value = values.get(propertyName); if (value != null) { return value; } for (Properties props : list) { if (props == null) { continue; } value = props.getProperty(propertyName); if (value != null) { return value; } } return null; } @Override public Set<String> propertyNames() { Set<String> names = new TreeSet<String>(); names.addAll(values.keySet()); for (Properties props : list) { if (props == null) { continue; } names.addAll(props.stringPropertyNames()); } return names; } @Override public PropertySource sourceFor(String propertyName) { if (lookup(propertyName) != null) { return source; } return null; } }; session.getPropertyManager().addPropertyLookup(propertyLookup); } /** * The main method that provides the properties. * * @return Properties, might be empty but never null. * * @throws IOException * @throws ArooaConversionException */ protected Properties toProperties() throws IOException, ArooaConversionException { Properties props = new Properties(); load(values, props); Properties[] sets = list.toArray(new Properties[list.size()]); for (int i = 0; i < sets.length; ++i) { if (sets[i] == null) { throw new ArooaConversionException("Index " + i + " of the property sets is null."); } load(sets[i], props); } if (input != null) { load(loadInput(), props); } return props; } /** * Load a set of Properties. * * @param set The set. * @param properties The properties. * @throws ArooaConversionException */ private void load(Map<?, ?> properties, Properties into) throws ArooaConversionException { for (Map.Entry<?, ?> entry: properties.entrySet()) { String name = entry.getKey().toString(); String value = entry.getValue().toString(); if (extract != null) { String extractWithDot = extract + "."; if (!name.startsWith(extractWithDot)) { continue; } name = name.substring(extractWithDot.length()); } if (prefix != null) { name = prefix + "." + name; } if (substitute) { ExpressionParser parser = session.getTools().getExpressionParser(); ParsedExpression expression = parser.parse(value); value = expression.evaluate(session, String.class); } if (value == null) { logger.info(name +" is null. skipping."); continue; } into.setProperty(name, value); } } /** * Utility method to load properties from an InputStream. * * @return The properties, never null. * * @throws IOException */ private Properties loadInput() throws IOException { Properties props = new Properties(); try { if (fromXML) { props.loadFromXML(input); } else { props.load(input); } logger.info("Loaded " + props.size() + " properties from " + input); return props; } finally { input.close(); } } /** * @oddjob.property input * @oddjob.description An input source for Properties. * @oddjob.required No. */ public void setInput(InputStream input) { this.input = input; } /** * @oddjob.property fromXML * @oddjob.description If the input for the properties is in XML format. * @oddjob.required No, defaults to false. */ public void setFromXML(boolean fromXml) { this.fromXML = fromXml; } /** * Getter for fromXML. * * @return true/false. */ public boolean isFromXML() { return fromXML; } /** * @oddjob.property values * @oddjob.description Properties defined as key value pairs. * @oddjob.required No. */ public void setValues(String key, String value) { if (value == null) { values.put(key, ""); } else { values.put(key, value); } } /** * @oddjob.property sets * @oddjob.description Extra properties to be merged into the overall * property set. * @oddjob.required No. */ public void setSets(int index, Properties props) { new ListSetterHelper<Properties>(list).set(index, props); } /** * Indexed getter for sets. * * @param index The index. * @return The properites. */ public Properties getSets(int index) { return list.get(index); } /** * @oddjob.property substitute * @oddjob.description Use substitution for the values of ${} type * properties. * @oddjob.required No. */ public void setSubstitute(boolean substitute) { this.substitute = substitute; } /** * Getter for substitute. * * @return true/false. */ public boolean isSubstitute() { return substitute; } /** * Getter for extract. * * @return The extract prefix or null. */ public String getExtract() { return extract; } /** * @oddjob.property extract * @oddjob.description Extract this prefix form property names. Filters * out properties that do not begin with this prefix. * @oddjob.required No. */ public void setExtract(String extract) { this.extract = extract; } /** * Getter for prefix. * * @return The appending prefix or null. */ public String getPrefix() { return prefix; } /** * @oddjob.property prefix * @oddjob.description Append this prefix to property names. * @oddjob.required No. */ public void setPrefix(String prefix) { this.prefix = prefix; } public PropertySource getSource() { return source; } public void setSource(PropertySource source) { this.source = source; } }