001/* 002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 003 * 004 * http://www.izforge.com/izpack/ 005 * http://developer.berlios.de/projects/izpack/ 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 022package com.izforge.izpack.compiler; 023 024import java.io.File; 025import java.io.FileInputStream; 026import java.io.IOException; 027import java.io.StringReader; 028import java.io.StringWriter; 029import java.util.Enumeration; 030import java.util.Properties; 031import java.util.Vector; 032 033import net.n3.nanoxml.XMLElement; 034 035import org.apache.tools.ant.taskdefs.Execute; 036 037import 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 */ 089public class Property 090{ 091 092 protected String name; 093 094 protected String value; 095 096 protected File file; 097 098 // protected String resource; 099 // protected Path classpath; 100 protected String env; 101 102 // protected Reference ref; 103 protected String prefix; 104 105 protected XMLElement xmlProp; 106 107 protected CompilerConfig config; 108 protected Compiler compiler; 109 110 public Property(XMLElement xmlProp, CompilerConfig config) 111 { 112 this.xmlProp = xmlProp; 113 this.config = config; 114 this.compiler = config.getCompiler(); 115 name = xmlProp.getAttribute("name"); 116 value = xmlProp.getAttribute("value"); 117 env = xmlProp.getAttribute("environment"); 118 if (env != null && !env.endsWith(".")) env += "."; 119 120 prefix = xmlProp.getAttribute("prefix"); 121 if (prefix != null && !prefix.endsWith(".")) prefix += "."; 122 123 String filename = xmlProp.getAttribute("file"); 124 if (filename != null) file = new File(filename); 125 } 126 127 /** 128 * get the value of this property 129 * 130 * @return the current value or the empty string 131 */ 132 public String getValue() 133 { 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 { 144 return value == null ? "" : value; 145 } 146 147 /** 148 * Set the property in the project to the value. If the task was give a file, resource or env 149 * attribute here is where it is loaded. 150 */ 151 public void execute() throws CompilerException 152 { 153 if (name != null) 154 { 155 if (value == null) 156 config.parseError(xmlProp, "You must specify a value with the name attribute"); 157 } 158 else 159 { 160 if (file == null && env == null) 161 config.parseError(xmlProp, 162 "You must specify file, or environment when not using the name attribute"); 163 } 164 165 if (file == null && prefix != null) 166 config.parseError(xmlProp, "Prefix is only valid when loading from a file "); 167 168 if ((name != null) && (value != null)) 169 addProperty(name, value); 170 171 else if (file != null) 172 loadFile(file); 173 174 else if (env != null) 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 { 184 Properties props = new Properties(); 185 config.getPackagerListener().packagerMsg("Loading " + file.getAbsolutePath(), 186 PackagerListener.MSG_VERBOSE); 187 try 188 { 189 if (file.exists()) 190 { 191 FileInputStream fis = new FileInputStream(file); 192 try 193 { 194 props.load(fis); 195 } 196 finally 197 { 198 if (fis != null) fis.close(); 199 } 200 addProperties(props); 201 } 202 else 203 { 204 config.getPackagerListener().packagerMsg( 205 "Unable to find property file: " + file.getAbsolutePath(), 206 PackagerListener.MSG_VERBOSE); 207 } 208 } 209 catch (IOException ex) 210 { 211 config.parseError(xmlProp, "Faild to load file: " + file.getAbsolutePath(), ex); 212 } 213 } 214 215 /** 216 * load the environment values 217 * 218 * @param prefix prefix to place before them 219 */ 220 protected void loadEnvironment(String prefix) throws CompilerException 221 { 222 Properties props = new Properties(); 223 config.getPackagerListener().packagerMsg("Loading Environment " + prefix, 224 PackagerListener.MSG_VERBOSE); 225 Vector osEnv = Execute.getProcEnvironment(); 226 for (Enumeration e = osEnv.elements(); e.hasMoreElements();) 227 { 228 String entry = (String) e.nextElement(); 229 int pos = entry.indexOf('='); 230 if (pos == -1) 231 { 232 config.getPackagerListener().packagerMsg("Ignoring " + prefix, 233 PackagerListener.MSG_WARN); 234 } 235 else 236 { 237 props.put(prefix + entry.substring(0, pos), entry.substring(pos + 1)); 238 } 239 } 240 addProperties(props); 241 } 242 243 /** 244 * Add a name value pair to the project property set 245 * 246 * @param name name of property 247 * @param value value to set 248 */ 249 protected void addProperty(String name, String value) throws CompilerException 250 { 251 value = compiler.replaceProperties(value); 252 253 compiler.addProperty(name, value); 254 } 255 256 /** 257 * iterate through a set of properties, resolve them then assign them 258 */ 259 protected void addProperties(Properties props) throws CompilerException 260 { 261 resolveAllProperties(props); 262 Enumeration e = props.keys(); 263 while (e.hasMoreElements()) 264 { 265 String name = (String) e.nextElement(); 266 String value = props.getProperty(name); 267 268 if (prefix != null) 269 { 270 name = prefix + name; 271 } 272 273 addProperty(name, value); 274 } 275 } 276 277 /** 278 * resolve properties inside a properties object 279 * 280 * @param props properties to resolve 281 */ 282 private void resolveAllProperties(Properties props) throws CompilerException 283 { 284 VariableSubstitutor subs = new VariableSubstitutor(props); 285 subs.setBracesRequired(true); 286 287 for (Enumeration e = props.keys(); e.hasMoreElements();) 288 { 289 String name = (String) e.nextElement(); 290 String value = props.getProperty(name); 291 292 int mods = -1; 293 do 294 { 295 StringReader read = new StringReader(value); 296 StringWriter write = new StringWriter(); 297 298 try 299 { 300 mods = subs.substitute(read, write, "at"); 301 // TODO: check for circular references. We need to know 302 // which 303 // variables were substituted to do that 304 props.put(name, value); 305 } 306 catch (IOException ex) 307 { 308 config.parseError(xmlProp, "Faild to load file: " + file.getAbsolutePath(), 309 ex); 310 } 311 } 312 while (mods != 0); 313 } 314 } 315}