001: /*
002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
003: *
004: * http://izpack.org/
005: * http://izpack.codehaus.org/
006: *
007: * Copyright 2004 Chadwick McHenry
008: *
009: * Licensed under the Apache License, Version 2.0 (the "License");
010: * you may not use this file except in compliance with the License.
011: * You may obtain a copy of the License at
012: *
013: * http://www.apache.org/licenses/LICENSE-2.0
014: *
015: * Unless required by applicable law or agreed to in writing, software
016: * distributed under the License is distributed on an "AS IS" BASIS,
017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018: * See the License for the specific language governing permissions and
019: * limitations under the License.
020: */
021:
022: package com.izforge.izpack.compiler;
023:
024: import java.io.File;
025: import java.io.FileInputStream;
026: import java.io.IOException;
027: import java.io.StringReader;
028: import java.io.StringWriter;
029: import java.util.Enumeration;
030: import java.util.Properties;
031: import java.util.Vector;
032:
033: import net.n3.nanoxml.XMLElement;
034:
035: import org.apache.tools.ant.taskdefs.Execute;
036:
037: import com.izforge.izpack.util.VariableSubstitutor;
038:
039: /**
040: * Sets a property by name, or set of properties (from file or resource) in the project. This is
041: * modeled after ant properties
042: * <p>
043: *
044: * Properties are immutable: once a property is set it cannot be changed. They are most definately
045: * not variable.
046: * <p>
047: *
048: * There are five ways to set properties:
049: * <ul>
050: * <li>By supplying both the <i>name</i> and <i>value</i> attributes.</li>
051: * <li>By setting the <i>file</i> attribute with the filename of the property file to load. This
052: * property file has the format as defined by the file used in the class java.util.Properties.</li>
053: * <li>By setting the <i>environment</i> attribute with a prefix to use. Properties will be
054: * defined for every environment variable by prefixing the supplied name and a period to the name of
055: * the variable.</li>
056: * </ul>
057: *
058: * Combinations of the above are considered an error.
059: * <p>
060: *
061: * The value part of the properties being set, might contain references to other properties. These
062: * references are resolved when the properties are set.
063: * <p>
064: *
065: * This also holds for properties loaded from a property file.
066: * <p>
067: *
068: * Properties are case sensitive.
069: * <p>
070: *
071: * When specifying the environment attribute, it's value is used as a prefix to use when retrieving
072: * environment variables. This functionality is currently only implemented on select platforms.
073: * <p>
074: *
075: * Thus if you specify environment="myenv" you will be able to access OS-specific
076: * environment variables via property names "myenv.PATH" or "myenv.TERM".
077: * <p>
078: *
079: * Note also that properties are case sensitive, even if the environment variables on your operating
080: * system are not, e.g. it will be ${env.Path} not ${env.PATH} on Windows 2000.
081: * <p>
082: *
083: * Note that when specifying either the <code>prefix</code> or <code>environment</code>
084: * attributes, if you supply a property name with a final "." it will not be doubled. ie
085: * environment="myenv." will still allow access of environment variables through
086: * "myenv.PATH" and "myenv.TERM".
087: * <p>
088: */
089: public class Property {
090:
091: protected String name;
092:
093: protected String value;
094:
095: protected File file;
096:
097: // protected String resource;
098: // protected Path classpath;
099: protected String env;
100:
101: // protected Reference ref;
102: protected String prefix;
103:
104: protected XMLElement xmlProp;
105:
106: protected CompilerConfig config;
107: protected Compiler compiler;
108:
109: public Property(XMLElement xmlProp, CompilerConfig config) {
110: this .xmlProp = xmlProp;
111: this .config = config;
112: this .compiler = config.getCompiler();
113: name = xmlProp.getAttribute("name");
114: value = xmlProp.getAttribute("value");
115: env = xmlProp.getAttribute("environment");
116: if (env != null && !env.endsWith("."))
117: env += ".";
118:
119: prefix = xmlProp.getAttribute("prefix");
120: if (prefix != null && !prefix.endsWith("."))
121: prefix += ".";
122:
123: String filename = xmlProp.getAttribute("file");
124: if (filename != null)
125: file = new File(filename);
126: }
127:
128: /**
129: * get the value of this property
130: *
131: * @return the current value or the empty string
132: */
133: public String getValue() {
134: return toString();
135: }
136:
137: /**
138: * get the value of this property
139: *
140: * @return the current value or the empty string
141: */
142: public String toString() {
143: return value == null ? "" : value;
144: }
145:
146: /**
147: * Set the property in the project to the value. If the task was give a file, resource or env
148: * attribute here is where it is loaded.
149: */
150: public void execute() throws CompilerException {
151: if (name != null) {
152: if (value == null)
153: config
154: .parseError(xmlProp,
155: "You must specify a value with the name attribute");
156: } else {
157: if (file == null && env == null)
158: config
159: .parseError(xmlProp,
160: "You must specify file, or environment when not using the name attribute");
161: }
162:
163: if (file == null && prefix != null)
164: config.parseError(xmlProp,
165: "Prefix is only valid when loading from a file ");
166:
167: if ((name != null) && (value != null))
168: addProperty(name, value);
169:
170: else if (file != null)
171: loadFile(file);
172:
173: else if (env != null)
174: loadEnvironment(env);
175: }
176:
177: /**
178: * load properties from a file
179: *
180: * @param file file to load
181: */
182: protected void loadFile(File file) throws CompilerException {
183: Properties props = new Properties();
184: config.getPackagerListener().packagerMsg(
185: "Loading " + file.getAbsolutePath(),
186: PackagerListener.MSG_VERBOSE);
187: try {
188: if (file.exists()) {
189: FileInputStream fis = new FileInputStream(file);
190: try {
191: props.load(fis);
192: } finally {
193: if (fis != null)
194: fis.close();
195: }
196: addProperties(props);
197: } else {
198: config.getPackagerListener().packagerMsg(
199: "Unable to find property file: "
200: + file.getAbsolutePath(),
201: PackagerListener.MSG_VERBOSE);
202: }
203: } catch (IOException ex) {
204: config.parseError(xmlProp, "Faild to load file: "
205: + file.getAbsolutePath(), ex);
206: }
207: }
208:
209: /**
210: * load the environment values
211: *
212: * @param prefix prefix to place before them
213: */
214: protected void loadEnvironment(String prefix)
215: throws CompilerException {
216: Properties props = new Properties();
217: config.getPackagerListener().packagerMsg(
218: "Loading Environment " + prefix,
219: PackagerListener.MSG_VERBOSE);
220: Vector osEnv = Execute.getProcEnvironment();
221: for (Enumeration e = osEnv.elements(); e.hasMoreElements();) {
222: String entry = (String) e.nextElement();
223: int pos = entry.indexOf('=');
224: if (pos == -1) {
225: config.getPackagerListener()
226: .packagerMsg("Ignoring " + prefix,
227: PackagerListener.MSG_WARN);
228: } else {
229: props.put(prefix + entry.substring(0, pos), entry
230: .substring(pos + 1));
231: }
232: }
233: addProperties(props);
234: }
235:
236: /**
237: * Add a name value pair to the project property set
238: *
239: * @param name name of property
240: * @param value value to set
241: */
242: protected void addProperty(String name, String value)
243: throws CompilerException {
244: value = compiler.replaceProperties(value);
245:
246: compiler.addProperty(name, value);
247: }
248:
249: /**
250: * iterate through a set of properties, resolve them then assign them
251: */
252: protected void addProperties(Properties props)
253: throws CompilerException {
254: resolveAllProperties(props);
255: Enumeration e = props.keys();
256: while (e.hasMoreElements()) {
257: String name = (String) e.nextElement();
258: String value = props.getProperty(name);
259:
260: if (prefix != null) {
261: name = prefix + name;
262: }
263:
264: addProperty(name, value);
265: }
266: }
267:
268: /**
269: * resolve properties inside a properties object
270: *
271: * @param props properties to resolve
272: */
273: private void resolveAllProperties(Properties props)
274: throws CompilerException {
275: VariableSubstitutor subs = new VariableSubstitutor(props);
276: subs.setBracesRequired(true);
277:
278: for (Enumeration e = props.keys(); e.hasMoreElements();) {
279: String name = (String) e.nextElement();
280: String value = props.getProperty(name);
281:
282: int mods = -1;
283: do {
284: StringReader read = new StringReader(value);
285: StringWriter write = new StringWriter();
286:
287: try {
288: mods = subs.substitute(read, write, "at");
289: // TODO: check for circular references. We need to know
290: // which
291: // variables were substituted to do that
292: props.put(name, value);
293: } catch (IOException ex) {
294: config.parseError(xmlProp, "Faild to load file: "
295: + file.getAbsolutePath(), ex);
296: }
297: } while (mods != 0);
298: }
299: }
300: }
|