| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.tools.ant.taskdefs; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.apache.tools.ant.AntClassLoader; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.PropertyHelper; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.property.ResolvePropertyMap; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.types.Reference; |
| import org.apache.tools.ant.util.FileUtils; |
| |
| /** |
| * <p>Sets a property by name, or set of properties (from file or |
| * resource) in the project. </p> |
| * <p>Properties are immutable: whoever sets a property first freezes it for the |
| * rest of the build; they are most definitely not variable. |
| * <p>There are seven ways to set properties:</p> |
| * <ul> |
| * <li>By supplying both the <i>name</i> and <i>value</i> attribute.</li> |
| * <li>By supplying the <i>name</i> and nested text.</li> |
| * <li>By supplying both the <i>name</i> and <i>refid</i> attribute.</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>url</i> attribute with the url from which to load the |
| * properties. This url must be directed to a file that has the format as defined |
| * by the file used in the class java.util.Properties.</li> |
| * <li>By setting the <i>resource</i> attribute with the resource name 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>Although combinations of these ways are possible, only one should be used |
| * at a time. Problems might occur with the order in which properties are set, for |
| * instance.</p> |
| * <p>The value part of the properties being set, might contain references to other |
| * properties. These references are resolved at the time these properties are set. |
| * This also holds for properties loaded from a property file.</p> |
| * Properties are case sensitive. |
| * |
| * @since Ant 1.1 |
| * |
| * @ant.attribute.group name="name" description="One of these, when using the name attribute" |
| * @ant.attribute.group name="noname" description="One of these, when not using the name attribute" |
| * @ant.task category="property" |
| */ |
| public class Property extends Task { |
| |
| // CheckStyle:VisibilityModifier OFF - bc |
| protected String name; |
| protected String value; |
| protected File file; |
| protected URL url; |
| protected String resource; |
| protected Path classpath; |
| protected String env; |
| protected Reference ref; |
| protected String prefix; |
| private Project fallback; |
| private Object untypedValue; |
| private boolean valueAttributeUsed = false; |
| private boolean relative = false; |
| private File basedir; |
| private boolean prefixValues = false; |
| |
| protected boolean userProperty; // set read-only properties |
| // CheckStyle:VisibilityModifier ON |
| |
| /** |
| * Constructor for Property. |
| */ |
| public Property() { |
| this(false); |
| } |
| |
| /** |
| * Constructor for Property. |
| * @param userProperty if true this is a user property |
| * @since Ant 1.5 |
| */ |
| protected Property(boolean userProperty) { |
| this(userProperty, null); |
| } |
| |
| /** |
| * Constructor for Property. |
| * @param userProperty if true this is a user property |
| * @param fallback a project to use to look for references if the reference is |
| * not in the current project |
| * @since Ant 1.5 |
| */ |
| protected Property(boolean userProperty, Project fallback) { |
| this.userProperty = userProperty; |
| this.fallback = fallback; |
| } |
| |
| /** |
| * Sets 'relative' attribute. |
| * @param relative new value |
| * @since Ant 1.8.0 |
| */ |
| public void setRelative(boolean relative) { |
| this.relative = relative; |
| } |
| |
| /** |
| * Sets 'basedir' attribute. |
| * @param basedir new value |
| * @since Ant 1.8.0 |
| */ |
| public void setBasedir(File basedir) { |
| this.basedir = basedir; |
| } |
| |
| /** |
| * The name of the property to set. |
| * @param name property name |
| */ |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| /** |
| * Get the property name. |
| * @return the property name |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Sets the property to the absolute filename of the |
| * given file. If the value of this attribute is an absolute path, it |
| * is left unchanged (with / and \ characters converted to the |
| * current platforms conventions). Otherwise it is taken as a path |
| * relative to the project's basedir and expanded. |
| * @param location path to set |
| * |
| * @ant.attribute group="name" |
| */ |
| public void setLocation(File location) { |
| if (relative) { |
| internalSetValue(location); |
| } else { |
| setValue(location.getAbsolutePath()); |
| } |
| } |
| |
| /* the following method is first in source so IH will pick it up first: |
| * Hopefully we'll never get any classes compiled by wise-guy compilers that behave otherwise... |
| */ |
| |
| /** |
| * Set the value of the property. |
| * @param value the value to use. |
| */ |
| public void setValue(Object value) { |
| valueAttributeUsed = true; |
| internalSetValue(value); |
| } |
| |
| private void internalSetValue(Object value) { |
| this.untypedValue = value; |
| //preserve protected string value for subclasses :( |
| this.value = value == null ? null : value.toString(); |
| } |
| |
| /** |
| * Set the value of the property as a String. |
| * @param value value to assign |
| * |
| * @ant.attribute group="name" |
| */ |
| public void setValue(String value) { |
| setValue((Object) value); |
| } |
| |
| /** |
| * Set a (multiline) property as nested text. |
| * @param msg the text to append to the output text |
| * @since Ant 1.8.0 |
| */ |
| public void addText(String msg) { |
| if (!valueAttributeUsed) { |
| msg = getProject().replaceProperties(msg); |
| String currentValue = getValue(); |
| if (currentValue != null) { |
| msg = currentValue + msg; |
| } |
| internalSetValue(msg); |
| } else if (msg.trim().length() > 0) { |
| throw new BuildException("can't combine nested text with value" |
| + " attribute"); |
| } |
| } |
| |
| /** |
| * Get the property value. |
| * @return the property value |
| */ |
| public String getValue() { |
| return value; |
| } |
| |
| /** |
| * Filename of a property file to load. |
| * @param file filename |
| * |
| * @ant.attribute group="noname" |
| */ |
| public void setFile(File file) { |
| this.file = file; |
| } |
| |
| /** |
| * Get the file attribute. |
| * @return the file attribute |
| */ |
| public File getFile() { |
| return file; |
| } |
| |
| /** |
| * The url from which to load properties. |
| * @param url url string |
| * |
| * @ant.attribute group="noname" |
| */ |
| public void setUrl(URL url) { |
| this.url = url; |
| } |
| |
| /** |
| * Get the url attribute. |
| * @return the url attribute |
| */ |
| public URL getUrl() { |
| return url; |
| } |
| |
| /** |
| * Prefix to apply to properties loaded using <code>file</code> |
| * or <code>resource</code>. |
| * A "." is appended to the prefix if not specified. |
| * @param prefix prefix string |
| * @since Ant 1.5 |
| */ |
| public void setPrefix(String prefix) { |
| this.prefix = prefix; |
| if (prefix != null && !prefix.endsWith(".")) { |
| this.prefix += "."; |
| } |
| } |
| |
| /** |
| * Get the prefix attribute. |
| * @return the prefix attribute |
| * @since Ant 1.5 |
| */ |
| public String getPrefix() { |
| return prefix; |
| } |
| |
| /** |
| * Whether to apply the prefix when expanding properties on the |
| * right hand side of a properties file as well. |
| * |
| * @since Ant 1.8.2 |
| */ |
| public void setPrefixValues(boolean b) { |
| prefixValues = b; |
| } |
| |
| /** |
| * Whether to apply the prefix when expanding properties on the |
| * right hand side of a properties file as well. |
| * |
| * @since Ant 1.8.2 |
| */ |
| public boolean getPrefixValues() { |
| return prefixValues; |
| } |
| |
| /** |
| * Sets a reference to an Ant datatype |
| * declared elsewhere. |
| * Only yields reasonable results for references |
| * PATH like structures or properties. |
| * @param ref reference |
| * |
| * @ant.attribute group="name" |
| */ |
| public void setRefid(Reference ref) { |
| this.ref = ref; |
| } |
| |
| /** |
| * Get the refid attribute. |
| * @return the refid attribute |
| */ |
| public Reference getRefid() { |
| return ref; |
| } |
| |
| /** |
| * The resource name of a property file to load |
| * @param resource resource on classpath |
| * |
| * @ant.attribute group="noname" |
| */ |
| public void setResource(String resource) { |
| this.resource = resource; |
| } |
| |
| /** |
| * Get the resource attribute. |
| * @return the resource attribute |
| */ |
| public String getResource() { |
| return resource; |
| } |
| |
| /** |
| * Prefix to use when retrieving environment variables. |
| * 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> |
| * Note that 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". This functionality is currently only implemented |
| * on select platforms. Feel free to send patches to increase the number of platforms |
| * this functionality is supported on ;).<br> |
| * 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. |
| * @param env prefix |
| * |
| * @ant.attribute group="noname" |
| */ |
| public void setEnvironment(String env) { |
| this.env = env; |
| } |
| |
| /** |
| * Get the environment attribute. |
| * @return the environment attribute |
| * @since Ant 1.5 |
| */ |
| public String getEnvironment() { |
| return env; |
| } |
| |
| /** |
| * The classpath to use when looking up a resource. |
| * @param classpath to add to any existing classpath |
| */ |
| public void setClasspath(Path classpath) { |
| if (this.classpath == null) { |
| this.classpath = classpath; |
| } else { |
| this.classpath.append(classpath); |
| } |
| } |
| |
| /** |
| * The classpath to use when looking up a resource. |
| * @return a path to be configured |
| */ |
| public Path createClasspath() { |
| if (this.classpath == null) { |
| this.classpath = new Path(getProject()); |
| } |
| return this.classpath.createPath(); |
| } |
| |
| /** |
| * the classpath to use when looking up a resource, |
| * given as reference to a <path> defined elsewhere |
| * @param r a reference to a classpath |
| */ |
| public void setClasspathRef(Reference r) { |
| createClasspath().setRefid(r); |
| } |
| |
| /** |
| * Get the classpath used when looking up a resource. |
| * @return the classpath |
| * @since Ant 1.5 |
| */ |
| public Path getClasspath() { |
| return classpath; |
| } |
| |
| /** |
| * @param userProperty ignored |
| * @deprecated since 1.5.x. |
| * This was never a supported feature and has been |
| * deprecated without replacement. |
| * @ant.attribute ignore="true" |
| */ |
| @Deprecated |
| public void setUserProperty(boolean userProperty) { |
| log("DEPRECATED: Ignoring request to set user property in Property" |
| + " task.", Project.MSG_WARN); |
| } |
| |
| /** |
| * get the value of this property |
| * @return the current value or the empty string |
| */ |
| @Override |
| 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 |
| * @throws BuildException on error |
| */ |
| @Override |
| public void execute() throws BuildException { |
| if (getProject() == null) { |
| throw new IllegalStateException("project has not been set"); |
| } |
| |
| if (name != null) { |
| if (untypedValue == null && ref == null) { |
| throw new BuildException("You must specify value, location or " |
| + "refid with the name attribute", |
| getLocation()); |
| } |
| } else { |
| if (url == null && file == null && resource == null && env == null) { |
| throw new BuildException("You must specify url, file, resource or " |
| + "environment when not using the " |
| + "name attribute", getLocation()); |
| } |
| } |
| |
| if (url == null && file == null && resource == null && prefix != null) { |
| throw new BuildException("Prefix is only valid when loading from " |
| + "a url, file or resource", getLocation()); |
| } |
| |
| if (name != null && untypedValue != null) { |
| if (relative) { |
| try { |
| File from = untypedValue instanceof File ? (File)untypedValue : new File(untypedValue.toString()); |
| File to = basedir != null ? basedir : getProject().getBaseDir(); |
| String relPath = FileUtils.getRelativePath(to, from); |
| relPath = relPath.replace('/', File.separatorChar); |
| addProperty(name, relPath); |
| } catch (Exception e) { |
| throw new BuildException(e, getLocation()); |
| } |
| } else { |
| addProperty(name, untypedValue); |
| } |
| } |
| |
| if (file != null) { |
| loadFile(file); |
| } |
| |
| if (url != null) { |
| loadUrl(url); |
| } |
| |
| if (resource != null) { |
| loadResource(resource); |
| } |
| |
| if (env != null) { |
| loadEnvironment(env); |
| } |
| |
| if ((name != null) && (ref != null)) { |
| try { |
| addProperty(name, |
| ref.getReferencedObject(getProject()).toString()); |
| } catch (BuildException be) { |
| if (fallback != null) { |
| addProperty(name, |
| ref.getReferencedObject(fallback).toString()); |
| } else { |
| throw be; |
| } |
| } |
| } |
| } |
| |
| /** |
| * load properties from a url |
| * @param url url to load from |
| * @throws BuildException on error |
| */ |
| protected void loadUrl(URL url) throws BuildException { |
| Properties props = new Properties(); |
| log("Loading " + url, Project.MSG_VERBOSE); |
| try { |
| InputStream is = url.openStream(); |
| try { |
| loadProperties(props, is, url.getFile().endsWith(".xml")); |
| } finally { |
| if (is != null) { |
| is.close(); |
| } |
| } |
| addProperties(props); |
| } catch (IOException ex) { |
| throw new BuildException(ex, getLocation()); |
| } |
| } |
| |
| /** |
| * Loads the properties defined in the InputStream into the given |
| * property. On Java5+ it supports reading from XML based property |
| * definition. |
| * @param props The property object to load into |
| * @param is The input stream from where to load |
| * @param isXml <tt>true</tt> if we should try to load from xml |
| * @throws IOException if something goes wrong |
| * @since 1.8.0 |
| * @see "http://java.sun.com/dtd/properties.dtd" |
| * @see java.util.Properties#loadFromXML(InputStream) |
| */ |
| private void loadProperties( |
| Properties props, InputStream is, boolean isXml) throws IOException { |
| if (isXml) { |
| // load the xml based property definition |
| props.loadFromXML(is); |
| } else { |
| // load ".properties" format |
| props.load(is); |
| } |
| } |
| |
| /** |
| * load properties from a file |
| * @param file file to load |
| * @throws BuildException on error |
| */ |
| protected void loadFile(File file) throws BuildException { |
| Properties props = new Properties(); |
| log("Loading " + file.getAbsolutePath(), Project.MSG_VERBOSE); |
| try { |
| if (file.exists()) { |
| FileInputStream fis = null; |
| try { |
| fis = new FileInputStream(file); |
| loadProperties(props, fis, file.getName().endsWith(".xml")); |
| } finally { |
| FileUtils.close(fis); |
| } |
| addProperties(props); |
| } else { |
| log("Unable to find property file: " + file.getAbsolutePath(), |
| Project.MSG_VERBOSE); |
| } |
| } catch (IOException ex) { |
| throw new BuildException(ex, getLocation()); |
| } |
| } |
| |
| /** |
| * load properties from a resource in the current classpath |
| * @param name name of resource to load |
| */ |
| protected void loadResource(String name) { |
| Properties props = new Properties(); |
| log("Resource Loading " + name, Project.MSG_VERBOSE); |
| InputStream is = null; |
| ClassLoader cL = null; |
| boolean cleanup = false; |
| try { |
| if (classpath != null) { |
| cleanup = true; |
| cL = getProject().createClassLoader(classpath); |
| } else { |
| cL = this.getClass().getClassLoader(); |
| } |
| |
| if (cL == null) { |
| is = ClassLoader.getSystemResourceAsStream(name); |
| } else { |
| is = cL.getResourceAsStream(name); |
| } |
| |
| if (is != null) { |
| loadProperties(props, is, name.endsWith(".xml")); |
| addProperties(props); |
| } else { |
| log("Unable to find resource " + name, Project.MSG_WARN); |
| } |
| } catch (IOException ex) { |
| throw new BuildException(ex, getLocation()); |
| } finally { |
| FileUtils.close(is); |
| if (cleanup && cL != null) { |
| ((AntClassLoader) cL).cleanup(); |
| } |
| } |
| } |
| |
| /** |
| * load the environment values |
| * @param prefix prefix to place before them |
| */ |
| protected void loadEnvironment(String prefix) { |
| Properties props = new Properties(); |
| if (!prefix.endsWith(".")) { |
| prefix += "."; |
| } |
| log("Loading Environment " + prefix, Project.MSG_VERBOSE); |
| Map osEnv = Execute.getEnvironmentVariables(); |
| for (Iterator e = osEnv.entrySet().iterator(); e.hasNext();) { |
| Map.Entry entry = (Map.Entry) e.next(); |
| props.put(prefix + entry.getKey(), entry.getValue()); |
| } |
| addProperties(props); |
| } |
| |
| /** |
| * iterate through a set of properties, |
| * resolve them then assign them |
| * @param props the properties to iterate over |
| */ |
| protected void addProperties(Properties props) { |
| HashMap m = new HashMap(props); |
| resolveAllProperties(m); |
| for (Iterator it = m.keySet().iterator(); it.hasNext();) { |
| Object k = it.next(); |
| if (k instanceof String) { |
| String propertyName = (String) k; |
| if (prefix != null) { |
| propertyName = prefix + propertyName; |
| } |
| addProperty(propertyName, m.get(k)); |
| } |
| } |
| } |
| |
| /** |
| * add a name value pair to the project property set |
| * @param n name of property |
| * @param v value to set |
| */ |
| protected void addProperty(String n, String v) { |
| addProperty(n, (Object) v); |
| } |
| |
| /** |
| * add a name value pair to the project property set |
| * @param n name of property |
| * @param v value to set |
| * @since Ant 1.8 |
| */ |
| protected void addProperty(String n, Object v) { |
| PropertyHelper ph = PropertyHelper.getPropertyHelper(getProject()); |
| if (userProperty) { |
| if (ph.getUserProperty(n) == null) { |
| ph.setInheritedProperty(n, v); |
| } else { |
| log("Override ignored for " + n, Project.MSG_VERBOSE); |
| } |
| } else { |
| ph.setNewProperty(n, v); |
| } |
| } |
| |
| /** |
| * resolve properties inside a properties hashtable |
| * @param props properties object to resolve |
| */ |
| private void resolveAllProperties(Map props) throws BuildException { |
| PropertyHelper propertyHelper |
| = PropertyHelper.getPropertyHelper(getProject()); |
| new ResolvePropertyMap( |
| getProject(), |
| propertyHelper, |
| propertyHelper.getExpanders()) |
| .resolveAllProperties(props, getPrefix(), getPrefixValues()); |
| } |
| |
| } |