001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is scripting.dev.java.net. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc.
028: *
029: * Portions Copyrighted 2006 Sun Microsystems, Inc.
030: */
031:
032: package org.netbeans.libs.freemarker;
033:
034: import javax.script.*;
035: import java.io.*;
036: import java.util.Properties;
037: import java.util.Set;
038: import freemarker.template.*;
039: import java.util.Collections;
040: import java.util.Map;
041: import java.util.WeakHashMap;
042: import org.openide.filesystems.FileAttributeEvent;
043: import org.openide.filesystems.FileChangeListener;
044: import org.openide.filesystems.FileEvent;
045: import org.openide.filesystems.FileObject;
046:
047: /* Taken from A. Sundararajan and adopted by Jaroslav Tulach
048: * for NetBeans needs.
049: *
050: * @author A. Sundararajan
051: */
052: import org.openide.filesystems.FileRenameEvent;
053: import org.openide.filesystems.FileUtil;
054:
055: class FreemarkerEngine extends AbstractScriptEngine {
056:
057: public static final String STRING_OUTPUT_MODE = "com.sun.script.freemarker.stringOut";
058: public static final String FREEMARKER_CONFIG = "com.sun.script.freemarker.config";
059: public static final String FREEMARKER_PROPERTIES = "com.sun.script.freemarker.properties";
060: public static final String FREEMARKER_TEMPLATE_DIR = "com.sun.script.freemarker.template.dir";
061: public static final String FREEMARKER_TEMPLATE = "org.openide.filesystems.FileObject";
062:
063: private static Map<FileObject, Template> templates = Collections
064: .synchronizedMap(new WeakHashMap<FileObject, Template>());
065:
066: // my factory, may be null
067: private volatile ScriptEngineFactory factory;
068: private volatile Configuration conf;
069: private volatile FileObject fo;
070:
071: public FreemarkerEngine(ScriptEngineFactory factory) {
072: this .factory = factory;
073: }
074:
075: public FreemarkerEngine() {
076: this (null);
077: }
078:
079: // ScriptEngine methods
080: public Object eval(String str, ScriptContext ctx)
081: throws ScriptException {
082: return eval(new StringReader(str), ctx);
083: }
084:
085: public Object eval(Reader reader, ScriptContext ctx)
086: throws ScriptException {
087: ctx.setAttribute("context", ctx, ScriptContext.ENGINE_SCOPE);
088: initFreeMarkerConfiguration(ctx);
089: String fileName = getFilename(ctx);
090: boolean outputAsString = isStringOutputMode(ctx);
091: Writer out;
092: if (outputAsString) {
093: out = new StringWriter();
094: } else {
095: out = ctx.getWriter();
096: }
097:
098: Template template = null;
099: try {
100: if (fo != null) {
101: template = templates.remove(fo);
102: }
103:
104: if (template == null) {
105: template = new MyTemplate(fo, fileName, reader, conf);
106: } else {
107: ((MyTemplate) template).conf = conf;
108: }
109: template.process(null, out);
110: out.flush();
111: if (fo != null) {
112: templates.put(fo, template);
113: }
114: } catch (Exception exp) {
115: throw new ScriptException(exp);
116: }
117: return outputAsString ? out.toString() : null;
118: }
119:
120: public ScriptEngineFactory getFactory() {
121: synchronized (this ) {
122: if (factory == null) {
123: factory = new FreemarkerFactory();
124: }
125: }
126: return factory;
127: }
128:
129: public Bindings createBindings() {
130: return new SimpleBindings();
131: }
132:
133: // internals only below this point
134: private static String getFilename(ScriptContext ctx) {
135: Object tfo = ctx.getAttribute(FREEMARKER_TEMPLATE);
136: if (tfo instanceof FileObject) {
137: return ((FileObject) tfo).getPath();
138: }
139: Object fileName = ctx.getAttribute(ScriptEngine.FILENAME);
140: if (fileName != null) {
141: return fileName.toString();
142: }
143: return "unknown";
144: }
145:
146: private static boolean isStringOutputMode(ScriptContext ctx) {
147: Object flag = ctx.getAttribute(STRING_OUTPUT_MODE);
148: if (flag != null) {
149: return flag.equals(Boolean.TRUE);
150: } else {
151: return false;
152: }
153: }
154:
155: private void initFreeMarkerConfiguration(ScriptContext ctx) {
156: if (conf == null) {
157: synchronized (this ) {
158: if (conf != null) {
159: return;
160: }
161: Object cfg = ctx.getAttribute(FREEMARKER_CONFIG);
162: if (cfg instanceof Configuration) {
163: conf = (Configuration) cfg;
164: return;
165: }
166:
167: Object tfo = ctx.getAttribute(FREEMARKER_TEMPLATE);
168: fo = tfo instanceof FileObject ? (FileObject) tfo
169: : null;
170:
171: Configuration tmpConf = new RsrcLoader(fo, ctx);
172: try {
173: initConfProps(tmpConf, ctx);
174: initTemplateDir(tmpConf, fo, ctx);
175: } catch (RuntimeException rexp) {
176: throw rexp;
177: } catch (Exception exp) {
178: throw new RuntimeException(exp);
179: }
180: conf = tmpConf;
181: }
182: }
183: }
184:
185: private static void initConfProps(Configuration conf,
186: ScriptContext ctx) {
187: try {
188: Properties props = null;
189: Object tmp = ctx.getAttribute(FREEMARKER_PROPERTIES);
190: if (props instanceof Properties) {
191: props = (Properties) tmp;
192: } else {
193: String propsName = System
194: .getProperty(FREEMARKER_PROPERTIES);
195: if (propsName != null) {
196: File propsFile = new File(propsName);
197: if (propsFile.exists() && propsFile.canRead()) {
198: props = new Properties();
199: props.load(new FileInputStream(propsFile));
200: }
201: }
202: }
203: if (props != null) {
204: Set<Object> keys = props.keySet();
205: for (Object obj : keys) {
206: String key;
207: if (obj instanceof String) {
208: key = (String) obj;
209: } else {
210: continue;
211: }
212: try {
213: conf.setSetting(key, props.get(key).toString());
214: } catch (TemplateException te) {
215: // ignore
216: }
217: }
218: }
219: } catch (RuntimeException re) {
220: throw re;
221: } catch (Exception exp) {
222: throw new RuntimeException(exp);
223: }
224: }
225:
226: private static void initTemplateDir(Configuration conf,
227: FileObject fo, ScriptContext ctx) {
228: try {
229: Object tmp = ctx.getAttribute(FREEMARKER_TEMPLATE_DIR);
230: String dirName;
231: if (tmp != null) {
232: dirName = tmp.toString();
233: } else {
234: if (fo != null) {
235: return;
236: }
237: tmp = System.getProperty(FREEMARKER_TEMPLATE_DIR);
238: dirName = (tmp == null) ? "." : tmp.toString();
239: }
240: File dir = new File(dirName);
241: if (dir.exists() && dir.isDirectory()) {
242: conf.setDirectoryForTemplateLoading(dir);
243: }
244: } catch (IOException exp) {
245: throw new RuntimeException(exp);
246: }
247: }
248:
249: private static final class MyTemplate extends Template implements
250: FileChangeListener {
251: public Configuration conf;
252:
253: public MyTemplate(FileObject fo, String s, Reader r,
254: Configuration c) throws IOException {
255: super (s, r, c);
256: fo.addFileChangeListener(FileUtil.weakFileChangeListener(
257: this , fo));
258: }
259:
260: @Override
261: public Configuration getConfiguration() {
262: return conf == null ? super .getConfiguration() : conf;
263: }
264:
265: public void fileFolderCreated(FileEvent fe) {
266: clear();
267: }
268:
269: public void fileDataCreated(FileEvent fe) {
270: clear();
271: }
272:
273: public void fileChanged(FileEvent fe) {
274: clear();
275: }
276:
277: public void fileDeleted(FileEvent fe) {
278: clear();
279: }
280:
281: public void fileRenamed(FileRenameEvent fe) {
282: clear();
283: }
284:
285: public void fileAttributeChanged(FileAttributeEvent fe) {
286: clear();
287: }
288:
289: private void clear() {
290: templates.clear();
291: }
292: } // end of MyTemplate
293:
294: }
|