001    package 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     */
018    import static com.nativelibs4java.velocity.Utils.*;
019    import com.google.common.base.Function;
020    import java.io.BufferedReader;
021    import org.apache.maven.plugin.AbstractMojo;
022    import org.apache.maven.plugin.MojoExecutionException;
023    import org.apache.maven.project.MavenProject;
024    import org.apache.maven.model.Resource;
025    
026    import java.io.File;
027    import java.io.FileReader;
028    import java.io.FileWriter;
029    import java.io.IOException;
030    import java.io.PrintWriter;
031    import java.io.StringWriter;
032    import java.util.ArrayList;
033    import java.util.Collection;
034    import java.util.Map;
035    import java.util.List;
036    import java.util.regex.Matcher;
037    import java.util.regex.Pattern;
038    import org.apache.velocity.*;
039    import org.apache.velocity.app.VelocityEngine;
040    import org.apache.velocity.app.Velocity;
041    import 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     */
050    public 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    }