001package org.apache.maven.tools.plugin.extractor;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.IOException;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028import java.util.TreeMap;
029
030import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
031import org.apache.maven.plugin.descriptor.MojoDescriptor;
032import org.apache.maven.project.MavenProject;
033import org.apache.maven.tools.plugin.PluginToolsRequest;
034import org.codehaus.plexus.logging.AbstractLogEnabled;
035import org.codehaus.plexus.util.DirectoryScanner;
036import org.codehaus.plexus.util.FileUtils;
037import org.codehaus.plexus.util.StringUtils;
038
039/**
040 * @author jdcasey
041 * @version $Id: AbstractScriptedMojoDescriptorExtractor.html 1030109 2018-05-20 14:45:18Z hboutemy $
042 */
043public abstract class AbstractScriptedMojoDescriptorExtractor
044    extends AbstractLogEnabled
045    implements MojoDescriptorExtractor
046{
047    /** {@inheritDoc} */
048    public List<MojoDescriptor> execute( PluginToolsRequest request )
049        throws ExtractionException, InvalidPluginDescriptorException
050    {
051        getLogger().debug( "Running: " + getClass().getName() );
052        String metadataExtension = getMetadataFileExtension( request );
053        String scriptExtension = getScriptFileExtension( request );
054        
055        MavenProject project = request.getProject();
056
057        @SuppressWarnings( "unchecked" )
058        Map<String, Set<File>> scriptFilesKeyedByBasedir =
059            gatherFilesByBasedir( project.getBasedir(), project.getScriptSourceRoots(), scriptExtension, request );
060
061        List<MojoDescriptor> mojoDescriptors;
062        if ( !StringUtils.isEmpty( metadataExtension ) )
063        {
064            @SuppressWarnings( "unchecked" )
065            Map<String, Set<File>> metadataFilesKeyedByBasedir =
066                gatherFilesByBasedir( project.getBasedir(), project.getScriptSourceRoots(), metadataExtension,
067                                      request );
068
069            mojoDescriptors = extractMojoDescriptorsFromMetadata( metadataFilesKeyedByBasedir, request );
070        }
071        else
072        {
073            mojoDescriptors = extractMojoDescriptors( scriptFilesKeyedByBasedir, request );
074        }
075
076        copyScriptsToOutputDirectory( scriptFilesKeyedByBasedir, project.getBuild().getOutputDirectory(), request );
077
078        return mojoDescriptors;
079    }
080
081    /**
082     * @param scriptFilesKeyedByBasedir not null
083     * @param outputDirectory not null
084     * @throws ExtractionException if any
085     */
086    protected void copyScriptsToOutputDirectory( Map<String, Set<File>> scriptFilesKeyedByBasedir,
087                                                 String outputDirectory, PluginToolsRequest request )
088        throws ExtractionException
089    {
090        File outputDir = new File( outputDirectory );
091
092        if ( !outputDir.exists() )
093        {
094            outputDir.mkdirs();
095        }
096
097        for ( Map.Entry<String, Set<File>> entry : scriptFilesKeyedByBasedir.entrySet() )
098        {
099            File sourceDir = new File( entry.getKey() );
100
101            Set<File> scripts = entry.getValue();
102
103            for ( File scriptFile : scripts )
104            {
105                String relativePath = scriptFile.getPath().substring( sourceDir.getPath().length() );
106
107                if ( relativePath.charAt( 0 ) == File.separatorChar )
108                {
109                    relativePath = relativePath.substring( 1 );
110                }
111
112                File outputFile = new File( outputDir, relativePath ).getAbsoluteFile();
113
114                if ( !outputFile.getParentFile().exists() )
115                {
116                    outputFile.getParentFile().mkdirs();
117                }
118
119                try
120                {
121                    FileUtils.copyFile( scriptFile, outputFile );
122                }
123                catch ( IOException e )
124                {
125                    throw new ExtractionException(
126                        "Cannot copy script file: " + scriptFile + " to output: " + outputFile, e );
127                }
128            }
129        }
130    }
131
132    /**
133     * @param basedir not null
134     * @param directories not null
135     * @param scriptFileExtension not null
136     * @return map with subdirs paths as key
137     */
138    protected Map<String, Set<File>> gatherFilesByBasedir( File basedir, List<String> directories,
139                                                           String scriptFileExtension, PluginToolsRequest request )
140    {
141        Map<String, Set<File>> sourcesByBasedir = new TreeMap<String, Set<File>>();
142
143        for ( String resourceDir : directories )
144        {
145            Set<File> sources = new HashSet<File>();
146
147            getLogger().debug( "Scanning script dir: " + resourceDir + " with extractor: " + getClass().getName() );
148            File dir = new File( resourceDir );
149            if ( !dir.isAbsolute() )
150            {
151                dir = new File( basedir, resourceDir ).getAbsoluteFile();
152            }
153
154            resourceDir = dir.getPath();
155
156            if ( dir.exists() )
157            {
158                DirectoryScanner scanner = new DirectoryScanner();
159
160                scanner.setBasedir( dir );
161                scanner.addDefaultExcludes();
162                scanner.setIncludes( new String[]{"**/*" + scriptFileExtension} );
163                scanner.scan();
164
165                String[] relativePaths = scanner.getIncludedFiles();
166
167                for ( String relativePath : relativePaths )
168                {
169                    File scriptFile = new File( dir, relativePath ).getAbsoluteFile();
170
171                    if ( scriptFile.isFile() && relativePath.endsWith( scriptFileExtension ) )
172                    {
173                        sources.add( scriptFile );
174                    }
175                }
176
177                sourcesByBasedir.put( resourceDir, sources );
178            }
179        }
180
181        return sourcesByBasedir;
182    }
183
184    /**
185     * Should be implemented in the sub classes.
186     *
187     * @param metadataFilesByBasedir could be null
188     * @param request The plugin request, never <code>null</code>.
189     * @return always null
190     * @throws ExtractionException if any
191     * @throws InvalidPluginDescriptorException if any
192     */
193    protected List<MojoDescriptor> extractMojoDescriptorsFromMetadata( Map<String, Set<File>> metadataFilesByBasedir,
194                                                                       PluginToolsRequest request )
195        throws ExtractionException, InvalidPluginDescriptorException
196    {
197        return null;
198    }
199
200    /**
201     * Should be implemented in the sub classes.
202     *
203     * @return always null
204     */
205    protected String getMetadataFileExtension( PluginToolsRequest request )
206    {
207        return null;
208    }
209
210    /**
211     * Should be implemented in the sub classes.
212     *
213     * @param scriptFilesKeyedByBasedir could be null
214     * @param request The plugin request, never <code>null</code>.
215     * @return always null
216     * @throws ExtractionException if any
217     * @throws InvalidPluginDescriptorException if any
218     */
219    protected List<MojoDescriptor> extractMojoDescriptors( Map<String, Set<File>> scriptFilesKeyedByBasedir,
220                                                           PluginToolsRequest request )
221        throws ExtractionException, InvalidPluginDescriptorException
222    {
223        return null;
224    }
225
226    /**
227     * @return the file extension like <code>.bsh</code> for BeanShell.
228     */
229    protected abstract String getScriptFileExtension( PluginToolsRequest request );
230
231}