001: /*
002: * $Id: FlexibleProperties.java,v 1.1 2003/08/15 20:23:20 ajzeneski Exp $
003: *
004: * Copyright (c) 2001 The Open For Business Project - www.ofbiz.org
005: *
006: * Permission is hereby granted, free of charge, to any person obtaining a
007: * copy of this software and associated documentation files (the "Software"),
008: * to deal in the Software without restriction, including without limitation
009: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
010: * and/or sell copies of the Software, and to permit persons to whom the
011: * Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included
014: * in all copies or substantial portions of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
017: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
018: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
019: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
021: * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
022: * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023: */
024: package org.ofbiz.base.util;
025:
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.net.URL;
029: import java.util.ArrayList;
030: import java.util.Enumeration;
031: import java.util.Iterator;
032: import java.util.Properties;
033: import java.util.Set;
034:
035: /**
036: * Simple Class for flexibly working with properties files
037: *
038: * @author <a href="mailto:jonesde@ofbiz.org">David E. Jones</a>
039: * @version $Revision
040: * @since 1.0
041: */
042: public class FlexibleProperties extends Properties {
043:
044: public static final String module = FlexibleProperties.class
045: .getName();
046:
047: private static final boolean truncateIfMissingDefault = false;
048: private static final boolean doPropertyExpansionDefault = true;
049:
050: private URL url = null;
051: private boolean doPropertyExpansion = doPropertyExpansionDefault;
052: private boolean truncateIfMissing = truncateIfMissingDefault;
053:
054: // constructors
055: public FlexibleProperties() {
056: super ();
057: }
058:
059: public FlexibleProperties(Properties properties) {
060: super (properties);
061: }
062:
063: public FlexibleProperties(URL url) {
064: this .url = url;
065: init();
066: }
067:
068: public FlexibleProperties(URL url, Properties properties) {
069: super (properties);
070: this .url = url;
071: init();
072: }
073:
074: // factories
075: public static FlexibleProperties makeFlexibleProperties(
076: Properties properties) {
077: return new FlexibleProperties(properties);
078: }
079:
080: public static FlexibleProperties makeFlexibleProperties(URL url) {
081: return new FlexibleProperties(url);
082: }
083:
084: public static FlexibleProperties makeFlexibleProperties(URL url,
085: Properties properties) {
086: return new FlexibleProperties(url, properties);
087: }
088:
089: public static FlexibleProperties makeFlexibleProperties(
090: String[] keysAndValues) {
091: // if they gave me an odd number of elements
092: if ((keysAndValues.length % 2) != 0) {
093: throw new IllegalArgumentException(
094: "FlexibleProperties(String[] keysAndValues) cannot accept an odd number of elements!");
095: }
096: Properties newProperties = new Properties();
097:
098: for (int i = 0; i < keysAndValues.length; i += 2) {
099: newProperties.setProperty(keysAndValues[i],
100: keysAndValues[i + 1]);
101: }
102:
103: return new FlexibleProperties(newProperties);
104: }
105:
106: private void init() {
107: try {
108: load();
109: } catch (IOException e) {
110: Debug.log(e, module);
111: }
112: }
113:
114: public boolean getDoPropertyExpansion() {
115: return doPropertyExpansion;
116: }
117:
118: public void setDoPropertyExpansion(boolean doPropertyExpansion) {
119: this .doPropertyExpansion = doPropertyExpansion;
120: }
121:
122: public boolean getTruncateIfMissing() {
123: return truncateIfMissing;
124: }
125:
126: public void setTruncateIfMissing(boolean truncateIfMissing) {
127: this .truncateIfMissing = truncateIfMissing;
128: }
129:
130: public URL getURL() {
131: return url;
132: }
133:
134: public void setURL(URL url) {
135: this .url = url;
136: init();
137: }
138:
139: public Properties getDefaultProperties() {
140: return this .defaults;
141: }
142:
143: public void setDefaultProperties(Properties defaults) {
144: this .defaults = new FlexibleProperties(defaults);
145: }
146:
147: protected synchronized void load() throws IOException {
148: if (url == null)
149: return;
150: InputStream in = null;
151:
152: try {
153: in = url.openStream();
154: } catch (Exception urlex) {
155: Debug.log(urlex,
156: "[FlexibleProperties.load]: Couldn't find the URL: "
157: + url, module);
158: }
159:
160: if (in == null)
161: throw new IOException("Could not open resource URL " + url);
162:
163: super .load(in);
164: in.close();
165:
166: if (defaults instanceof FlexibleProperties)
167: ((FlexibleProperties) defaults).reload();
168: if (getDoPropertyExpansion())
169: interpolateProperties();
170: }
171:
172: public synchronized void store(String header) throws IOException {
173: super .store(url.openConnection().getOutputStream(), header);
174: }
175:
176: public synchronized void reload() throws IOException {
177: Debug.log("Reloading the resource: " + url, module);
178: this .load();
179: }
180:
181: // ==== Property interpolation methods ====
182: public void interpolateProperties() {
183: if ((defaults != null)
184: && (defaults instanceof FlexibleProperties)) {
185: ((FlexibleProperties) defaults).interpolateProperties();
186: }
187: interpolateProperties(this , getTruncateIfMissing());
188: }
189:
190: public static void interpolateProperties(Properties props) {
191: interpolateProperties(props, truncateIfMissingDefault);
192: }
193:
194: public static void interpolateProperties(Properties props,
195: boolean truncateIfMissing) {
196: Enumeration keys = props.keys();
197:
198: while (keys.hasMoreElements()) {
199: String key = keys.nextElement().toString();
200: String value = props.getProperty(key);
201:
202: key = interpolate(key, props, truncateIfMissing);
203: props.setProperty(key, interpolate(value, props,
204: truncateIfMissing));
205: }
206: }
207:
208: public static String interpolate(String value, Properties props) {
209: return interpolate(value, props, truncateIfMissingDefault);
210: }
211:
212: public static String interpolate(String value, Properties props,
213: boolean truncateIfMissing) {
214: return interpolate(value, props, truncateIfMissing, null);
215: }
216:
217: public static String interpolate(String value, Properties props,
218: boolean truncateIfMissing, ArrayList beenThere) {
219: if (props == null || value == null)
220: return value;
221: if (beenThere == null) {
222: beenThere = new ArrayList();
223: // Debug.log("[FlexibleProperties.interpolate] starting interpolate: value=[" + value + "]");
224: } else {// Debug.log("[FlexibleProperties.interpolate] starting sub-interpolate: beenThere=[" + beenThere + "], value=[" + value + "]");
225: }
226: int start = value.indexOf("${");
227:
228: while (start > -1) {
229: int end = value.indexOf("}", (start + 2));
230:
231: if (end > start + 2) {
232: String keyToExpand = value.substring((start + 2), end);
233: int nestedStart = keyToExpand.indexOf("${");
234:
235: while (nestedStart > -1) {
236: end = value.indexOf("}", (end + 1));
237: if (end > -1) {
238: keyToExpand = value.substring((start + 2), end);
239: nestedStart = keyToExpand.indexOf("${",
240: (nestedStart + 2));
241: } else {
242: Debug
243: .log(
244: "[FlexibleProperties.interpolate] Malformed value: ["
245: + value
246: + "] "
247: + "contained unbalanced start \"${\" and end \"}\" characters",
248: module);
249: return value;
250: }
251: }
252: // if this key needs to be interpolated itself
253: if (keyToExpand.indexOf("${") > -1) {
254: // Debug.log("[FlexibleProperties.interpolate] recursing on key: keyToExpand=[" + keyToExpand + "]");
255:
256: // save current beenThere and restore after so the later interpolates don't get messed up
257: ArrayList tempBeenThere = new ArrayList(beenThere);
258:
259: beenThere.add(keyToExpand);
260: keyToExpand = interpolate(keyToExpand, props,
261: truncateIfMissing, beenThere);
262: beenThere = tempBeenThere;
263: }
264: if (beenThere.contains(keyToExpand)) {
265: beenThere.add(keyToExpand);
266: Debug.log(
267: "[FlexibleProperties.interpolate] Recursion loop detected: Property:["
268: + beenThere.get(0) + "] "
269: + "included property: ["
270: + keyToExpand + "]", module);
271: Debug.log(
272: "[FlexibleProperties.interpolate] Recursion loop path:"
273: + beenThere, module);
274: return value;
275: } else {
276: String expandValue = null;
277:
278: if (keyToExpand.startsWith("env.")) {
279: String envValue = System
280: .getProperty(keyToExpand.substring(4));
281:
282: if (envValue == null) {
283: Debug
284: .log(
285: "[FlexibleProperties.interpolate] ERROR: Could not find environment variable named: "
286: + keyToExpand
287: .substring(4),
288: module);
289: } else {
290: expandValue = envValue;
291: // Debug.log("[FlexibleProperties.interpolate] Got expandValue from environment: " + expandValue);
292: }
293: } else {
294: expandValue = props.getProperty(keyToExpand);
295: // Debug.log("[FlexibleProperties.interpolate] Got expandValue from another property: " + expandValue);
296: }
297:
298: if (expandValue != null) {
299: // Key found - interpolate
300:
301: // if this value needs to be interpolated itself
302: if (expandValue.indexOf("${") > -1) {
303: // Debug.log("[FlexibleProperties] recursing on value: expandValue=[" + expandValue + "]");
304: // save current beenThere and restore after so the later interpolates don't get messed up
305: ArrayList tempBeenThere = new ArrayList(
306: beenThere);
307:
308: beenThere.add(keyToExpand);
309: expandValue = interpolate(expandValue,
310: props, truncateIfMissing, beenThere);
311: beenThere = tempBeenThere;
312: }
313: value = value.substring(0, start) + expandValue
314: + value.substring(end + 1);
315: end = start + expandValue.length();
316:
317: } else {
318: // Key not found - (expandValue == null)
319: if (truncateIfMissing == true) {
320: value = value.substring(0, start)
321: + value.substring(end + 1);
322: }
323: }
324: }
325: } else {
326: Debug.log("[FlexibleProperties.interpolate] Value ["
327: + value + "] starts but does end variable",
328: module);
329: return value;
330: }
331: start = value.indexOf("${", end);
332: }
333: return value;
334: }
335:
336: // ==== Utility/override methods ====
337: public Object clone() {
338: FlexibleProperties c = (FlexibleProperties) super .clone();
339:
340: // avoid recursion for some reason someone used themselves as defaults
341: if (defaults != null && !this .equals(defaults)) {
342: c.defaults = (FlexibleProperties) getDefaultProperties()
343: .clone();
344: }
345: return c;
346: }
347:
348: public String toString() {
349: StringBuffer retVal = new StringBuffer();
350: Set keySet = keySet();
351: Iterator keys = keySet.iterator();
352:
353: while (keys.hasNext()) {
354: String key = keys.next().toString();
355: String value = getProperty(key);
356:
357: retVal.append(key);
358: retVal.append("=");
359: retVal.append(value);
360: retVal.append("\n");
361: }
362:
363: return retVal.toString();
364: }
365: }
|