/* * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved. * * http://izpack.org/ * http://izpack.codehaus.org/ * * Copyright 2004 Chadwick McHenry * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.izforge.izpack.compiler; import com.izforge.izpack.util.VariableSubstitutor; import com.izforge.izpack.adaptator.IXMLElement; import org.apache.tools.ant.taskdefs.Execute; import java.io.*; import java.util.Enumeration; import java.util.Properties; import java.util.Vector; /** * Sets a property by name, or set of properties (from file or resource) in the project. This is * modeled after ant properties * <p/> * <p/> * Properties are immutable: once a property is set it cannot be changed. They are most definately * not variable. * <p/> * <p/> * There are five ways to set properties: * <ul> * <li>By supplying both the <i>name</i> and <i>value</i> attributes.</li> * <li>By setting the <i>file</i> attribute with the filename of the property file to load. This * property file has the format as defined by the file used in the class java.util.Properties.</li> * <li>By setting the <i>environment</i> attribute with a prefix to use. Properties will be * defined for every environment variable by prefixing the supplied name and a period to the name of * the variable.</li> * </ul> * <p/> * Combinations of the above are considered an error. * <p/> * <p/> * The value part of the properties being set, might contain references to other properties. These * references are resolved when the properties are set. * <p/> * <p/> * This also holds for properties loaded from a property file. * <p/> * <p/> * Properties are case sensitive. * <p/> * <p/> * When specifying the environment attribute, it's value is used as a prefix to use when retrieving * environment variables. This functionality is currently only implemented on select platforms. * <p/> * <p/> * Thus if you specify environment="myenv" you will be able to access OS-specific * environment variables via property names "myenv.PATH" or "myenv.TERM". * <p/> * <p/> * Note also that properties are case sensitive, even if the environment variables on your operating * system are not, e.g. it will be ${env.Path} not ${env.PATH} on Windows 2000. * <p/> * <p/> * Note that when specifying either the <code>prefix</code> or <code>environment</code> * attributes, if you supply a property name with a final "." it will not be doubled. ie * environment="myenv." will still allow access of environment variables through * "myenv.PATH" and "myenv.TERM". * <p/> */ public class Property { protected String name; protected String value; protected File file; // protected String resource; // protected Path classpath; protected String env; // protected Reference ref; protected String prefix; protected IXMLElement xmlProp; protected CompilerConfig config; protected Compiler compiler; public Property(IXMLElement xmlProp, CompilerConfig config) { this.xmlProp = xmlProp; this.config = config; this.compiler = config.getCompiler(); name = xmlProp.getAttribute("name"); value = xmlProp.getAttribute("value"); env = xmlProp.getAttribute("environment"); if (env != null && !env.endsWith(".")) { env += "."; } prefix = xmlProp.getAttribute("prefix"); if (prefix != null && !prefix.endsWith(".")) { prefix += "."; } String filename = xmlProp.getAttribute("file"); if (filename != null) { file = new File(filename); } } /** * get the value of this property * * @return the current value or the empty string */ public String getValue() { return toString(); } /** * get the value of this property * * @return the current value or the empty string */ public String toString() { return value == null ? "" : value; } /** * Set the property in the project to the value. If the task was give a file, resource or env * attribute here is where it is loaded. */ public void execute() throws CompilerException { if (name != null) { if (value == null) { config.parseError(xmlProp, "You must specify a value with the name attribute"); } } else { if (file == null && env == null) { config.parseError(xmlProp, "You must specify file, or environment when not using the name attribute"); } } if (file == null && prefix != null) { config.parseError(xmlProp, "Prefix is only valid when loading from a file "); } if ((name != null) && (value != null)) { addProperty(name, value); } else if (file != null) { loadFile(file); } else if (env != null) { loadEnvironment(env); } } /** * load properties from a file * * @param file file to load */ protected void loadFile(File file) throws CompilerException { Properties props = new Properties(); config.getPackagerListener().packagerMsg("Loading " + file.getAbsolutePath(), PackagerListener.MSG_VERBOSE); try { if (file.exists()) { FileInputStream fis = new FileInputStream(file); try { props.load(fis); } finally { if (fis != null) { fis.close(); } } addProperties(props); } else { config.getPackagerListener().packagerMsg( "Unable to find property file: " + file.getAbsolutePath(), PackagerListener.MSG_VERBOSE); } } catch (IOException ex) { config.parseError(xmlProp, "Faild to load file: " + file.getAbsolutePath(), ex); } } /** * load the environment values * * @param prefix prefix to place before them */ protected void loadEnvironment(String prefix) throws CompilerException { Properties props = new Properties(); config.getPackagerListener().packagerMsg("Loading Environment " + prefix, PackagerListener.MSG_VERBOSE); Vector osEnv = Execute.getProcEnvironment(); for (Enumeration e = osEnv.elements(); e.hasMoreElements();) { String entry = (String) e.nextElement(); int pos = entry.indexOf('='); if (pos == -1) { config.getPackagerListener().packagerMsg("Ignoring " + prefix, PackagerListener.MSG_WARN); } else { props.put(prefix + entry.substring(0, pos), entry.substring(pos + 1)); } } addProperties(props); } /** * Add a name value pair to the project property set * * @param name name of property * @param value value to set */ protected void addProperty(String name, String value) throws CompilerException { value = compiler.replaceProperties(value); compiler.addProperty(name, value); } /** * iterate through a set of properties, resolve them then assign them */ protected void addProperties(Properties props) throws CompilerException { resolveAllProperties(props); Enumeration e = props.keys(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String value = props.getProperty(name); if (prefix != null) { name = prefix + name; } addProperty(name, value); } } /** * resolve properties inside a properties object * * @param props properties to resolve */ private void resolveAllProperties(Properties props) throws CompilerException { VariableSubstitutor subs = new VariableSubstitutor(props); subs.setBracesRequired(true); for (Enumeration e = props.keys(); e.hasMoreElements();) { String name = (String) e.nextElement(); String value = props.getProperty(name); int mods = -1; do { StringReader read = new StringReader(value); StringWriter write = new StringWriter(); try { mods = subs.substitute(read, write, "at"); // TODO: check for circular references. We need to know // which // variables were substituted to do that props.put(name, value); } catch (IOException ex) { config.parseError(xmlProp, "Faild to load file: " + file.getAbsolutePath(), ex); } } while (mods != 0); } } }