001 /* ============================================================
002 * JRobin : Pure java implementation of RRDTool's functionality
003 * ============================================================
004 *
005 * Project Info: http://www.jrobin.org
006 * Project Lead: Sasa Markovic (saxon@jrobin.org);
007 *
008 * (C) Copyright 2003-2005, by Sasa Markovic.
009 *
010 * Developers: Sasa Markovic (saxon@jrobin.org)
011 *
012 *
013 * This library is free software; you can redistribute it and/or modify it under the terms
014 * of the GNU Lesser General Public License as published by the Free Software Foundation;
015 * either version 2.1 of the License, or (at your option) any later version.
016 *
017 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
018 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
019 * See the GNU Lesser General Public License for more details.
020 *
021 * You should have received a copy of the GNU Lesser General Public License along with this
022 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
023 * Boston, MA 02111-1307, USA.
024 */
025
026 package org.jrobin.core;
027
028 import org.w3c.dom.Element;
029 import org.w3c.dom.Node;
030 import org.xml.sax.InputSource;
031
032 import java.awt.*;
033 import java.io.File;
034 import java.io.IOException;
035 import java.util.*;
036 import java.util.regex.Matcher;
037 import java.util.regex.Pattern;
038
039 /**
040 * Class used as a base class for various XML template related classes. Class provides
041 * methods for XML source parsing and XML tree traversing. XML source may have unlimited
042 * number of placeholders (variables) in the format <code>${variable_name}</code>.
043 * Methods are provided to specify variable values at runtime.
044 * Note that this class has limited functionality: XML source gets parsed, and variable
045 * values are collected. You have to extend this class to do something more useful.<p>
046 */
047 public abstract class XmlTemplate {
048 private static final String PATTERN_STRING = "\\$\\{(\\w+)\\}";
049 private static final Pattern PATTERN = Pattern.compile(PATTERN_STRING);
050
051 protected Element root;
052 private HashMap<String, Object> valueMap = new HashMap<String, Object>();
053 private HashSet<Node> validatedNodes = new HashSet<Node>();
054
055 protected XmlTemplate(InputSource xmlSource) throws IOException, RrdException {
056 root = Util.Xml.getRootElement(xmlSource);
057 }
058
059 protected XmlTemplate(String xmlString) throws IOException, RrdException {
060 root = Util.Xml.getRootElement(xmlString);
061 }
062
063 protected XmlTemplate(File xmlFile) throws IOException, RrdException {
064 root = Util.Xml.getRootElement(xmlFile);
065 }
066
067 /**
068 * Removes all placeholder-value mappings.
069 */
070 public void clearValues() {
071 valueMap.clear();
072 }
073
074 /**
075 * Sets value for a single XML template variable. Variable name should be specified
076 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
077 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
078 *
079 * @param name variable name
080 * @param value value to be set in the XML template
081 */
082 public void setVariable(String name, String value) {
083 valueMap.put(name, value);
084 }
085
086 /**
087 * Sets value for a single XML template variable. Variable name should be specified
088 * without leading '${' and ending '}' placeholder markers. For example, for a placeholder
089 * <code>${start}</code>, specify <code>start</start> for the <code>name</code> parameter.
090 *
091 * @param name variable name
092 * @param value value to be set in the XML template
093 */
094 public void setVariable(String name, int value) {
095 valueMap.put(name, value);
096 }
097
098 /**
099 * 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 }