package org.codehaus.mojo.ruby.extractor; import java.io.File; 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.RubyIO; import org.jruby.exceptions.RaiseException; public class RDocExtractor 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 ); if( mojoDescriptor != null ) { descriptors.add( mojoDescriptor ); } } } return descriptors; } private MojoDescriptor createMojoDescriptor( String basedir, String resource, PluginDescriptor pluginDescriptor ) throws ExtractionException, InvalidPluginDescriptorException { MojoDescriptor mojoDescriptor = new MojoDescriptor(); mojoDescriptor.setPluginDescriptor( pluginDescriptor ); mojoDescriptor.setLanguage( "jruby-mojo" ); mojoDescriptor.setComponentConfigurator( "jruby" ); mojoDescriptor.setImplementation( resource ); InputStream extractor = Thread.currentThread().getContextClassLoader().getResourceAsStream( "rdoc_extractor.rb" ); StringOutputStream stdout = new StringOutputStream(); StringOutputStream stderr = new StringOutputStream(); try { JRubyInvoker invoker = new JRubyRuntimeInvoker( new InputStreamReader( extractor ) ); invoker.setRequires( new String[] { "mojo_require.rb" } ); invoker.putGlobal( "file_name", new File( basedir, resource ).getAbsolutePath() ); Object invoked = invoker.invoke( stdout, stderr ); if( invoked instanceof RubyIO ) { if( stderr.toString().startsWith( "No goal" ) ) { return null; } logOutput( stdout.toString(), false ); logOutput( stderr.toString(), true ); throw new ExtractionException( stdout.toString() ); } getLogger().info( "Ruby Mojo File: " + resource ); logOutput( stdout.toString(), false ); logOutput( stderr.toString(), true ); Map map = (Map)invoked; mojoDescriptor.setGoal( (String) map.get( "goal" ) ); mojoDescriptor.setPhase( (String) map.get( "phase" ) ); mojoDescriptor.setDependencyResolutionRequired( (String) map.get( "requiresDependencyResolution" ) ); mojoDescriptor.setDescription( (String) map.get( "description" ) ); mojoDescriptor.setAggregator( "true".equals( map.get( "aggregator" ) ) ); String configurator = (String) map.get( "configurator" ); if ( configurator != null ) { mojoDescriptor.setComponentConfigurator( configurator ); } Object executes = map.get( "execute" ); if ( executes instanceof Map ) { Map executesMap = (Map) executes; mojoDescriptor.setExecutePhase( (String) executesMap.get( "phase" ) ); mojoDescriptor.setExecuteLifecycle( (String) executesMap.get( "lifecycle" ) ); mojoDescriptor.setExecuteGoal( (String) executesMap.get( "goal" ) ); } Object fields = map.get( "fields" ); if ( fields != null ) { if ( fields instanceof List ) { List fieldsList = (List) fields; for ( Iterator iter = fieldsList.iterator(); iter.hasNext(); ) { Map field = (Map) iter.next(); setParameter( field, mojoDescriptor ); } } else if ( fields instanceof Map ) { setParameter( (Map) ( (Map) fields ).get( "field" ), mojoDescriptor ); } else { throw new IllegalArgumentException( "Internal exception. 'fields' is assumed to be a Map or a List." ); } } setBasedirParameter( mojoDescriptor ); } catch( RaiseException e ) { // TODO: should send to log JRubyRuntimeInvoker.printREStackTrace( e, System.err ); } catch ( Exception e ) { getLogger().error( "", e ); } return mojoDescriptor; } private void setParameter( Map field, MojoDescriptor mojoDescriptor ) throws DuplicateParameterException { if ( field == null || !field.containsKey( "parameter" ) ) { return; } String name = (String) field.get( "name" ); if ( name == null ) { throw new NullPointerException( "Expected a non-null value for a declared parameter name" ); } if ( "execute".equals( name ) ) { // We don't need to add this return; } Parameter param = new Parameter(); param.setName( name ); param.setRequired( "true".equals( field.get( "required" ) ) ); param.setEditable( !"true".equals( field.get( "readonly" ) ) ); param.setDeprecated( (String) field.get( "deprecated" ) ); param.setDescription( (String) field.get( "description" ) ); Object parameter = field.get( "parameter" ); if ( parameter instanceof Map ) { Map parameterMap = (Map) parameter; param.setAlias( (String) parameterMap.get( "alias" ) ); param.setDefaultValue( (String) parameterMap.get( "default-value" ) ); String expression = (String) parameterMap.get( "expression" ); param.setExpression( expression ); String type = (String)parameterMap.get( "type" ); if( type != null && type.length() > 0 ) { param.setType( type ); // XXX: This is a horrible hack to deal with ComponentConfigurator's lack of full plugin.xml access if( expression == null && !"java.lang.String".equals( type ) && !"java.util.Map".equals( type ) ) { param.setExpression( "${" + name + "}" ); } } else { param.setType( "java.lang.String" ); } } mojoDescriptor.addParameter( param ); } private void setBasedirParameter( MojoDescriptor mojoDescriptor ) throws DuplicateParameterException { Map parameters = mojoDescriptor.getParameterMap(); if ( parameters == null || parameters.containsKey( "basedir" ) ) { return; } Parameter param = new Parameter(); param.setName( "basedir" ); param.setRequired( true ); param.setEditable( false ); param.setExpression( "${basedir}" ); param.setType( File.class.getName() ); 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() ); } } } } }