001: package org.tigris.scarab.util.build;
002:
003: /* ================================================================
004: * Copyright (c) 2000-2002 CollabNet. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are
008: * met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowlegement: "This product includes
019: * software developed by Collab.Net <http://www.Collab.Net/>."
020: * Alternately, this acknowlegement may appear in the software itself, if
021: * and wherever such third-party acknowlegements normally appear.
022: *
023: * 4. The hosted project names must not be used to endorse or promote
024: * products derived from this software without prior written
025: * permission. For written permission, please contact info@collab.net.
026: *
027: * 5. Products derived from this software may not use the "Tigris" or
028: * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
029: * prior written permission of Collab.Net.
030: *
031: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
032: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
033: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
034: * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
035: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
036: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
037: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
038: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
039: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
040: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
041: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
042: *
043: * ====================================================================
044: *
045: * This software consists of voluntary contributions made by many
046: * individuals on behalf of Collab.Net.
047: */
048:
049: import java.io.BufferedReader;
050: import java.io.File;
051: import java.io.FileReader;
052: import java.io.FileWriter;
053: import java.io.IOException;
054: import java.io.PrintWriter;
055: import java.io.Reader;
056: import java.io.Writer;
057: import java.util.Hashtable;
058: import java.util.List;
059: import java.util.Map;
060: import java.util.StringTokenizer;
061: import java.util.Vector;
062:
063: /**
064: * This class is used as ant task backend for the generation
065: * of a property file by use of a template file.
066: *
067: * @author <a href="mailto:dabbous@saxess.com">Hussayn Dabbous</a>
068: * @version $Id: PropertyFileGenerator.java 9429 2005-02-25 21:24:30Z dabbous $
069: */
070:
071: public class PropertyFileGenerator {
072: /**
073: * This file will be read in and all property values
074: * in it will be replaced by the actual values as they
075: * are in the online environment.
076: */
077: private File templateFile;
078:
079: /**
080: * This is the file, which shall contain the
081: * final property name/value pairs. It's overall
082: * layout is similar to the templateFile, but the
083: * property values are generated from the current
084: * online property settings.
085: */
086: private File customFile;
087:
088: /**
089: * This Map contains the user specified properties
090: * defined in the list of property files given
091: * when the method setProperties() is called.
092: **/
093:
094: private Map userProperties;
095:
096: /**
097: * Setter: set the path to the template file.
098: * Throws an exception, if the template file does not exist.
099: * @param theTemplatePath
100: * @return
101: */
102: public boolean setTemplate(String theTemplatePath) {
103: boolean status = true;
104: templateFile = new File(theTemplatePath);
105: if (!templateFile.exists()) {
106: status = false;
107: }
108: return status;
109: }
110:
111: /**
112: * Return the absolute path to the templateFile, or null, if
113: * no template file has been set.
114: * @return
115: */
116: public String getTemplate() {
117: return (templateFile == null) ? null : templateFile
118: .getAbsolutePath();
119: }
120:
121: /**
122: * Setter: set the path to the final property file.
123: * Throws an exception, if the customFile exist,
124: * but can't be overwritten (due to permission settings).
125: * @param theCustomPath
126: */
127: public boolean setCustom(String theCustomPath) {
128: boolean status = true;
129: customFile = new File(theCustomPath);
130: if (!customFile.getParentFile().exists()) {
131: customFile.getParentFile().mkdir();
132: }
133:
134: if (customFile.exists() && !customFile.canWrite()) {
135: status = false;
136: }
137:
138: return status;
139: }
140:
141: /**
142: * Return the absoute path to the customFile, or null, if no path
143: * has been set.
144: * @return
145: */
146: public String getCustom() {
147: String result = null;
148: if (customFile != null) {
149: result = customFile.getAbsolutePath();
150: }
151: return result;
152: }
153:
154: /**
155: * Setter: Create a Map of unresolved properties from
156: * the files defined in theUserpathes.
157: * Throws an exception, if a customFile exist,
158: * but can't be read (due to permission settings).
159: * First definition of a property wins.
160: * @param theUserPath
161: */
162: public boolean setProperties(String theUserPathes) {
163: List filePathes = createPathList(theUserPathes);
164:
165: if (filePathes != null) {
166: userProperties = new Hashtable();
167: for (int index = 0; index < filePathes.size(); index++) {
168: File file = new File((String) filePathes.get(index));
169: if (file.exists()) {
170: if (!file.canRead()) {
171: throw new RuntimeException(
172: "No Read permission for file ["
173: + filePathes.get(index) + "]");
174: }
175: try {
176: addUnresolvedProperties(file, userProperties);
177: } catch (IOException e) {
178: throw new RuntimeException(
179: "Could not read file ["
180: + filePathes.get(index) + "]");
181: }
182: }
183: }
184: }
185: return true;
186: }
187:
188: /**
189: * @param file
190: * @param properties
191: */
192: private void addUnresolvedProperties(File file, Map properties)
193: throws IOException {
194: Reader reader = new FileReader(file);
195: BufferedReader br = new BufferedReader(reader);
196:
197: String line;
198:
199: while ((line = br.readLine()) != null) {
200: String trimmedLine = line.trim();
201: if (trimmedLine.equals("") || trimmedLine.startsWith("#")) {
202: continue; // forget comment lines and empty lines.
203: } else {
204: String name = null;
205: String value = null;
206: int index = line.indexOf("=");
207: if (index >= 0) {
208: name = line.substring(0, index).trim();
209: value = line.substring(index + 1).trim();
210: } else {
211: name = line.trim();
212: value = "";
213: }
214:
215: if (properties.get(name) == null) {
216: properties.put(name, value);
217: }
218: }
219: }
220: br.close();
221: }
222:
223: /**
224: * Convert a unix style pathlist to a List of pathes.
225: * E.g. the String "path1:path2:path3" is converted into
226: * a three component vector containing "path1", "path2" and
227: * "path3"
228: * If theUserpathes contains no path, this method returns null
229: * @param theUserPathes
230: * @return
231: */
232: private List createPathList(String theUserPathes) {
233: List result = null;
234: StringTokenizer stok = new StringTokenizer(theUserPathes, ":");
235: while (stok.hasMoreTokens()) {
236: String path = stok.nextToken();
237: if (path.length() == 1 && stok.hasMoreTokens()) {
238: // deal with windows drive letters, e.g. "c:scarab/build.properties"
239: path += ":" + stok.nextToken();
240: }
241:
242: if (result == null) {
243: result = new Vector();
244: }
245:
246: result.add(path);
247: }
248: return result;
249: }
250:
251: /**
252: * Read the templateFile and behave according to
253: * following rule set:
254: * <ul>
255: * <li> rule 1: Copy every line, which does NOT contain
256: * a property verbatim to the customFile.</li>
257: *
258: * <li> rule 2: Retrieve the current online value of each
259: * property found in the templateFile and generate an
260: * appropriate name/value pair in the customFile.</li>
261: *
262: * <li> rule 3: If a property value starts with a "${" in
263: * the templateFile, keep the value as is. By this we
264: * can propagate ${variables} to the customFile, which
265: * will be resolved during startup of Scarab.</li>
266: * </ul>
267: *
268: */
269: public void execute(PropertyGetter props) throws IOException {
270:
271: Reader reader = new FileReader(templateFile);
272: Writer writer = new FileWriter(customFile);
273: BufferedReader br = new BufferedReader(reader);
274: PrintWriter pw = new PrintWriter(writer);
275:
276: String line;
277:
278: while ((line = br.readLine()) != null) {
279: String trimmedLine = line.trim();
280: if (trimmedLine.equals("") || trimmedLine.startsWith("#")) {
281: pw.println(line);
282: } else {
283: String resultLine = createResultLine(line, props);
284: pw.println(resultLine);
285: }
286: }
287: pw.close();
288: br.close();
289:
290: }
291:
292: /**
293: * Read the current line and behave according to
294: * following rule set:
295: * <ul>
296: * <li> rule 1: If the line does not contain a property, copy
297: * it verbatim.</li>
298: *
299: * <li> rule 2: Retrieve the current online value of the
300: * property found in the line parameter and generate an
301: * appropriate name/value pair in the resultLine.</li>
302: *
303: * <li> rule 3: If a property value starts with a "${" in
304: * the line, keep the value as is. By this we
305: * can propagate ${variables} to the customFile, which
306: * will be "resolved late" (during startup of Scarab).</li>
307: * </ul>
308: * @param line
309: * @param props
310: * @return
311: */
312: private String createResultLine(String line, PropertyGetter props) {
313: String propertyName = null;
314: String templateValue = null;
315: String resultLine;
316: int index = line.indexOf("=");
317: if (index >= 0) {
318: propertyName = line.substring(0, index).trim();
319: templateValue = line.substring(index + 1).trim();
320: int beginOfValue = line.indexOf(templateValue, index + 1);
321: resultLine = (beginOfValue == -1) ? line : line.substring(
322: 0, beginOfValue);
323: } else {
324: propertyName = line.trim();
325: templateValue = "";
326: int endOfLine = line.indexOf(propertyName)
327: + propertyName.length();
328: resultLine = ((endOfLine == -1) ? line : line.substring(0,
329: endOfLine))
330: + " = ";
331: }
332:
333: String newValue = (String) userProperties.get(propertyName);
334:
335: if (newValue == null) {
336: newValue = (String) props.getProperty(propertyName,
337: templateValue);
338: if (newValue == null) {
339: newValue = "";
340: }
341: } else if (newValue.equalsIgnoreCase("**generated**")) {
342: String dbtype = (String) props.getProperty(
343: "scarab.database.type", "hypersonic");
344: if (dbtype.equals("")) {
345: dbtype = "hypersonic";
346: }
347: newValue = "${" + propertyName + "." + dbtype + "}";
348: }
349:
350: if (newValue.equals(templateValue)) {
351: resultLine = line;
352: } else {
353: resultLine += newValue;
354: }
355: return resultLine;
356: }
357: }
|