View Javadoc

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 }