001    /*
002     * Copyright (C) 2006-2007 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.codehaus.gmaven.plugin.stubgen;
018    
019    import org.apache.maven.plugin.MojoExecutionException;
020    import org.apache.maven.plugin.MojoFailureException;
021    import org.apache.maven.shared.io.scan.mapping.SourceMapping;
022    import org.apache.maven.shared.io.scan.mapping.SuffixMapping;
023    import org.apache.maven.shared.model.fileset.FileSet;
024    import org.codehaus.gmaven.feature.Component;
025    import org.codehaus.gmaven.plugin.CompilerMojoSupport;
026    import org.codehaus.gmaven.runtime.StubCompiler;
027    
028    import java.io.File;
029    import java.io.IOException;
030    import java.util.ArrayList;
031    import java.util.Iterator;
032    import java.util.List;
033    
034    /**
035     * Support for Java stub generation mojos.
036     *
037     * <p>
038     * Stub generation basically parses Groovy sources, and then creates the bare-minimum
039     * Java source equivilent so that the maven-compiler-plugin's compile and testCompile
040     * goals can execute and resolve Groovy classes that may be referenced by Java sources.
041     * </p>
042     *
043     * <p>
044     * This is important, since our compile and testCompile goals execute *after* the 
045     * normal Java compiler does.
046     * </p>
047     *
048     * @version $Id: AbstractGenerateStubsMojo.java 49 2009-10-16 14:03:56Z user57 $
049     * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
050     * @author Jason Smith
051     */
052    public abstract class AbstractGenerateStubsMojo
053        extends CompilerMojoSupport
054    {
055        protected AbstractGenerateStubsMojo() {
056            super(StubCompiler.KEY);
057        }
058        
059        public void execute() throws MojoExecutionException, MojoFailureException {
060            super.execute();
061    
062            // Treatment for MGROOVY-187.
063            try {
064                resetStubModifiedDates();
065            }
066            catch (Exception e) {
067                throw new MojoExecutionException("Failed to get output folder.", e);
068            }
069        }
070       
071        /**
072         * Modifies the dates of the created stubs to 1970, ensuring that the Java
073         * compiler will not come along and overwrite perfectly good compiled Groovy 
074         * just because it has a newer source stub.  Basically, this prevents the 
075         * stubs from causing a side effect with the Java compiler, but still allows
076         * the stubs to work with JavaDoc.  Ideally, the code for this should be 
077         * added to the code that creates the stubs, but as that code is sprinkled 
078         * across several different runtimes, I am putting this into the common area.
079         */
080        private void resetStubModifiedDates() throws Exception {
081            List stubs = recurseFiles(getOutputDirectory());
082    
083            for (Iterator i = stubs.iterator(); i.hasNext();) {
084                File file = (File) i.next();
085                file.setLastModified(0L);
086            }
087        }
088        
089        /**
090         * Get all files, recursively, in a folder.
091         * TODO: Should be moved into a utility class.
092         *
093         * @param folder The folder to look in.
094         * @return A list of <code>File</code> instances.
095         */
096        private List recurseFiles(final File folder) {
097            assert folder != null;
098    
099            List result = new ArrayList();
100            File[] files = folder.listFiles();
101    
102            if (files != null) {
103                for (int i = 0; i < files.length; i++) {
104                    if (files[i].isDirectory()) {
105                        result.addAll(recurseFiles(files[i]));
106                    }
107                    else {
108                        result.add(files[i]);
109                    }
110                }
111            }
112    
113            return result;
114        }
115    
116        protected abstract void forceCompile(final File file);
117    
118        protected void process(final Component component) throws Exception {
119            assert component != null;
120    
121            StubCompiler compiler = (StubCompiler)component;
122    
123            compiler.setTargetDirectory(getOutputDirectory());
124    
125            compiler.setClassPath(createClassPath());
126    
127            //
128            // TODO: Bridge mojo config to component config
129            //
130    
131            compile(compiler, sources != null ? sources : getDefaultSources());
132        }
133    
134        protected void compile(final StubCompiler compiler, final FileSet[] sources) throws Exception {
135            assert compiler != null;
136            assert sources != null;
137    
138            // Seems like we have to add the output dir each time so that the m-p-p site muck works
139            addSourceRoot(getOutputDirectory());
140            
141            for (int i=0; i<sources.length; i++) {
142                addSourceRoot(sources[i]);
143    
144                SourceMapping[] mappings = {
145                    new SuffixMapping(".groovy", ".java"),
146                };
147    
148                File[] files = scanForSources(sources[i], mappings);
149    
150                for (int j=0; j < files.length; j++) {
151                    log.debug(" + " + files[j]);
152    
153                    compiler.add(files[j]);
154    
155                    // For now assume we compile this puppy
156                    forceCompile(files[j]);
157                }
158            }
159    
160            int count = compiler.compile();
161    
162            if (count == 0) {
163                log.info("No sources found for Java stub generation");
164            }
165            else {
166                log.info("Generated " + count + " Java stub" + (count > 1 ? "s" : ""));
167            }
168        }
169    
170        private void addSourceRoot(final FileSet fileSet) throws IOException {
171            assert fileSet != null;
172    
173            // Hook up as a source root so other plugins (like the m-compiler-p) can process anything in here if needed
174            File basedir = new File(fileSet.getDirectory());
175            
176            addSourceRoot(basedir);
177        }
178    }