View Javadoc

1   /* ============================================================
2    * JRobin : Pure java implementation of RRDTool's functionality
3    * ============================================================
4    *
5    * Project Info:  http://www.jrobin.org
6    * Project Lead:  Sasa Markovic (saxon@jrobin.org);
7    *
8    * (C) Copyright 2003-2005, by Sasa Markovic.
9    *
10   * Developers:    Sasa Markovic (saxon@jrobin.org)
11   *
12   *
13   * This library is free software; you can redistribute it and/or modify it under the terms
14   * of the GNU Lesser General Public License as published by the Free Software Foundation;
15   * either version 2.1 of the License, or (at your option) any later version.
16   *
17   * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18   * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19   * See the GNU Lesser General Public License for more details.
20   *
21   * You should have received a copy of the GNU Lesser General Public License along with this
22   * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
23   * Boston, MA 02111-1307, USA.
24   */
25  
26  package org.jrobin.core;
27  
28  import org.w3c.dom.Element;
29  import org.w3c.dom.Node;
30  import org.xml.sax.InputSource;
31  
32  import java.awt.*;
33  import java.io.File;
34  import java.io.IOException;
35  import java.util.*;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38  
39  /**
40   * Class used as a base class for various XML template related classes. Class provides
41   * methods for XML source parsing and XML tree traversing. XML source may have unlimited
42   * number of placeholders (variables) in the format <code>${variable_name}</code>.
43   * Methods are provided to specify variable values at runtime.
44   * Note that this class has limited functionality: XML source gets parsed, and variable
45   * values are collected. You have to extend this class to do something more useful.<p>
46   */
47  public abstract class XmlTemplate {
48  	private static final String PATTERN_STRING = "\\$\\{(\\w+)\\}";
49  	private static final Pattern PATTERN = Pattern.compile(PATTERN_STRING);
50  
51  	protected Element root;
52  	private HashMap<String, Object> valueMap = new HashMap<String, Object>();
53  	private HashSet<Node> validatedNodes = new HashSet<Node>();
54  
55  	protected XmlTemplate(InputSource xmlSource) throws IOException, RrdException {
56  		root = Util.Xml.getRootElement(xmlSource);
57  	}
58  
59  	protected XmlTemplate(String xmlString) throws IOException, RrdException {
60  		root = Util.Xml.getRootElement(xmlString);
61  	}
62  
63  	protected XmlTemplate(File xmlFile) throws IOException, RrdException {
64  		root = Util.Xml.getRootElement(xmlFile);
65  	}
66  
67  	/**
68  	 * Removes all placeholder-value mappings.
69  	 */
70  	public void clearValues() {
71  		valueMap.clear();
72  	}
73  
74  	/**
75  	 * Sets value for a single XML template variable. Variable name should be specified
76  	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
77  	 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
78  	 *
79  	 * @param name  variable name
80  	 * @param value value to be set in the XML template
81  	 */
82  	public void setVariable(String name, String value) {
83  		valueMap.put(name, value);
84  	}
85  
86  	/**
87  	 * Sets value for a single XML template variable. Variable name should be specified
88  	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
89  	 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
90  	 *
91  	 * @param name  variable name
92  	 * @param value value to be set in the XML template
93  	 */
94  	public void setVariable(String name, int value) {
95  		valueMap.put(name, value);
96  	}
97  
98  	/**
99  	 * Sets value for a single XML template variable. Variable name should be specified
100 	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
101 	 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
102 	 *
103 	 * @param name  variable name
104 	 * @param value value to be set in the XML template
105 	 */
106 	public void setVariable(String name, long value) {
107 		valueMap.put(name, value);
108 	}
109 
110 	/**
111 	 * Sets value for a single XML template variable. Variable name should be specified
112 	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
113 	 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
114 	 *
115 	 * @param name  variable name
116 	 * @param value value to be set in the XML template
117 	 */
118 	public void setVariable(String name, double value) {
119 		valueMap.put(name, value);
120 	}
121 
122 	/**
123 	 * Sets value for a single XML template variable. Variable name should be specified
124 	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
125 	 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
126 	 *
127 	 * @param name  variable name
128 	 * @param value value to be set in the XML template
129 	 */
130 	public void setVariable(String name, Color value) {
131 		String r = byteToHex(value.getRed());
132 		String g = byteToHex(value.getGreen());
133 		String b = byteToHex(value.getBlue());
134 		String a = byteToHex(value.getAlpha());
135 		valueMap.put(name, "#" + r + g + b + a);
136 	}
137 
138 	private String byteToHex(int i) {
139 		String s = Integer.toHexString(i);
140 		while (s.length() < 2) {
141 			s = "0" + s;
142 		}
143 		return s;
144 	}
145 
146 	/**
147 	 * Sets value for a single XML template variable. Variable name should be specified
148 	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
149 	 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
150 	 *
151 	 * @param name  variable name
152 	 * @param value value to be set in the XML template
153 	 */
154 	public void setVariable(String name, Date value) {
155 		setVariable(name, Util.getTimestamp(value));
156 	}
157 
158 	/**
159 	 * Sets value for a single XML template variable. Variable name should be specified
160 	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
161 	 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
162 	 *
163 	 * @param name  variable name
164 	 * @param value value to be set in the XML template
165 	 */
166 	public void setVariable(String name, Calendar value) {
167 		setVariable(name, Util.getTimestamp(value));
168 	}
169 
170 	/**
171 	 * Sets value for a single XML template variable. Variable name should be specified
172 	 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
173 	 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
174 	 *
175 	 * @param name  variable name
176 	 * @param value value to be set in the XML template
177 	 */
178 	public void setVariable(String name, boolean value) {
179 		valueMap.put(name, "" + value);
180 	}
181 
182 	/**
183 	 * Searches the XML template to see if there are variables in there that
184 	 * will need to be set.
185 	 *
186 	 * @return True if variables were detected, false if not.
187 	 */
188 	public boolean hasVariables() {
189 		return PATTERN.matcher(root.toString()).find();
190 	}
191 
192 	/**
193 	 * Returns the list of variables that should be set in this template.
194 	 *
195 	 * @return List of variable names as an array of strings.
196 	 */
197 	public String[] getVariables() {
198 		ArrayList<String> list = new ArrayList<String>();
199 		Matcher m = PATTERN.matcher(root.toString());
200 
201 		while (m.find()) {
202 			String var = m.group(1);
203 			if (!list.contains(var)) {
204 				list.add(var);
205 			}
206 		}
207 
208 		return list.toArray(new String[list.size()]);
209 	}
210 
211 	protected static Node[] getChildNodes(Node parentNode, String childName) {
212 		return Util.Xml.getChildNodes(parentNode, childName);
213 	}
214 
215 	protected static Node[] getChildNodes(Node parentNode) {
216 		return Util.Xml.getChildNodes(parentNode, null);
217 	}
218 
219 	protected static Node getFirstChildNode(Node parentNode, String childName) throws RrdException {
220 		return Util.Xml.getFirstChildNode(parentNode, childName);
221 	}
222 
223 	protected boolean hasChildNode(Node parentNode, String childName) {
224 		return Util.Xml.hasChildNode(parentNode, childName);
225 	}
226 
227 	protected String getChildValue(Node parentNode, String childName) throws RrdException {
228 		return getChildValue(parentNode, childName, true);
229 	}
230 
231 	protected String getChildValue(Node parentNode, String childName, boolean trim) throws RrdException {
232 		String value = Util.Xml.getChildValue(parentNode, childName, trim);
233 		return resolveMappings(value);
234 	}
235 
236 	protected String getValue(Node parentNode) {
237 		return getValue(parentNode, true);
238 	}
239 
240 	protected String getValue(Node parentNode, boolean trim) {
241 		String value = Util.Xml.getValue(parentNode, trim);
242 		return resolveMappings(value);
243 	}
244 
245 	private String resolveMappings(String templateValue) {
246 		if (templateValue == null) {
247 			return null;
248 		}
249 		Matcher matcher = PATTERN.matcher(templateValue);
250 		StringBuffer result = new StringBuffer();
251 		int lastMatchEnd = 0;
252 		while (matcher.find()) {
253 			String var = matcher.group(1);
254 			if (valueMap.containsKey(var)) {
255 				// mapping found
256 				result.append(templateValue.substring(lastMatchEnd, matcher.start()));
257 				result.append(valueMap.get(var).toString());
258 				lastMatchEnd = matcher.end();
259 			}
260 			else {
261 				// no mapping found - this is illegal
262 				// throw runtime exception
263 				throw new IllegalArgumentException("No mapping found for template variable ${" + var + "}");
264 			}
265 		}
266 		result.append(templateValue.substring(lastMatchEnd));
267 		return result.toString();
268 	}
269 
270 	protected int getChildValueAsInt(Node parentNode, String childName) throws RrdException {
271 		String valueStr = getChildValue(parentNode, childName);
272 		return Integer.parseInt(valueStr);
273 	}
274 
275 	protected int getValueAsInt(Node parentNode) {
276 		String valueStr = getValue(parentNode);
277 		return Integer.parseInt(valueStr);
278 	}
279 
280 	protected long getChildValueAsLong(Node parentNode, String childName) throws RrdException {
281 		String valueStr = getChildValue(parentNode, childName);
282 		return Long.parseLong(valueStr);
283 	}
284 
285 	protected long getValueAsLong(Node parentNode) {
286 		String valueStr = getValue(parentNode);
287 		return Long.parseLong(valueStr);
288 	}
289 
290 	protected double getChildValueAsDouble(Node parentNode, String childName) throws RrdException {
291 		String valueStr = getChildValue(parentNode, childName);
292 		return Util.parseDouble(valueStr);
293 	}
294 
295 	protected double getValueAsDouble(Node parentNode) {
296 		String valueStr = getValue(parentNode);
297 		return Util.parseDouble(valueStr);
298 	}
299 
300 	protected boolean getChildValueAsBoolean(Node parentNode, String childName) throws RrdException {
301 		String valueStr = getChildValue(parentNode, childName);
302 		return Util.parseBoolean(valueStr);
303 	}
304 
305 	protected boolean getValueAsBoolean(Node parentNode) {
306 		String valueStr = getValue(parentNode);
307 		return Util.parseBoolean(valueStr);
308 	}
309 
310 	protected Paint getValueAsColor(Node parentNode) throws RrdException {
311 		String rgbStr = getValue(parentNode);
312 		return Util.parseColor(rgbStr);
313 	}
314 
315 	protected boolean isEmptyNode(Node node) {
316 		// comment node or empty text node
317 		return node.getNodeName().equals("#comment") ||
318 				(node.getNodeName().equals("#text") && node.getNodeValue().trim().length() == 0);
319 	}
320 
321 	protected void validateTagsOnlyOnce(Node parentNode, String[] allowedChildNames) throws RrdException {
322 		// validate node only once
323 		if (validatedNodes.contains(parentNode)) {
324 			return;
325 		}
326 		Node[] childs = getChildNodes(parentNode);
327 		main:
328 		for (Node child : childs) {
329 			String childName = child.getNodeName();
330 			for (int j = 0; j < allowedChildNames.length; j++) {
331 				if (allowedChildNames[j].equals(childName)) {
332 					// only one such tag is allowed
333 					allowedChildNames[j] = "<--removed-->";
334 					continue main;
335 				}
336 				else if (allowedChildNames[j].equals(childName + "*")) {
337 					// several tags allowed
338 					continue main;
339 				}
340 			}
341 			if (!isEmptyNode(child)) {
342 				throw new RrdException("Unexpected tag encountered: <" + childName + ">");
343 			}
344 		}
345 		// everything is OK
346 		validatedNodes.add(parentNode);
347 	}
348 }