1 /*
2 Copyright (C) 2000 - 2007 Grid Systems, S.A.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 /*
19 * Project: KernelConfigurator
20 * Created on 02-mar-2004
21 *
22 * Copyright (c)2003 Grid Systems
23 */
24 package com.gridsystems.config.tools;
25
26 import java.io.BufferedInputStream;
27 import java.io.BufferedReader;
28 import java.io.File;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStreamReader;
32 import java.io.PrintWriter;
33 import java.lang.reflect.Method;
34 import java.net.MalformedURLException;
35 import java.net.URL;
36 import java.util.Properties;
37
38 /**
39 * Simple template implementation.
40 * <p>
41 * Instances of this class use a template file as its source, with an optional
42 * default template located in the classpath.
43 *
44 * @author <a href="mailto:rruiz@gridsystems.com">Rodrigo Ruiz Aguayo</a>
45 * @version 1.0
46 */
47 public class Template {
48
49 /**
50 * Default disk cluster size in bytes.
51 */
52 private static final int CLUSTER_SIZE = 4096;
53
54 /**
55 * URL of the template source.
56 */
57 private URL src;
58
59 /**
60 * Creates a template using the specified file as the source. It will search for
61 * a default template with the same name as the file.
62 *
63 * @param f The source template file
64 * @throws IOException In case of error handling resources
65 */
66 public Template(File f) throws IOException {
67 this(f, f.getName());
68 }
69
70 /**
71 * Creates a template using the specified file as the source.
72 *
73 * @param f The file of the template source
74 * @param resourceName The default template source as a resource name
75 * @throws IOException In case of error handling resources
76 */
77 public Template(File f, String resourceName) throws IOException {
78 this(f, Thread.currentThread().getContextClassLoader().getResource(resourceName));
79 }
80
81 /**
82 * Create a template using the specified file and URL as the template source, and
83 * its default source respectively.
84 * <p>
85 * If the file is not found, it will be created as a copy of the specified default.
86 * This way, anyone can modify the template, and turning back to the default will
87 * be as simple as deleting the file.
88 *
89 * @param f The template source file
90 * @param url The URL of the default template source
91 * @throws IOException In case of error handling resources
92 */
93 public Template(File f, URL url) throws IOException {
94
95 if (f.exists() && f.isFile()) {
96 this.src = f.toURI().toURL();
97 }
98
99 if (src == null && url != null) {
100 copyResource(url, f);
101 this.src = f.toURI().toURL();
102 }
103
104 if (src == null) {
105 throw new MalformedURLException("Could not construct a valid URL from " + f);
106 }
107 }
108
109 /**
110 * Copies the default template into the file.
111 *
112 * @param resource The URL of the default template
113 * @param dest The destination file to copy in
114 * @throws IOException In case of I/O error
115 */
116 private void copyResource(URL resource, File dest) throws IOException {
117 if (dest.isDirectory()) {
118 throw new IOException("Could not copy resource into a directory");
119 }
120
121 FileOutputStream fos = null;
122 BufferedInputStream is = null;
123 try {
124 fos = new FileOutputStream(dest);
125 is = new BufferedInputStream(resource.openStream());
126 byte[] buf = new byte[CLUSTER_SIZE];
127 int count = is.read(buf);
128 while (count != -1) {
129 fos.write(buf, 0, count);
130 count = is.read(buf);
131 }
132 } finally {
133 close(fos);
134 close(is);
135 }
136 }
137
138 /**
139 * Writes the template to the specified file. The props argument contains
140 * the list of replacements to apply to the template
141 *
142 * @param props The replacements to perform
143 * @param f The destination file
144 * @throws IOException In case of I/O error
145 */
146 public void writeTo(Properties props, File f) throws IOException {
147 if (f == null) {
148 throw new NullPointerException("Null file");
149 }
150
151 PrintWriter writer = null;
152 try {
153 writer = new PrintWriter(new FileOutputStream(f));
154 printTo(props, writer);
155 } finally {
156 close(writer);
157 }
158 }
159
160 /**
161 * Writes the tamplate into the specified writer, replacing all found
162 * keys in the template text with the values in the props argument.
163 * <p>
164 * A key in the template has the following syntax: <code>${key}</code>
165 *
166 * @param props The replacement list
167 * @param out The writer
168 * @throws IOException In case of I/O error
169 */
170 public void printTo(Properties props, PrintWriter out) throws IOException {
171 BufferedReader bis = null;
172 try {
173 bis = new BufferedReader(new InputStreamReader(src.openStream()));
174 String line = bis.readLine();
175 while (line != null) {
176 line = replace(line, props);
177 out.println(line);
178 line = bis.readLine();
179 }
180 } finally {
181 close(bis);
182 }
183 }
184
185 /**
186 * Replaces all occurrences of ${key} in the string with the value assigned to
187 * key in props. If no value is mapped for key, it is left as is.
188 *
189 * @param s The string to parse
190 * @param props The replacement list
191 * @return The parsed string with all keys replaced
192 */
193 private String replace(String s, Properties props) {
194 int pos1 = s.indexOf("${");
195 while (pos1 != -1) {
196 int pos2 = s.indexOf("}", pos1 + 2);
197 if (pos2 != -1) {
198 String name = s.substring(pos1 + 2, pos2);
199 String value = props.getProperty(name);
200 if (value != null) {
201 s = s.substring(0, pos1) + value + s.substring(pos2 + 1);
202 }
203 }
204 pos1 = s.indexOf("${", pos1 + 1);
205 }
206 return s;
207 }
208
209 /**
210 * Closes obj if it has a close method.
211 *
212 * @param obj The object to close
213 */
214 private static void close(Object obj) {
215 try {
216 Method m = obj.getClass().getMethod("close");
217 m.invoke(obj);
218 } catch (Exception e) { }
219 }
220 }