001: /*
002: * Copyright 2004 Outerthought bvba and Schaubroeck nv
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.outerj.daisy.configutil;
017:
018: import java.util.Properties;
019: import java.util.Stack;
020: import java.util.regex.Pattern;
021: import java.util.regex.Matcher;
022: import java.net.URLEncoder;
023: import java.net.URI;
024: import java.net.URISyntaxException;
025: import java.io.UnsupportedEncodingException;
026: import java.io.File;
027:
028: public class PropertyResolver {
029: public static String resolveProperties(String input) {
030: return resolveProperties(input, System.getProperties());
031: }
032:
033: /**
034: * Resolves properties in the input string referenced using
035: * ${property} syntax. Special 'function' properties are also
036: * supported, which use a syntax like ${url-encode:something}.
037: * Nested syntax is also supported, e.g.
038: * ${property and ${anotherproperty}}, which is mostly useful
039: * in combination with the function properties like 'url-encode:'.
040: */
041: public static String resolveProperties(String input,
042: Properties properties) {
043: StringBuilder result = new StringBuilder(input.length());
044: StringBuilder propertyBuffer = null;
045: Stack openPropertyBuffers = new Stack();
046:
047: final int STATE_DEFAULT = 0;
048: final int STATE_IN_PROP = 1;
049: int state = STATE_DEFAULT;
050:
051: for (int i = 0; i < input.length(); i++) {
052: char c = input.charAt(i);
053: switch (c) {
054: case '$':
055: if (i + 1 < input.length()
056: && input.charAt(i + 1) == '{') {
057: if (state == STATE_IN_PROP) {
058: openPropertyBuffers.push(propertyBuffer);
059: }
060: i++;
061: state = STATE_IN_PROP;
062: propertyBuffer = new StringBuilder();
063: } else {
064: (state == STATE_IN_PROP ? propertyBuffer : result)
065: .append("$");
066: }
067: break;
068: case '}':
069: if (state == STATE_IN_PROP) {
070: String propName = propertyBuffer.toString();
071: String propValue = evaluateProperty(propName,
072: properties);
073: String propEvalResult = propValue != null ? propValue
074: : "${" + propName + "}";
075: if (!openPropertyBuffers.empty()) {
076: propertyBuffer = (StringBuilder) openPropertyBuffers
077: .pop();
078: propertyBuffer.append(propEvalResult);
079: // stay in STATE_IN_PROP
080: } else {
081: result.append(propEvalResult);
082: state = STATE_DEFAULT;
083: }
084: } else {
085: result.append(c);
086: }
087: break;
088: default:
089: (state == STATE_IN_PROP ? propertyBuffer : result)
090: .append(c);
091: }
092: }
093:
094: if (state == STATE_IN_PROP) {
095: // process any property buffers still open
096: do {
097: if (!openPropertyBuffers.empty()) {
098: propertyBuffer = ((StringBuilder) openPropertyBuffers
099: .pop()).append("${").append(propertyBuffer);
100: } else {
101: result.append("${").append(propertyBuffer);
102: propertyBuffer = null;
103: }
104: } while (propertyBuffer != null);
105: }
106:
107: return result.toString();
108: }
109:
110: private static Pattern PROP_PATTERN = Pattern
111: .compile("^([^:]+):(.+)$");
112:
113: /**
114: * Evaluates properties containing special syntax.
115: *
116: * <p>Allows for things like ${url-encode:propname}.
117: */
118: private static String evaluateProperty(String input,
119: Properties properties) {
120: Matcher matcher = PROP_PATTERN.matcher(input);
121: if (matcher.matches()) {
122: String action = matcher.group(1);
123: String value = matcher.group(2);
124:
125: try {
126: if (action.equals("url-encode")) {
127: return URLEncoder.encode(value, "UTF-8");
128: } else if (action.equals("double-url-encode")) {
129: return URLEncoder.encode(URLEncoder.encode(value,
130: "UTF-8"), "UTF-8");
131: } else if (action.equals("tripple-url-encode")) {
132: return URLEncoder.encode(
133: URLEncoder.encode(URLEncoder.encode(value,
134: "UTF-8"), "UTF-8"), "UTF-8");
135: } else if (action.equals("uri-path-encode")) {
136: try {
137: return new URI(null, null, value, null)
138: .getRawSchemeSpecificPart();
139: } catch (URISyntaxException e) {
140: throw new RuntimeException(
141: "Error in uri-path-encode function of property resolver.",
142: e);
143: }
144: } else if (action.equals("file-to-uri")) {
145: return new File(value).toURI().toString();
146: } else {
147: return null;
148: }
149: } catch (UnsupportedEncodingException e) {
150: throw new RuntimeException(e);
151: }
152: } else {
153: return properties.getProperty(input);
154: }
155: }
156: }
|