001: package org.apache.dvsl;
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.FileInputStream;
024: import java.io.FileReader;
025: import java.io.FileWriter;
026: import java.io.InputStream;
027: import java.io.InputStreamReader;
028: import java.io.OutputStreamWriter;
029: import java.io.Reader;
030: import java.io.StringWriter;
031: import java.io.Writer;
032:
033: import java.util.Enumeration;
034: import java.util.HashMap;
035: import java.util.Map;
036: import java.util.Properties;
037:
038: import org.apache.velocity.VelocityContext;
039: import org.apache.velocity.app.VelocityEngine;
040: import org.apache.velocity.context.Context;
041: import org.apache.velocity.runtime.log.LogChute;
042: import org.apache.velocity.runtime.log.LogChuteSystem;
043: import org.apache.velocity.runtime.log.LogSystem;
044:
045: import org.dom4j.Document;
046: import org.dom4j.io.SAXReader;
047:
048: /**
049: * Main DVSL class - use this as the helper class for apps
050: *
051: * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
052: * @author <a href="mailto:billb@progress.com">Bill Burton.</a>
053: */
054: public class DVSL {
055: private static String TOOL_PROP_PREFIX = "toolbox.tool.";
056: private static String STRING_PROP_PREFIX = "toolbox.string.";
057: private static String INTEGER_PROP_PREFIX = "toolbox.integer.";
058: private static String TOOLBOX_NAME = "toolbox.contextname.";
059:
060: private VelocityEngine ve = null;
061: private Document currentDocument = null;
062: private Writer currentWriter = null;
063: private Context toolContext;
064: private Context userContext;
065: private Context styleContext;
066: private DVSLContext baseContext = new DVSLContext();
067: private Transformer transformer;
068: private ClassLoader classLoader;
069: private boolean ready = false;
070:
071: private Map velConfig = null;
072: private File logFile;
073: private LogChute logger;
074:
075: private Map appVals = new HashMap();
076:
077: private TemplateHandler templateHandler = new TemplateHandler();
078:
079: boolean validate = false;
080:
081: public DVSL() {
082: classLoader = DVSL.class.getClassLoader();
083: }
084:
085: /**
086: * <p>
087: * lets the user specify a filename for logging.
088: * </p>
089: */
090: public void setLogFile(File logFile) {
091: this .logFile = logFile;
092:
093: if (velConfig == null) {
094: velConfig = new HashMap();
095: }
096:
097: velConfig.put(VelocityEngine.RUNTIME_LOG, logFile
098: .getAbsolutePath());
099: }
100:
101: /**
102: * <p>
103: * lets the user specify a class instance for logging.
104: * </p>
105: * @deprecated use setLogChute instead
106: */
107: public void setLogSystem(LogSystem logger) {
108: this .logger = new LogAdapter(logger);
109:
110: if (velConfig == null) {
111: velConfig = new HashMap();
112: }
113:
114: velConfig.put(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, logger);
115: }
116:
117: /**
118: * <p>
119: * lets the user specify a class instance for logging.
120: * </p>
121: */
122: public void setLogChute(LogChute logger) {
123: this .logger = logger;
124:
125: if (velConfig == null) {
126: velConfig = new HashMap();
127: }
128:
129: velConfig.put(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, logger);
130: }
131:
132: /**
133: * <p>
134: * lets the user pass a java.util.Properties containing
135: * properties for the configuration of the VelocityEngine
136: * used by DVSL
137: * </p>
138: */
139: public void setVelocityConfig(Map map) {
140: if (velConfig != null) {
141: map.putAll(velConfig);
142: }
143:
144: velConfig = map;
145: }
146:
147: /**
148: * <p>
149: * Sets the user context. The user context is
150: * a Velocity Context containing user-supplied
151: * objects and data that are to be made available
152: * in the template
153: * </p>
154: *
155: * @param ctx User context of data
156: */
157: public void setUserContext(Context ctx) {
158: ready = false;
159: userContext = ctx;
160: }
161:
162: /**
163: * <p>
164: * Uses a validating parser on all input documents
165: * </p>
166: *
167: * @param validate
168: */
169:
170: public void setValidatingParser(boolean validate) {
171: this .validate = validate;
172: }
173:
174: /**
175: * <p>
176: * Specify a classloader for loading the Toolbox classes. Setting to null
177: * resets to the default ClassLoader.
178: * </p>
179: *
180: * @param classLoader ClassLoader or null for default ClassLoader
181: */
182: public void setClassLoader(ClassLoader classLoader) {
183: if (classLoader == null) {
184: this .classLoader = this .getClass().getClassLoader();
185: } else {
186: this .classLoader = classLoader;
187: }
188: }
189:
190: /**
191: * <p>
192: * Loads the toolbox from the input Properties.
193: * </p>
194: *
195: * <p>
196: * Currently supports specification of the Toolbox
197: * name in the context, creating classes, and string
198: * and integer values. Ex :
199: * </p>
200: *
201: * <pre>
202: * toolbox.contextname = floyd
203: * toolbox.tool.footool = Footool
204: * toolbox.string.mystring = Hello there!
205: * toolbox.integer.myint = 7
206: * toolbox.string.sourcebase = ./xdocs/
207: * </pre>
208: *
209: * <p>
210: * So in template, this toolbox and it's values would
211: * be accessed as :
212: * </p>
213: * <pre>
214: * $context.floyd.footool.getFoo()
215: * $context.floyd.mystring
216: * $context.floyd.myint
217: * </pre>
218: */
219: public void setToolbox(Properties p) throws ClassNotFoundException,
220: InstantiationException, IllegalAccessException {
221: ready = false;
222:
223: /*
224: * for each key that looks like
225: * toolbox.tool.<token> = class
226: */
227:
228: Map toolbox = new HashMap();
229:
230: String toolboxname = "toolbox";
231:
232: for (Enumeration e = p.propertyNames(); e.hasMoreElements();) {
233: String key = (String) e.nextElement();
234:
235: String value = p.getProperty(key);
236:
237: if (key.startsWith(TOOL_PROP_PREFIX)) {
238: String toolname = key.substring(TOOL_PROP_PREFIX
239: .length());
240:
241: Object o = Class.forName(value, true, classLoader)
242: .newInstance();
243:
244: toolbox.put(toolname, o);
245: } else if (key.startsWith(INTEGER_PROP_PREFIX)) {
246: String toolname = key.substring(INTEGER_PROP_PREFIX
247: .length());
248:
249: int i = 0;
250:
251: try {
252: i = Integer.parseInt(value);
253: } catch (Exception ee) {
254: }
255:
256: toolbox.put(toolname, new Integer(i));
257: } else if (key.startsWith(STRING_PROP_PREFIX)) {
258: String toolname = key.substring(STRING_PROP_PREFIX
259: .length());
260: toolbox.put(toolname, value);
261: } else if (key.startsWith(TOOLBOX_NAME)) {
262: toolboxname = value;
263: }
264: }
265:
266: toolContext = new VelocityContext();
267:
268: toolContext.put(toolboxname, toolbox);
269: }
270:
271: /**
272: * Convenience function. See...
273: */
274: public void setStylesheet(String stylesheet) throws Exception {
275: setStylesheet(new File(stylesheet), null);
276: }
277:
278: /**
279: * Convenience function. See...
280: */
281: public void setStylesheet(File stylesheet) throws Exception {
282: setStylesheet(stylesheet, null);
283: }
284:
285: /**
286: * Convenience function. See...
287: */
288: public void setStylesheet(File stylesheet, String stylesheetEncoding)
289: throws Exception {
290: Reader fr = null;
291:
292: try {
293: if (stylesheetEncoding != null) {
294: fr = new InputStreamReader(new FileInputStream(
295: stylesheet), stylesheetEncoding);
296: } else {
297: fr = new FileReader(stylesheet);
298: }
299:
300: setStylesheet(fr);
301: } finally {
302: if (fr != null)
303: fr.close();
304: }
305: }
306:
307: /**
308: * <p>
309: * Sets the stylesheet for this transformation set
310: * </p>
311: *
312: * <p>
313: * Note that don't need this for each document you want
314: * to transform. Just do it once, and transform away...
315: * </p>
316: *
317: * @param styleReader Reader with stylesheet char stream
318: */
319: public void setStylesheet(Reader styleReader) throws Exception {
320: ready = false;
321: /*
322: * now initialize Velocity - we need to do that
323: * on change of stylesheet
324: */
325:
326: ve = new VelocityEngine();
327:
328: /*
329: * if there are user properties, set those first - carefully
330: */
331:
332: if (velConfig != null) {
333: configureVelocityEngine(ve, velConfig);
334: }
335:
336: /*
337: * register our template() directive
338: */
339:
340: ve.setProperty("userdirective",
341: "org.apache.dvsl.directive.MatchDirective");
342: ve.init();
343:
344: /*
345: * add our template accumulator
346: */
347:
348: ve.setApplicationAttribute("org.apache.dvsl.TemplateHandler",
349: templateHandler);
350:
351: /*
352: * load and render the stylesheet
353: *
354: * this sets stylesheet specific context
355: * values
356: */
357:
358: StringWriter junkWriter = new StringWriter();
359:
360: styleContext = new VelocityContext();
361: ve.evaluate(styleContext, junkWriter, "DVSL:stylesheet",
362: styleReader);
363:
364: /*
365: * now run the base template through for the rules
366: */
367:
368: InputStream is = this .getClass().getClassLoader()
369: .getResourceAsStream(
370: "org/apache/dvsl/resource/defaultroot.dvsl");
371:
372: if (is == null) {
373: System.out.println("DEFAULT TRANSFORM RULES NOT FOUND ");
374: } else {
375: ve.evaluate(new VelocityContext(), junkWriter,
376: "defaultroot.dvsl", new InputStreamReader(is));
377: is.close();
378: }
379:
380: /*
381: * need a new transformer, as it depends on the
382: * velocity engine
383: */
384:
385: transformer = new Transformer(ve, templateHandler, baseContext,
386: appVals, validate);
387: }
388:
389: /**
390: * <p>
391: * Adds the allowed properties from the Properties to the
392: * velocity engine
393: * </p>
394: * <p>
395: * So far, we support, in RuntimeConstant parlance :
396: * </p>
397: * <ul>
398: * <li>VM_LIBRARY</li>
399: * <li>FILE_RESOURCE_LOADER_PATH</li>
400: * <li>RUNTIME_LOG</li>
401: * <li>RUNTIME_LOG_LOGSYSTEM</li>
402: * <li>RUNTIME_LOG_LOGSYSTEM_CLASS</li>
403: * </ul>
404: *
405: * <p>
406: * If you are going to use this, ensure you do it *before* setting
407: * the stylesheet, as that creates the VelocityEngine
408: * </p>
409: */
410: private void configureVelocityEngine(VelocityEngine ve, Map map) {
411: if (ve == null || map == null) {
412: return;
413: }
414:
415: /*
416: * for now, keep it simple
417: */
418:
419: Object val = map.get(VelocityEngine.VM_LIBRARY);
420:
421: if (val instanceof String) {
422: ve.setProperty(VelocityEngine.VM_LIBRARY, (String) val);
423: }
424:
425: /*
426: * assumes that for now, we are using the file loader
427: */
428: val = map.get(VelocityEngine.FILE_RESOURCE_LOADER_PATH);
429:
430: if (val instanceof String) {
431: ve.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH,
432: (String) val);
433: }
434:
435: /*
436: * No real assumptions - pass what you want
437: */
438: val = map.get(VelocityEngine.RUNTIME_LOG);
439:
440: if (val instanceof String) {
441: ve.setProperty(VelocityEngine.RUNTIME_LOG, val);
442: }
443:
444: val = map.get(VelocityEngine.RUNTIME_LOG_LOGSYSTEM);
445:
446: if (val != null) {
447: ve.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, val);
448: }
449:
450: val = map.get(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS);
451:
452: if (val instanceof String) {
453: ve.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS,
454: val);
455: }
456:
457: }
458:
459: /**
460: * sets up all the context goodies
461: */
462: protected void makeReady() {
463: /*
464: * put all the contexts together
465: */
466:
467: baseContext.clearContexts();
468:
469: baseContext.addContext(userContext);
470: baseContext.addContext(toolContext);
471: baseContext.setStyleContext(styleContext);
472:
473: ready = true;
474: }
475:
476: /**
477: * does the transformation of the inputstream into
478: * the output writer
479: */
480: protected long xform(Reader reader, Writer writer) throws Exception {
481: if (!ready)
482: makeReady();
483:
484: return transformer.transform(reader, writer);
485: }
486:
487: protected long xform(Document dom4jdoc, Writer writer)
488: throws Exception {
489: if (!ready)
490: makeReady();
491:
492: return transformer.transform(dom4jdoc, writer);
493: }
494:
495: public long transform(File f, Writer writer) throws Exception {
496: InputStream is = null;
497: try {
498: is = new FileInputStream(f);
499: return transform(is, writer);
500: } finally {
501: if (is != null) {
502: is.close();
503: }
504: }
505: }
506:
507: public long transform(Reader reader, Writer writer)
508: throws Exception {
509: return xform(reader, writer);
510: }
511:
512: public long transform(InputStream is, Writer writer)
513: throws Exception {
514: SAXReader reader = new SAXReader();
515: return xform(reader.read(is), writer);
516: }
517:
518: /**
519: * Transforms the given dom4j Document into the writer.
520: *
521: * @param dom4jdoc dom4j Document object
522: * @param writer Writer for output
523: */
524: public long transform(Document dom4jdoc, Writer writer)
525: throws Exception {
526: return xform(dom4jdoc, writer);
527: }
528:
529: public long transform(String infile, Writer writer)
530: throws Exception {
531: return transform(new File(infile), writer);
532: }
533:
534: /**
535: * Gets the application value for the specified key
536: *
537: * @param key key to use to retrieve value
538: * @return value if found, null otherwise
539: */
540: public Object getAppValue(Object key) {
541: return appVals.get(key);
542: }
543:
544: /**
545: * Sets the application value for the specified key
546: *
547: * @param key key to use to store value
548: * @param value value to be stored
549: * @return old value if any, null otherwise
550: */
551: public Object putAppValue(Object key, Object value) {
552: return appVals.put(key, value);
553: }
554:
555: /**
556: * <p>
557: * Allows command-line access.
558: * </p>
559: * <p>
560: * Usage : java -jar dvsl.jar -STYLE stylesheeet [-IN infile] [-OUT outfile] [-TOOL toolboxname]
561: * </p>
562: */
563: public static void main(String[] args) throws Exception {
564:
565: DVSL dvsl = new DVSL();
566:
567: Reader in = new InputStreamReader(System.in);
568: String infile = null;
569: String style = null;
570: String outfile = null;
571:
572: Writer out = new OutputStreamWriter(System.out);
573: String toolfile = null;
574:
575: for (int i = 0; i < args.length; i++) {
576: if (args[i].equals("-IN"))
577: infile = args[++i];
578: else if (args[i].equals("-OUT"))
579: outfile = args[++i];
580: else if (args[i].equals("-STYLE"))
581: style = args[++i];
582: else if (args[i].equals("-TOOL"))
583: toolfile = args[++i];
584: }
585:
586: if (style == null) {
587: System.out.println("usage :need to specify a stylesheet. ");
588: System.out
589: .println("java -jar dvsl.jar -STYLE stylesheeet [-IN infile] [-OUT outfile] [-TOOL toolboxname]");
590: return;
591: }
592:
593: if (style != null)
594: dvsl.setStylesheet(style);
595:
596: if (toolfile != null) {
597: Properties p = new Properties();
598:
599: InputStream fis = new FileInputStream(toolfile);
600:
601: p.load(fis);
602:
603: dvsl.setToolbox(p);
604: }
605:
606: if (infile != null)
607: in = new FileReader(infile);
608:
609: if (outfile != null)
610: out = new FileWriter(outfile);
611:
612: long time = dvsl.transform(in, out);
613:
614: out.flush();
615:
616: }
617:
618: /*
619: * inner class to wrap a LogSystem into a LogChute
620: */
621: protected static class LogAdapter extends LogChuteSystem {
622: protected LogAdapter(LogSystem logSystem) {
623: super(logSystem);
624: }
625: }
626: }
|