001: package org.apache.velocity.texen;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.File;
023: import java.io.InputStream;
024: import java.io.FileInputStream;
025: import java.io.BufferedInputStream;
026: import java.io.Writer;
027: import java.io.FileWriter;
028: import java.io.IOException;
029: import java.io.StringWriter;
030: import java.io.OutputStreamWriter;
031: import java.io.BufferedWriter;
032: import java.io.FileOutputStream;
033:
034: import java.util.Enumeration;
035: import java.util.Hashtable;
036: import java.util.Iterator;
037: import java.util.Properties;
038:
039: import org.apache.velocity.Template;
040: import org.apache.velocity.context.Context;
041: import org.apache.velocity.VelocityContext;
042: import org.apache.velocity.app.VelocityEngine;
043: import org.apache.velocity.util.ClassUtils;
044:
045: /**
046: * A text/code generator class
047: *
048: * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
049: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
050: * @version $Id: Generator.java 463298 2006-10-12 16:10:32Z henning $
051: */
052: public class Generator {
053: /**
054: * Where the texen output will placed.
055: */
056: public static final String OUTPUT_PATH = "output.path";
057:
058: /**
059: * Where the velocity templates live.
060: */
061: public static final String TEMPLATE_PATH = "template.path";
062:
063: /**
064: * Default properties file used for controlling the
065: * tools placed in the context.
066: */
067: private static final String DEFAULT_TEXEN_PROPERTIES = "org/apache/velocity/texen/defaults/texen.properties";
068:
069: /**
070: * Default properties used by texen.
071: */
072: private Properties props = new Properties();
073:
074: /**
075: * Context used for generating the texen output.
076: */
077: private Context controlContext;
078:
079: /**
080: * Keep track of the file writers used for outputting
081: * to files. If we come across a file writer more
082: * then once then the additional output will be
083: * appended to the file instead of overwritting
084: * the contents.
085: */
086: private Hashtable writers = new Hashtable();
087:
088: /**
089: * The generator tools used for creating additional
090: * output withing the control template. This could
091: * use some cleaning up.
092: */
093: private static Generator instance = new Generator();
094:
095: /**
096: * This is the encoding for the output file(s).
097: */
098: protected String outputEncoding;
099:
100: /**
101: * This is the encoding for the input file(s)
102: * (templates).
103: */
104: protected String inputEncoding;
105:
106: /**
107: * Velocity engine.
108: */
109: protected VelocityEngine ve;
110:
111: /**
112: * Default constructor.
113: */
114: private Generator() {
115: setDefaultProps();
116: }
117:
118: /**
119: * Create a new generator object with default properties.
120: *
121: * @return Generator generator used in the control context.
122: */
123: public static Generator getInstance() {
124: return instance;
125: }
126:
127: /**
128: * Set the velocity engine.
129: * @param ve
130: */
131: public void setVelocityEngine(VelocityEngine ve) {
132: this .ve = ve;
133: }
134:
135: /**
136: * Create a new generator object with properties loaded from
137: * a file. If the file does not exist or any other exception
138: * occurs during the reading operation the default properties
139: * are used.
140: *
141: * @param propFile properties used to help populate the control context.
142: */
143: public Generator(String propFile) {
144: try {
145: BufferedInputStream bi = null;
146: try {
147: bi = new BufferedInputStream(new FileInputStream(
148: propFile));
149: props.load(bi);
150: } finally {
151: if (bi != null) {
152: bi.close();
153: }
154: }
155: } catch (IOException e) {
156: System.err.println("Could not load " + propFile
157: + ", falling back to defaults. (" + e.getMessage()
158: + ")");
159: /*
160: * If something goes wrong we use default properties
161: */
162: setDefaultProps();
163: }
164: }
165:
166: /**
167: * Create a new Generator object with a given property
168: * set. The property set will be duplicated.
169: *
170: * @param props properties object to help populate the control context.
171: */
172: public Generator(Properties props) {
173: this .props = (Properties) props.clone();
174: }
175:
176: /**
177: * Set default properties.
178: */
179: protected void setDefaultProps() {
180: ClassLoader classLoader = VelocityEngine.class.getClassLoader();
181: try {
182: InputStream inputStream = null;
183: try {
184: inputStream = classLoader
185: .getResourceAsStream(DEFAULT_TEXEN_PROPERTIES);
186:
187: props.load(inputStream);
188: } finally {
189: if (inputStream != null) {
190: inputStream.close();
191: }
192: }
193: } catch (IOException ioe) {
194: System.err.println("Cannot get default properties: "
195: + ioe.getMessage());
196: }
197: }
198:
199: /**
200: * Set the template path, where Texen will look
201: * for Velocity templates.
202: *
203: * @param templatePath template path for velocity templates.
204: */
205: public void setTemplatePath(String templatePath) {
206: props.put(TEMPLATE_PATH, templatePath);
207: }
208:
209: /**
210: * Get the template path.
211: *
212: * @return String template path for velocity templates.
213: */
214: public String getTemplatePath() {
215: return props.getProperty(TEMPLATE_PATH);
216: }
217:
218: /**
219: * Set the output path for the generated
220: * output.
221: * @param outputPath
222: */
223: public void setOutputPath(String outputPath) {
224: props.put(OUTPUT_PATH, outputPath);
225: }
226:
227: /**
228: * Get the output path for the generated
229: * output.
230: *
231: * @return String output path for texen output.
232: */
233: public String getOutputPath() {
234: return props.getProperty(OUTPUT_PATH);
235: }
236:
237: /**
238: * Set the output encoding.
239: * @param outputEncoding
240: */
241: public void setOutputEncoding(String outputEncoding) {
242: this .outputEncoding = outputEncoding;
243: }
244:
245: /**
246: * Set the input (template) encoding.
247: * @param inputEncoding
248: */
249: public void setInputEncoding(String inputEncoding) {
250: this .inputEncoding = inputEncoding;
251: }
252:
253: /**
254: * Returns a writer, based on encoding and path.
255: *
256: * @param path path to the output file
257: * @param encoding output encoding
258: * @return A Writer for this generator.
259: * @throws Exception
260: */
261: public Writer getWriter(String path, String encoding)
262: throws Exception {
263: Writer writer;
264: if (encoding == null || encoding.length() == 0
265: || encoding.equals("8859-1")
266: || encoding.equals("8859_1")) {
267: writer = new FileWriter(path);
268: } else {
269: writer = new BufferedWriter(new OutputStreamWriter(
270: new FileOutputStream(path), encoding));
271: }
272: return writer;
273: }
274:
275: /**
276: * Returns a template, based on encoding and path.
277: *
278: * @param templateName name of the template
279: * @param encoding template encoding
280: * @return A Template.
281: * @throws Exception
282: */
283: public Template getTemplate(String templateName, String encoding)
284: throws Exception {
285: Template template;
286: if (encoding == null || encoding.length() == 0
287: || encoding.equals("8859-1")
288: || encoding.equals("8859_1")) {
289: template = ve.getTemplate(templateName);
290: } else {
291: template = ve.getTemplate(templateName, encoding);
292: }
293: return template;
294: }
295:
296: /**
297: * Parse an input and write the output to an output file. If the
298: * output file parameter is null or an empty string the result is
299: * returned as a string object. Otherwise an empty string is returned.
300: *
301: * @param inputTemplate input template
302: * @param outputFile output file
303: * @return The parsed file.
304: * @throws Exception
305: */
306: public String parse(String inputTemplate, String outputFile)
307: throws Exception {
308: return parse(inputTemplate, outputFile, null, null);
309: }
310:
311: /**
312: * Parse an input and write the output to an output file. If the
313: * output file parameter is null or an empty string the result is
314: * returned as a string object. Otherwise an empty string is returned.
315: * You can add objects to the context with the objs Hashtable.
316: *
317: * @param inputTemplate input template
318: * @param outputFile output file
319: * @param objectID id for object to be placed in the control context
320: * @param object object to be placed in the context
321: * @return String generated output from velocity
322: * @throws Exception
323: */
324: public String parse(String inputTemplate, String outputFile,
325: String objectID, Object object) throws Exception {
326: return parse(inputTemplate, null, outputFile, null, objectID,
327: object);
328: }
329:
330: /**
331: * Parse an input and write the output to an output file. If the
332: * output file parameter is null or an empty string the result is
333: * returned as a string object. Otherwise an empty string is returned.
334: * You can add objects to the context with the objs Hashtable.
335: *
336: * @param inputTemplate input template
337: * @param inputEncoding template encoding
338: * @param outputFile output file
339: * @param outputEncoding outputEncoding encoding of output file
340: * @param objectID id for object to be placed in the control context
341: * @param object object to be placed in the context
342: * @return String generated output from velocity
343: * @throws Exception
344: */
345: public String parse(String inputTemplate, String inputEncoding,
346: String outputFile, String outputEncoding, String objectID,
347: Object object) throws Exception {
348: if (objectID != null && object != null) {
349: controlContext.put(objectID, object);
350: }
351:
352: Template template = getTemplate(inputTemplate,
353: inputEncoding != null ? inputEncoding
354: : this .inputEncoding);
355:
356: if (outputFile == null || outputFile.equals("")) {
357: StringWriter sw = new StringWriter();
358: template.merge(controlContext, sw);
359: return sw.toString();
360: } else {
361: Writer writer = null;
362:
363: if (writers.get(outputFile) == null) {
364: /*
365: * We have never seen this file before so create
366: * a new file writer for it.
367: */
368: writer = getWriter(getOutputPath() + File.separator
369: + outputFile,
370: outputEncoding != null ? outputEncoding
371: : this .outputEncoding);
372:
373: /*
374: * Place the file writer in our collection
375: * of file writers.
376: */
377: writers.put(outputFile, writer);
378: } else {
379: writer = (Writer) writers.get(outputFile);
380: }
381:
382: VelocityContext vc = new VelocityContext(controlContext);
383: template.merge(vc, writer);
384:
385: // commented because it is closed in shutdown();
386: //fw.close();
387:
388: return "";
389: }
390: }
391:
392: /**
393: * Parse the control template and merge it with the control
394: * context. This is the starting point in texen.
395: *
396: * @param controlTemplate control template
397: * @param controlContext control context
398: * @return String generated output
399: * @throws Exception
400: */
401: public String parse(String controlTemplate, Context controlContext)
402: throws Exception {
403: this .controlContext = controlContext;
404: fillContextDefaults(this .controlContext);
405: fillContextProperties(this .controlContext);
406:
407: Template template = getTemplate(controlTemplate, inputEncoding);
408: StringWriter sw = new StringWriter();
409: template.merge(controlContext, sw);
410:
411: return sw.toString();
412: }
413:
414: /**
415: * Create a new context and fill it with the elements of the
416: * objs Hashtable. Default objects and objects that comes from
417: * the properties of this Generator object is also added.
418: *
419: * @param objs objects to place in the control context
420: * @return Context context filled with objects
421: */
422: protected Context getContext(Hashtable objs) {
423: fillContextHash(controlContext, objs);
424: return controlContext;
425: }
426:
427: /**
428: * Add all the contents of a Hashtable to the context.
429: *
430: * @param context context to fill with objects
431: * @param objs source of objects
432: */
433: protected void fillContextHash(Context context, Hashtable objs) {
434: Enumeration enumeration = objs.keys();
435: while (enumeration.hasMoreElements()) {
436: String key = enumeration.nextElement().toString();
437: context.put(key, objs.get(key));
438: }
439: }
440:
441: /**
442: * Add properties that will aways be in the context by default
443: *
444: * @param context control context to fill with default values.
445: */
446: protected void fillContextDefaults(Context context) {
447: context.put("generator", instance);
448: context.put("outputDirectory", getOutputPath());
449: }
450:
451: /**
452: * Add objects to the context from the current properties.
453: *
454: * @param context control context to fill with objects
455: * that are specified in the default.properties
456: * file
457: */
458: protected void fillContextProperties(Context context) {
459: Enumeration enumeration = props.propertyNames();
460:
461: while (enumeration.hasMoreElements()) {
462: String nm = (String) enumeration.nextElement();
463: if (nm.startsWith("context.objects.")) {
464:
465: String contextObj = props.getProperty(nm);
466: int colon = nm.lastIndexOf('.');
467: String contextName = nm.substring(colon + 1);
468:
469: try {
470: Object o = ClassUtils.getNewInstance(contextObj);
471: context.put(contextName, o);
472: } catch (Exception e) {
473: e.printStackTrace();
474: //TO DO: Log Something Here
475: }
476: }
477: }
478: }
479:
480: /**
481: * Properly shut down the generator, right now
482: * this is simply flushing and closing the file
483: * writers that we have been holding on to.
484: */
485: public void shutdown() {
486: Iterator iterator = writers.values().iterator();
487:
488: while (iterator.hasNext()) {
489: Writer writer = (Writer) iterator.next();
490:
491: try {
492: writer.flush();
493: } catch (IOException e) {
494: /* do nothing */
495: }
496:
497: try {
498: writer.close();
499: } catch (IOException e) {
500: /* do nothing */
501: }
502: }
503: // clear the file writers cache
504: writers.clear();
505: }
506: }
|