1 package org.apache.velocity.convert;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.File;
23 import java.io.FileWriter;
24 import java.io.IOException;
25
26 import org.apache.oro.text.perl.Perl5Util;
27 import org.apache.velocity.util.StringUtils;
28 import org.apache.tools.ant.DirectoryScanner;
29
30 /**
31 * This class will convert a WebMacro template to
32 * a Velocity template. Uses the ORO Regexp package to do the
33 * rewrites. Note, it isn't 100% perfect, but will definitely get
34 * you about 99.99% of the way to a converted system. Please
35 * see the website documentation for more information on how to use
36 * this class.
37 *
38 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
39 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
40 * @version $Id: WebMacro.java 463298 2006-10-12 16:10:32Z henning $
41 */
42 public class WebMacro
43 {
44 /**
45 *
46 */
47 protected static final String VM_EXT = ".vm";
48
49 /**
50 *
51 */
52 protected static final String WM_EXT = ".wm";
53
54 /**
55 * The regexes to use for line by line substition. The regexes
56 * come in pairs. The first is the string to match, the second is
57 * the substitution to make.
58 */
59 protected static String[] perLineREs =
60 {
61 // Make #if directive match the Velocity directive style.
62 "#if\\s*[(]\\s*(.*\\S)\\s*[)]\\s*(#begin|{)[ \\t]?",
63 "#if( $1 )",
64
65 // Remove the WM #end #else #begin usage.
66 "[ \\t]?(#end|})[ \\t]*\n(\\s*)#else\\s*(#begin|{)[ \\t]?(\\w)",
67 "$2#else#**#$4", // avoid touching followup word with embedded comment
68 "[ \\t]?(#end|})[ \\t]*\n(\\s*)#else\\s*(#begin|{)[ \\t]?",
69 "$2#else",
70 "(#end|})(\\s*#else)\\s*(#begin|{)[ \\t]?",
71 "$1\n$2",
72
73 // Convert WM style #foreach to Velocity directive style.
74 "#foreach\\s+(\\$\\w+)\\s+in\\s+(\\$[^\\s#]+)\\s*(#begin|{)[ \\t]?",
75 "#foreach( $1 in $2 )",
76
77 // Convert WM style #set to Velocity directive style.
78 "#set\\s+(\\$[^\\s=]+)\\s*=\\s*([\\S \\t]+)",
79 "#set( $1 = $2 )",
80 "(##[# \\t\\w]*)\\)", // fix comments included at end of line
81 ")$1",
82
83 // Convert WM style #parse to Velocity directive style.
84 "#parse\\s+([^\\s#]+)[ \\t]?",
85 "#parse( $1 )",
86
87 // Convert WM style #include to Velocity directive style.
88 "#include\\s+([^\\s#]+)[ \\t]?",
89 "#include( $1 )",
90
91 // Convert WM formal reference to VTL syntax.
92 "\\$\\(([^\\)]+)\\)",
93 "${$1}",
94 "\\${([^}\\(]+)\\(([^}]+)}\\)", // fix encapsulated brakets: {(})
95 "${$1($2)}",
96
97 // Velocity currently does not permit leading underscore.
98 "\\$_",
99 "$l_",
100 "\\${(_[^}]+)}", // within a formal reference
101 "${l$1}",
102
103 // Eat semi-colons in (converted) VTL #set directives.
104 "(#set\\s*\\([^;]+);(\\s*\\))",
105 "$1$2",
106
107 // Convert explicitly terminated WM statements to VTL syntax.
108 "(^|[^\\\\])\\$(\\w[^=\n;'\"]*);",
109 "$1${$2}",
110
111 // Change extensions when seen.
112 "\\.wm",
113 ".vm"
114 };
115
116 /**
117 * Iterate through the set of find/replace regexes
118 * that will convert a given WM template to a VM template
119 * @param target
120 */
121 public void convert(String target)
122 {
123 File file = new File(target);
124
125 if (!file.exists())
126 {
127 throw new RuntimeException("The specified template or directory does not exist");
128 }
129
130 if (file.isDirectory())
131 {
132 String basedir = file.getAbsolutePath();
133 String newBasedir = basedir + VM_EXT;
134
135 DirectoryScanner ds = new DirectoryScanner();
136 ds.setBasedir(basedir);
137 ds.addDefaultExcludes();
138 ds.scan();
139 String[] files = ds.getIncludedFiles();
140
141 for (int i = 0; i < files.length; i++)
142 {
143 writeTemplate(files[i], basedir, newBasedir);
144 }
145 }
146 else
147 {
148 writeTemplate(file.getAbsolutePath(), "", "");
149 }
150 }
151
152 /**
153 * Write out the converted template to the given named file
154 * and base directory.
155 */
156 private boolean writeTemplate(String file, String basedir,
157 String newBasedir)
158 {
159 if (file.indexOf(WM_EXT) < 0)
160 {
161 return false;
162 }
163
164 System.out.println("Converting " + file + "...");
165
166 String template = file;
167 String newTemplate = convertName(file);
168
169 if (basedir.length() > 0)
170 {
171 String templateDir = newBasedir + extractPath(file);
172 File outputDirectory = new File(templateDir);
173
174 template = basedir + File.separator + file;
175
176
177 if (! outputDirectory.exists())
178 {
179 outputDirectory.mkdirs();
180 }
181
182 newTemplate = newBasedir + File.separator + convertName(file);
183 }
184
185 String convertedTemplate = convertTemplate(template);
186
187 FileWriter fw = null;
188 try
189 {
190 fw = new FileWriter(newTemplate);
191 fw.write(convertedTemplate);
192 }
193 catch (Exception e)
194 {
195 e.printStackTrace();
196 }
197 finally
198 {
199 if (fw != null)
200 {
201 try
202 {
203 fw.close();
204 }
205 catch (IOException io)
206 {
207 // Do nothing
208 }
209 }
210 }
211
212 return true;
213 }
214
215 /**
216 * Gets the path segment of the full path to a file (i.e. one
217 * which originally included the file name).
218 */
219 private final String extractPath(String file)
220 {
221 int lastSepPos = file.lastIndexOf(File.separator);
222 return (lastSepPos == -1 ? "" :
223 File.separator + file.substring(0, lastSepPos));
224 }
225
226 /**
227 * Simple extension conversion of .wm to .vm
228 */
229 private String convertName(String name)
230 {
231 return (name.indexOf(WM_EXT) < 0)
232 ? name
233 : name.substring(0, name.indexOf(WM_EXT)) + VM_EXT;
234 }
235
236 /**
237 * How to use this little puppy :-)
238 */
239 private static final void usage()
240 {
241 System.err.println("Usage: convert-wm <template.wm | directory>");
242 }
243
244 /**
245 * Apply find/replace regexes to our WM template
246 * @param template
247 * @return Returns the template with all regexprs applied.
248 */
249 public String convertTemplate(String template)
250 {
251 String contents = StringUtils.fileContentsToString(template);
252
253 // Overcome Velocity 0.71 limitation.
254 // HELP: Is this still necessary?
255 if (!contents.endsWith("\n"))
256 {
257 contents += "\n";
258 }
259
260 // Convert most markup.
261 Perl5Util perl = new Perl5Util();
262 for (int i = 0; i < perLineREs.length; i += 2)
263 {
264 contents = perl.substitute(makeSubstRE(i), contents);
265 }
266
267 // Convert closing curlies.
268 if (perl.match("m/javascript/i", contents))
269 {
270 // ASSUMPTION: JavaScript is indented, WM is not.
271 contents = perl.substitute("s/\n}/\n#end/g", contents);
272 }
273 else
274 {
275 contents = perl.substitute("s/(\n\\s*)}/$1#end/g", contents);
276 contents = perl.substitute("s/#end\\s*\n\\s*#else/#else/g",
277 contents);
278 }
279
280 return contents;
281 }
282
283 /**
284 * Makes a Perl 5 regular expression for use by ORO.
285 */
286 private final String makeSubstRE(int i)
287 {
288 return ("s/" + perLineREs[i] + '/' + perLineREs[i + 1] + "/g");
289 }
290
291 /**
292 * Main hook for the conversion process.
293 * @param args
294 */
295 public static void main(String[] args)
296 {
297 if (args.length > 0)
298 {
299 for (int x=0; x < args.length; x++)
300 {
301 WebMacro converter = new WebMacro();
302 converter.convert(args[x]);
303 }
304 }
305 else
306 {
307 usage();
308 }
309 }
310 }