001/*
002/*
003 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved.
004 * 
005 * http://www.izforge.com/izpack/
006 * http://developer.berlios.de/projects/izpack/
007 * 
008 * Copyright 2002 Marcus Wolschon
009 * Copyright 2002 Jan Blok
010 * Copyright 2004 Klaus Bartz
011 * 
012 * Licensed under the Apache License, Version 2.0 (the "License");
013 * you may not use this file except in compliance with the License.
014 * You may obtain a copy of the License at
015 * 
016 *     http://www.apache.org/licenses/LICENSE-2.0
017 *     
018 * Unless required by applicable law or agreed to in writing, software
019 * distributed under the License is distributed on an "AS IS" BASIS,
020 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
021 * See the License for the specific language governing permissions and
022 * limitations under the License.
023 */
024package com.izforge.izpack.ant;
025
026import java.util.Enumeration;
027import java.util.Vector;
028
029import org.apache.tools.ant.BuildException;
030import org.apache.tools.ant.Project;
031
032/**
033 * A nested element holder for the installation configuration document content.
034 * The installation document must be passed in using a CDATA element.
035 * 
036 * @author Scott Stark
037 * @version $Revision: 1.1 $
038 */
039public class ConfigHolder
040{
041    /** The parent element project */
042    private Project project;
043
044    /** The config element body text with ${x} property references replaced */
045    private String installText;
046
047    /**
048     * Taken from the ant org.apache.tools.ant.PropertyHelper and '$' replaced
049     * with '@' to deal with @{x} style property references.
050     * 
051     * Parses a string containing @{xxx} style property references
052     * into two lists. The first list is a collection of text fragments, while
053     * the other is a set of string property names. null entries in the
054     * first list indicate a property reference from the second list.
055     * 
056     * It can be overridden with a more efficient or customized version.
057     * 
058     * @param value Text to parse. Must not be null.
059     * @param fragments List to add text fragments to. Must not be null.
060     * @param propertyRefs List to add property names to. Must not be null.
061     * 
062     * @exception BuildException if the string contains an opening @{ without a
063     * closing }
064     */
065    static void parseCompileProperties(String value, Vector fragments, Vector propertyRefs)
066            throws BuildException
067    {
068        int prev = 0;
069        int pos;
070        // search for the next instance of $ from the 'prev' position
071        while ((pos = value.indexOf("@", prev)) >= 0)
072        {
073
074            // if there was any text before this, add it as a fragment
075            // TODO, this check could be modified to go if pos>prev;
076            // seems like this current version could stick empty strings
077            // into the list
078            if (pos > 0)
079            {
080                fragments.addElement(value.substring(prev, pos));
081            }
082            // if we are at the end of the string, we tack on a $
083            // then move past it
084            if (pos == (value.length() - 1))
085            {
086                fragments.addElement("@");
087                prev = pos + 1;
088            }
089            else if (value.charAt(pos + 1) != '{')
090            {
091                // peek ahead to see if the next char is a property or not
092                // not a property: insert the char as a literal
093                /*
094                 * fragments.addElement(value.substring(pos + 1, pos + 2)); prev = pos + 2;
095                 */
096                if (value.charAt(pos + 1) == '@')
097                {
098                    // backwards compatibility two $ map to one mode
099                    fragments.addElement("@");
100                    prev = pos + 2;
101                }
102                else
103                {
104                    // new behaviour: $X maps to $X for all values of X!='$'
105                    fragments.addElement(value.substring(pos, pos + 2));
106                    prev = pos + 2;
107                }
108
109            }
110            else
111            {
112                // property found, extract its name or bail on a typo
113                int endName = value.indexOf('}', pos);
114                if (endName < 0)
115                {
116                    throw new BuildException("Syntax error in property: " + value);
117                }
118                String propertyName = value.substring(pos + 2, endName);
119                fragments.addElement(null);
120                propertyRefs.addElement(propertyName);
121                prev = endName + 1;
122            }
123        }
124        // no more @ signs found
125        // if there is any tail to the file, append it
126        if (prev < value.length())
127        {
128            fragments.addElement(value.substring(prev));
129        }
130    }
131
132    ConfigHolder(Project project)
133    {
134        this.project = project;
135    }
136
137    /**
138     * Called by ant to set the config element content. The content is scanned
139     * for @{x} style property references and replaced with the x project
140     * property.
141     * 
142     * @param rawText - the raw config element body text.
143     */
144    public void addText(String rawText)
145    {
146        // Locate the @{x} references
147        Vector fragments = new Vector();
148        Vector propertyRefs = new Vector();
149        parseCompileProperties(rawText, fragments, propertyRefs);
150
151        // Replace the references with the project property value
152        StringBuffer sb = new StringBuffer();
153        Enumeration i = fragments.elements();
154        Enumeration j = propertyRefs.elements();
155
156        while (i.hasMoreElements())
157        {
158            String fragment = (String) i.nextElement();
159            if (fragment == null)
160            {
161                String propertyName = (String) j.nextElement();
162                Object replacement = null;
163
164                // try to get it from the project
165                if (replacement == null)
166                {
167                    replacement = project.getProperty(propertyName);
168                }
169
170                if (replacement == null)
171                {
172                    project.log("Property @{" + propertyName + "} has not been set",
173                            Project.MSG_VERBOSE);
174                }
175                if (replacement != null)
176                    fragment = replacement.toString();
177                else
178                    fragment = "@{" + propertyName + "}";
179            }
180            sb.append(fragment);
181        }
182
183        installText = sb.toString();
184    }
185
186    /**
187     * Get the config element body text with @{x} property references replaced
188     * 
189     * @return the processed config element body text.
190     */
191    public String getText()
192    {
193        return installText;
194    }
195
196}