001: /* PropertyPlaceholderTransformer.java
002: *
003: * DDSteps - Data Driven JUnit Test Steps
004: * Copyright (C) 2005 Jayway AB
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License version 2.1 as published by the Free Software Foundation.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, visit
017: * http://www.opensource.org/licenses/lgpl-license.php
018: */
019: package org.ddsteps.dataset.transform;
020:
021: import java.util.regex.Matcher;
022: import java.util.regex.Pattern;
023:
024: import org.apache.commons.collections.Transformer;
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.ddsteps.properties.SystemPropertiesLoaderSupport;
028: import org.springframework.core.io.ClassPathResource;
029:
030: /**
031: * Searches for and replaces substrings on the format ${foo.bar}.
032: * <p>
033: * The whole expression is replaced with the property value of foo.bar in the
034: * properties passed to the constructor.
035: * <p>
036: * Please note that the expression language is quite simple - there is no way of
037: * quoting, so don't use ${} in your test data.
038: * <p>
039: * By default, properties are loaded from the classpath, from
040: * DDSteps-placeholders.properties in the "default" package. By default it also
041: * ignores if the file is not found.
042: *
043: * @author Adam
044: * @version $Id: PropertyPlaceholderTransformer.java,v 1.1 2005/08/25 08:32:38
045: * adamskogman Exp $
046: */
047: public class PropertyPlaceholderTransformer extends
048: SystemPropertiesLoaderSupport implements Transformer {
049:
050: private static Log LOG = LogFactory
051: .getLog(PropertyPlaceholderTransformer.class);
052:
053: /**
054: * Constant: Pattern for matching expressions
055: */
056: final static private Pattern EXPRESSION_PATTERN = Pattern
057: .compile("\\$\\{\\s*(\\S+)\\s*\\}");
058:
059: /**
060: * Property: Remove unknown expressions, or let them stay?
061: */
062: protected boolean removeUnknown = false;
063:
064: /**
065: * Default constructor.
066: * <p>
067: * Sets defaults to load "/ddsteps-placeholders.properties".
068: */
069: public PropertyPlaceholderTransformer() {
070: super ();
071: setLocation(new ClassPathResource(
072: "/ddsteps-placeholders.properties"));
073: setIgnoreResourceNotFound(true);
074: }
075:
076: /**
077: * Look for and replace strings.
078: *
079: * @see org.apache.commons.collections.Transformer#transform(java.lang.Object)
080: */
081: public Object transform(Object arg0) {
082:
083: // Ignore null
084: if (arg0 == null) {
085: return null;
086: }
087:
088: // ignore anything that is not a String
089: if (!(arg0 instanceof String)) {
090: return arg0;
091: }
092:
093: String input = (String) arg0;
094:
095: Matcher matcher = EXPRESSION_PATTERN.matcher(input);
096:
097: // Avoid resource waste, check if found first!
098: if (!matcher.find()) {
099: // Not found at all
100: if (LOG.isDebugEnabled()) {
101: LOG
102: .debug("[transform] No expressions found in string '"
103: + input + "'.");
104: }
105: return input;
106: }
107:
108: if (LOG.isInfoEnabled()) {
109: LOG.info("[transform] Replacing expressions in string '"
110: + input + "'");
111: }
112:
113: matcher.reset();
114:
115: StringBuffer sb = new StringBuffer(input.length() * 2);
116:
117: while (matcher.find()) {
118:
119: String expression = matcher.group(1);
120:
121: if (LOG.isDebugEnabled()) {
122: LOG.debug("[transform] Found expression '"
123: + matcher.group() + "'.");
124: }
125:
126: String replacement = findReplacement(expression);
127:
128: // found
129: if (replacement != null) {
130: replacement = escapeReplacement(replacement);
131: }
132: // not found
133: else if (this .removeUnknown) {
134: // drop expression
135: replacement = "";
136:
137: if (LOG.isDebugEnabled()) {
138: LOG
139: .debug("[transform] Removing unknown expression '"
140: + matcher.group() + "'.");
141: }
142: } else {
143: // Add full expression
144: replacement = "$0";
145:
146: if (LOG.isDebugEnabled()) {
147: LOG
148: .debug("[transform] Leaving unknown expression '"
149: + matcher.group() + "'.");
150: }
151: }
152:
153: matcher.appendReplacement(sb, replacement);
154: }
155:
156: matcher.appendTail(sb);
157:
158: String output = sb.toString();
159:
160: if (LOG.isInfoEnabled()) {
161: LOG.info("[transform] Returning string '" + output + "'.");
162: }
163:
164: return output;
165:
166: }
167:
168: /**
169: * Escape \ and $
170: *
171: * @param string
172: * @return The escaped string
173: */
174: String escapeReplacement(String string) {
175: // \ -> \\
176: string = string.replaceAll("\\\\", "\\\\\\\\");
177: // $ -> \$
178: string = string.replaceAll("\\$", "\\\\\\$");
179:
180: return string;
181: }
182:
183: /**
184: * Finds a replacement for the expression.
185: *
186: * @param expression
187: * Already trimmed
188: * @return Null if not found
189: */
190: String findReplacement(String expression) {
191:
192: String replacement = properties.getProperty(expression);
193:
194: if (LOG.isDebugEnabled()) {
195: if (replacement != null) {
196: LOG.debug("[transform] Found replacement '"
197: + replacement + "' for expression '"
198: + expression + "'.");
199: } else {
200: LOG.debug("[transform] No replacement for expression '"
201: + expression + "'.");
202: }
203: }
204: return replacement;
205:
206: }
207:
208: /**
209: * Remove expressions that could not be evaluated (property not found). Set
210: * to true to gracefully remove properties that were not found. Set to false
211: * if you have a chain if transformers to let another transformer try to
212: * handle it.
213: *
214: * @param dropUnknown
215: */
216: public void setRemoveUnknown(boolean dropUnknown) {
217: this.removeUnknown = dropUnknown;
218: }
219:
220: }
|