001package com.nativelibs4java.velocity;
002
003/*
004 * Copyright 2001-2005 The Apache Software Foundation.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018import static com.nativelibs4java.velocity.Utils.*;
019import com.google.common.base.Function;
020import java.io.BufferedReader;
021import org.apache.maven.plugin.AbstractMojo;
022import org.apache.maven.plugin.MojoExecutionException;
023import org.apache.maven.project.MavenProject;
024import org.apache.maven.model.Resource;
025
026import java.io.File;
027import java.io.FileReader;
028import java.io.FileWriter;
029import java.io.IOException;
030import java.io.PrintWriter;
031import java.io.StringWriter;
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.Map;
035import java.util.List;
036import java.util.regex.Matcher;
037import java.util.regex.Pattern;
038import org.apache.velocity.*;
039import org.apache.velocity.app.VelocityEngine;
040import org.apache.velocity.app.Velocity;
041import org.codehaus.plexus.util.IOUtil;
042
043/**
044 * Generates source code with velocity templates
045 *
046 * @goal generate
047 * @phase generate-sources
048 * @description Generates source code with velocity templates
049 */
050public class VelocityMojo
051        extends AbstractMojo {
052
053    /**
054     * Extra properties
055     *
056     * @parameter
057     * @optional
058     */
059    private Map<String, String> properties;
060    /**
061     * Source folder for velocity templates
062     *
063     * @parameter expression="${basedir}/src/"
064     * @required
065     */
066    private File sourcePathRoot;
067    /**
068     * Source folder for velocity templates
069     *
070     * @parameter expression="${basedir}/src/main/velocity/"
071     * @required
072     */
073    private File velocitySources;
074    /**
075     * Source folder for velocity test templates
076     *
077     * @parameter expression="${basedir}/src/test/velocity/"
078     * @required
079     */
080    private File velocityTestSources;
081    /**
082     * Output directory for generated sources.
083     *
084     * @parameter expression="${project.build.directory}/generated-sources/main"
085     * @optional
086     */
087    private File sourcesOutputDirectory;
088    /**
089     * Output directory for generated test sources.
090     *
091     * @parameter expression="${project.build.directory}/generated-sources/test"
092     * @optional
093     */
094    private File testSourcesOutputDirectory;
095    /**
096     * Output directory for resources.
097     *
098     * @parameter expression="${project.build.directory}/generated-resources/"
099     * @optional
100     */
101    private File resourcesOutputDirectory;
102    /**
103     * Output directory test resources.
104     *
105     * @parameter
106     * expression="${project.build.directory}/generated-test-resources/"
107     * @optional
108     */
109    private File testResourcesOutputDirectory;
110    /**
111     * @parameter expression="${project}"
112     * @required
113     * @readonly
114     * @since 1.0
115     */
116    private MavenProject project;
117
118    static void listVeloFiles(File f, Collection<File> out) throws IOException {
119        if (f.isHidden()) {
120            return;
121        }
122
123        String n = f.getName().toLowerCase();
124        if (f.isDirectory()) {
125            if (n.equals(".svn") || n.equals("CVS")) {
126                return;
127            }
128
129            for (File ff : f.listFiles()) {
130                listVeloFiles(ff.getAbsoluteFile(), out);
131            }
132        } else if (f.isFile()) {
133            //if (n.endsWith(".velo") || n.endsWith(".vm") || n.endsWith(".velocity"))
134            if (!n.startsWith("."))//endsWith(".velo") || n.endsWith(".vm") || n.endsWith(".velocity"))
135            {
136                out.add(f);
137            }
138        }
139    }
140
141    public File getOutputFile(File vmFile, File velocitySources, File outputDirectory) throws IOException {
142        //String canoRoot = sourcePathRoot.getCanonicalPath();
143        String canoRoot = velocitySources.getCanonicalPath();
144        //String canoSrc = velocitySources.getCanonicalPath();
145        String abs = vmFile.getCanonicalPath();
146        String rel = abs.substring(canoRoot.length());
147        String relLow = rel.toLowerCase();
148        for (String suf : new String[]{".vm", ".velo", ".velocity"}) {
149            if (relLow.endsWith(suf)) {
150                rel = rel.substring(0, rel.length() - suf.length());
151                break;
152            }
153        }
154        int i = rel.lastIndexOf('.');
155        File out = outputDirectory;
156        //if (i >= 0) {
157        //    String ext = rel.substring(i + 1);
158        //    out = new File(out, ext);
159        //}
160
161        return new File(out.getCanonicalPath() + rel);
162    }
163
164    public void execute()
165            throws MojoExecutionException {
166        if (executeAll(velocitySources, false)) {
167            //File jf = new File(outputDirectory, "java");
168            //if (jf.exists())
169            //  outputDirectory = jf;
170            project.addCompileSourceRoot(sourcesOutputDirectory.toString());
171            Resource res = new Resource();
172            res.setDirectory(resourcesOutputDirectory.getAbsolutePath());
173            project.addResource(res);
174        }
175
176        if (executeAll(velocityTestSources, true)) {
177            //File jf = new File(testOutputDirectory, "java");
178            //if (jf.exists())
179            //  testOutputDirectory = jf;
180            project.addTestCompileSourceRoot(testSourcesOutputDirectory.toString());
181            Resource res = new Resource();
182            res.setDirectory(testResourcesOutputDirectory.getAbsolutePath());
183            project.addTestResource(res);
184        }
185
186        /*if (templates == null)
187         getLog().error("Did not find <templates> !");
188         else {
189         getLog().info("Found " + templates.size() + " templates");
190         for (Template conf : templates)
191         conf.execute(this);
192         }*/
193    }
194
195    private VelocityEngine createEngine(String canoPath) throws Exception {
196        VelocityEngine ve = new VelocityEngine();
197        ve.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, new MavenLogChute(getLog()));
198        ve.setProperty("velocimacro.permissions.allow.inline.to.replace.global", "true");
199        ve.setProperty("velocimacro.permissions.allow.inline.local.scope", "false");
200        ve.setProperty("velocimacro.context.localscope", "false");
201        ve.setProperty("file.resource.loader.path", canoPath);
202        ve.init();
203        return ve;
204    }
205
206    private boolean executeAll(File velocitySources, boolean isTest) throws MojoExecutionException {
207
208        List<File> files = new ArrayList<File>();
209        String canoPath;
210        try {
211            velocitySources = velocitySources.getCanonicalFile();
212            listVeloFiles(velocitySources, files);
213
214            canoPath = sourcePathRoot.getCanonicalPath();
215            getLog().info("Velocity root path = " + canoPath);
216
217
218        } catch (Exception ex) {
219            throw new MojoExecutionException("Failed to list files from '" + velocitySources + "'", ex);
220        }
221
222
223        getLog().info("Found " + files.size() + " files in '" + velocitySources + "'...");
224        getLog().info("Got Maven properties : " + project.getProperties());
225        getLog().info("Got properties : " + properties);
226
227        if (files.isEmpty()) {
228            return false;
229        }
230
231        for (File file : files) {
232            try {
233                file = file.getCanonicalFile();
234
235                String name = file.getName();
236                if (name.endsWith("~") || name.endsWith(".bak")) {
237                    getLog().info("Skipping: '" + name + "'");
238                    continue;
239                }
240                String lowName = name.toLowerCase();
241
242                File outputDirectory;
243                boolean isSource = name.endsWith(".java") || name.endsWith(".scala");
244                if (isSource) {
245                    outputDirectory = isTest ? testSourcesOutputDirectory : sourcesOutputDirectory;
246                } else {
247                    outputDirectory = isTest ? testResourcesOutputDirectory : resourcesOutputDirectory;
248                }
249
250                File outFile = getOutputFile(file, velocitySources, outputDirectory);
251                if (outFile.exists() && outFile.lastModified() > file.lastModified()) {
252                    getLog().info("Up-to-date: '" + name + "'");
253                    continue;
254                }
255                getLog().info("Executing template '" + name + "'...");
256
257                //context = new VelocityContext();
258                String cano = file.getCanonicalPath();
259                cano = cano.substring(canoPath.length());
260                if (cano.startsWith(File.separator)) {
261                    cano = cano.substring(File.separator.length());
262                }
263
264                VelocityEngine ve = createEngine(canoPath);
265                VelocityContext context = new VelocityContext();//execution.getParameters());
266                context.put("primitives", Primitive.getPrimitives());
267                context.put("primitivesNoBool", Primitive.getPrimitivesNoBool());
268                context.put("bridJPrimitives", Primitive.getBridJPrimitives());
269                context.put("pom", project);
270
271                for (Map.Entry<Object, Object> e : project.getProperties().entrySet()) {
272                    String propName = ((String) e.getKey()).replace('.', '_'), propValue = (String) e.getValue();
273                    getLog().debug("Got property : " + propName + " = " + propValue);
274
275                    context.put(propName, propValue);
276                }
277
278                if (properties != null) {
279                    for (Map.Entry<String, String> e : properties.entrySet()) {
280                        String propName = e.getKey(), propValue = e.getValue();
281                        getLog().debug("Got property : " + propName + " = " + propValue);
282
283                        context.put(propName, propValue);
284                    }
285                }
286
287                StringWriter out = new StringWriter();
288                
289                boolean quoteComments = true;
290                if (quoteComments) {
291                    String source = readTextFile(file);
292                    String quoted = quoteSharpsInComments(source);
293                    ve.evaluate(context, out, "velocity", quoted);
294                } else {
295                    org.apache.velocity.Template template = ve.getTemplate(cano);//file.getName());
296                    template.merge(context, out);
297                }
298                out.close();
299
300                outFile.getParentFile().mkdirs();
301                String transformed = out.toString();
302                writeTextFile(outFile, transformed);
303                //getLog().info("\tGenerated '" + outFile.getName() + "'");
304
305            } catch (Exception ex) {
306                //throw 
307                new MojoExecutionException("Failed to execute template '" + file + "'", ex).printStackTrace();
308            }
309        }
310
311        return true;
312    }
313}