package org.codehaus.mojo.ruby.extractor; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.apache.maven.plugin.descriptor.DuplicateParameterException; import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.Parameter; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.tools.plugin.extractor.AbstractScriptedMojoDescriptorExtractor; import org.apache.maven.tools.plugin.extractor.ExtractionException; import org.codehaus.plexus.component.jruby.JRubyInvoker; import org.codehaus.plexus.component.jruby.JRubyRuntimeInvoker; import org.codehaus.plexus.util.StringOutputStream; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyHash; import org.jruby.RubyString; import org.jruby.runtime.builtin.IRubyObject; public class RubyExtractor extends AbstractScriptedMojoDescriptorExtractor { protected List extractMojoDescriptors( Map scriptFilesKeyedByBasedir, PluginDescriptor pluginDescriptor ) throws ExtractionException, InvalidPluginDescriptorException { List descriptors = new ArrayList(); for ( Iterator mapIterator = scriptFilesKeyedByBasedir.entrySet().iterator(); mapIterator.hasNext(); ) { Map.Entry entry = (Map.Entry) mapIterator.next(); String basedir = (String) entry.getKey(); Set metadataFiles = (Set) entry.getValue(); for ( Iterator it = metadataFiles.iterator(); it.hasNext(); ) { File scriptFile = (File) it.next(); String relativePath = null; if ( basedir.endsWith( "/" ) ) { basedir = basedir.substring( 0, basedir.length() - 2 ); } relativePath = scriptFile.getPath().substring( basedir.length() ); relativePath = relativePath.replace( '\\', '/' ); MojoDescriptor mojoDescriptor = createMojoDescriptor( basedir, relativePath, pluginDescriptor ); descriptors.add( mojoDescriptor ); } } return descriptors; } private MojoDescriptor createMojoDescriptor( String basedir, String resource, PluginDescriptor pluginDescriptor ) throws InvalidPluginDescriptorException { MojoDescriptor mojoDescriptor = new MojoDescriptor(); mojoDescriptor.setPluginDescriptor( pluginDescriptor ); mojoDescriptor.setLanguage( "jruby-mojo" ); mojoDescriptor.setComponentConfigurator( "jruby" ); mojoDescriptor.setImplementation( resource ); getLogger().info( "Ruby Mojo File: " + resource ); try { InputStream extractor = new FileInputStream( new File( basedir, resource ).getAbsolutePath() ); JRubyInvoker invoker = new JRubyRuntimeInvoker( new InputStreamReader( extractor ) ); invoker.setRequires( new String[] { "inline_extractor.rb" } ); StringOutputStream stdout = new StringOutputStream(); StringOutputStream stderr = new StringOutputStream(); Map map = (Map)invoker.invoke(); logOutput( stdout.toString(), false ); logOutput( stderr.toString(), true ); Ruby runtime = Ruby.getDefaultInstance(); String goal = getParameter( runtime, map, "goal" ); if ( goal == null ) { throw new InvalidPluginDescriptorException( "Cannot create plugin descriptor. Goal is a required value for a Mojo" ); } mojoDescriptor.setGoal( goal ); mojoDescriptor.setPhase( getParameter( runtime, map, "phase" ) ); mojoDescriptor.setDependencyResolutionRequired( getParameter( runtime, map, "requiresDependencyResolution" ) ); mojoDescriptor.setDescription( getParameter( runtime, map, "description" ) ); IRubyObject executes = (IRubyObject)map.get( RubyString.newString( runtime, "execute" ) ); if ( executes != null && !executes.isNil() ) { Map executesMap = (RubyHash)executes; mojoDescriptor.setExecutePhase( getParameter( runtime, executesMap, "phase" ) ); mojoDescriptor.setExecuteLifecycle( getParameter( runtime, executesMap, "lifecycle" ) ); mojoDescriptor.setExecuteGoal( getParameter( runtime, executesMap, "goal" ) ); } IRubyObject fields = (IRubyObject)map.get( RubyString.newString( runtime, "fields" ) ); if ( fields != null && !fields.isNil() ) { RubyArray fieldsList = (RubyArray) fields; for ( Iterator iter = fieldsList.iterator(); iter.hasNext(); ) { RubyHash field = (RubyHash)iter.next(); setParameter( runtime, field, mojoDescriptor ); } } } catch ( Exception e ) { getLogger().error( "", e ); } return mojoDescriptor; } private String getParameter( Ruby runtime, Map params, String annotation ) { Object param = params.get( RubyString.newString( runtime, annotation ) ); if( param instanceof String ) { return (String)param; } IRubyObject anno = (IRubyObject)param; if ( anno != null && !anno.isNil() ) { return anno.toString(); } return null; } private void setParameter( Ruby runtime, Map field, MojoDescriptor mojoDescriptor ) throws DuplicateParameterException { String name = getParameter( runtime, field, "name" ); if ( name == null ) { throw new NullPointerException( "Expected a non-null value for a declared parameter name" ); } Parameter param = new Parameter(); param.setName( name ); param.setRequired( "true".equals( getParameter( runtime, field, "required" ) ) ); param.setEditable( !"true".equals( getParameter( runtime, field, "readonly" ) ) ); param.setDeprecated( getParameter( runtime, field, "deprecated" ) ); param.setDescription( getParameter( runtime, field, "description" ) ); param.setType( getParameter( runtime, field, "type" ) ); param.setAlias( getParameter( runtime, field, "alias" ) ); param.setDefaultValue( getParameter( runtime, field, "default" ) ); param.setExpression( getParameter( runtime, field, "expression" ) ); mojoDescriptor.addParameter( param ); } protected String getScriptFileExtension() { return "rb"; } /** * Outputs Strings as info or error to the mojo's log. * * @param out * @param error true if error */ private void logOutput( String output, boolean error ) { if ( output != null && output.length() > 0 ) { for ( StringTokenizer tokens = new StringTokenizer( output, "\n" ); tokens.hasMoreTokens(); ) { if ( error ) { getLogger().error( tokens.nextToken() ); } else { getLogger().info( tokens.nextToken() ); } } } } }