/** * */ package net.sourceforge.seqware.pipeline.plugins; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import joptsimple.HelpFormatter; import joptsimple.OptionDescriptor; import joptsimple.OptionException; import joptsimple.OptionParser; import net.sourceforge.seqware.common.metadata.Metadata; import net.sourceforge.seqware.common.module.ReturnValue; import net.sourceforge.seqware.common.util.Log; import net.sourceforge.seqware.pipeline.module.Module; import net.sourceforge.seqware.pipeline.module.ModuleInterface; import net.sourceforge.seqware.pipeline.plugin.Plugin; import net.sourceforge.seqware.pipeline.plugin.PluginInterface; import org.openide.util.Lookup; import org.openide.util.lookup.ServiceProvider; /** * <p> * This plugin outputs documentation for our plugins and modules in markdown format * </p> * * @author dyuen ProviderFor(PluginInterface.class) * @version $Id: $Id */ @ServiceProvider(service = PluginInterface.class) public class MarkdownPlugin extends Plugin { /** * <p> * Constructor for HelloWorld. * </p> */ public MarkdownPlugin() { super(); parser.acceptsAll(Arrays.asList("skip", "s"), "Optional: comma separated list of module/plugin names to skip").requiresArgument(); parser.acceptsAll(Arrays.asList("modules", "m"), "Optional: if provided will list out modules instead of plugins."); parser.acceptsAll(Arrays.asList("help", "h", "?"), "Provides this help message."); } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#setConfig(java.util.Map) */ /** * {@inheritDoc} * * @param config */ @Override public void setConfig(Map<String, String> config) { /** * explicitly no nothing */ } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#setParams(java.util.List) */ /** * {@inheritDoc} * * @param params */ @Override public void setParams(List<String> params) { this.params = params.toArray(new String[params.size()]); } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#setMetadata(net.sourceforge.seqware.pipeline.metadata.Metadata) */ /** * {@inheritDoc} * * @param metadata */ @Override public void setMetadata(Metadata metadata) { // println("Setting Metadata: " + metadata); this.metadata = metadata; } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#get_syntax() */ /** * {@inheritDoc} * * @return */ @Override public String get_syntax() { try { parser.printHelpOn(System.err); } catch (IOException e) { // TODO Auto-generated catch block Log.fatal(e); } return (""); } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#parse_parameters() */ /** * {@inheritDoc} * * @return */ @Override public ReturnValue parse_parameters() { ReturnValue ret = new ReturnValue(); try { options = parser.parse(params); } catch (OptionException e) { get_syntax(); ret.setExitStatus(ReturnValue.INVALIDARGUMENT); } return ret; } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#init() */ /** * {@inheritDoc} * * @return */ @Override public ReturnValue init() { return new ReturnValue(); } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#do_test() */ /** * {@inheritDoc} * * @return */ @Override public ReturnValue do_test() { return new ReturnValue(); } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#do_run() */ /** * {@inheritDoc} * * @return */ @Override public ReturnValue do_run() { BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8)); Set<String> toSkip = new HashSet<>(); if (options.has("skip")) { Object valueOf = options.valuesOf("skip"); String[] vals = valueOf.toString().split(","); toSkip.addAll(Arrays.asList(vals)); } if (options.has("modules")) { Collection<ModuleInterface> mods; mods = (Collection<ModuleInterface>) Lookup.getDefault().lookupAll(ModuleInterface.class); List<ModuleInterface> modsList = new ArrayList<>(); modsList.addAll(mods); Collections.sort(modsList, new ModuleComparator()); handlePlugins(bufferedWriter, modsList, toSkip); } else { Collection<PluginInterface> mods; mods = (Collection<PluginInterface>) Lookup.getDefault().lookupAll(PluginInterface.class); List<PluginInterface> modsList = new ArrayList<>(); modsList.addAll(mods); Collections.sort(modsList, new PluginComparator()); handlePlugins(bufferedWriter, modsList, toSkip); } return new ReturnValue(); } private static Field getField(Class clazz, String fieldName) throws NoSuchFieldException { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { Class superClass = clazz.getSuperclass(); if (superClass == null) { throw e; } else { return getField(superClass, fieldName); } } } /* * (non-Javadoc) * * @see net.sourceforge.seqware.pipeline.plugin.PluginInterface#clean_up() */ /** * {@inheritDoc} * * @return */ @Override public ReturnValue clean_up() { return new ReturnValue(); } /** * <p> * get_description. * </p> * * @return a {@link java.lang.String} object. */ @Override public String get_description() { return ("A plugin that generates markdown documentation for all plugins."); } private void handlePlugins(BufferedWriter bufferedWriter, List<? extends Object> plugs, Set<String> skip) { writePageHeader(bufferedWriter, plugs); for (Object plug : plugs) { try { bufferedWriter.newLine(); if (plug instanceof PluginInterface) { writePluginDescription(bufferedWriter, plug, skip); } else if (plug instanceof Module) { writeModuleDescription(plug, bufferedWriter); } } catch (NoSuchFieldException | IllegalAccessException | IOException ex) { Log.fatal(ex, ex); } } } private void writePageHeader(BufferedWriter bufferedWriter, List<? extends Object> plugs) { try { bufferedWriter.append("---"); bufferedWriter.newLine(); bufferedWriter.append(""); bufferedWriter.newLine(); if (plugs.toArray()[0] instanceof PluginInterface) { bufferedWriter.append("title: \"Plugins\""); } else if (plugs.toArray()[0] instanceof Module) { bufferedWriter.append("title: \"Modules\""); } else { bufferedWriter.append("title: \"Unknown\""); } bufferedWriter.newLine(); bufferedWriter.append("toc_includes_sections: true"); bufferedWriter.newLine(); bufferedWriter.append("markdown: advanced"); bufferedWriter.newLine(); bufferedWriter.append(""); bufferedWriter.newLine(); bufferedWriter.append("---"); bufferedWriter.newLine(); } catch (IOException ex) { Log.fatal(ex, ex); } } private void writePluginDescription(BufferedWriter bufferedWriter, Object plug, Set<String> skip) throws IllegalAccessException, SecurityException, IOException, IllegalArgumentException, NoSuchFieldException { // check for skipping final String simpleName = plug.getClass().getSimpleName(); if (skip.contains(simpleName)) { return; } bufferedWriter.newLine(); bufferedWriter.append("## " + simpleName); bufferedWriter.newLine(); bufferedWriter.append(plug.getClass().getPackage().getName() + "." + simpleName); bufferedWriter.newLine(); bufferedWriter.append(((PluginInterface) plug).get_description()); bufferedWriter.newLine(); bufferedWriter.newLine(); Class myClass = plug.getClass(); Field myField = getField(myClass, "parser"); // required if field is not normally accessible myField.setAccessible(true); OptionParser get = (OptionParser) myField.get(plug); get.formatHelpWith(new MarkDownFormatter()); get.printHelpOn(bufferedWriter); } private void writeModuleDescription(Object plug, BufferedWriter bufferedWriter) throws SecurityException, IllegalAccessException, IOException, IllegalArgumentException { Module mod = (Module) plug; bufferedWriter.newLine(); bufferedWriter.append("## " + plug.getClass().getSimpleName()); bufferedWriter.newLine(); bufferedWriter.append(plug.getClass().getPackage().getName() + "." + plug.getClass().getSimpleName()); bufferedWriter.newLine(); String description = ""; try { ReturnValue init = mod.init(); description = init.getDescription() == null ? "" : init.getDescription(); } catch (Exception e) { Log.info("Could not print description for " + mod.getClass()); } bufferedWriter.append(description); bufferedWriter.newLine(); bufferedWriter.newLine(); Class myClass = plug.getClass(); try { Method getOptionParserMethod = myClass.getDeclaredMethod("getOptionParser"); getOptionParserMethod.setAccessible(true); Object invoke = getOptionParserMethod.invoke(plug, new Object[] {}); OptionParser get = (OptionParser) invoke; get.formatHelpWith(new MarkDownFormatter()); get.printHelpOn(bufferedWriter); } catch (InvocationTargetException ex) { Log.info("Could not retrieve OptionParser for " + mod.getClass()); } catch (NoSuchMethodException ex) { Log.info(ex, ex); } } public class MarkDownFormatter implements HelpFormatter { @Override public String format(Map<String, ? extends OptionDescriptor> options) { if (options.isEmpty()) { return ""; } StringBuilder buffer = new StringBuilder(); buffer.append("| Command-line option | Description |\n"); buffer.append("|--------------------|--------------|\n"); // not sure why options are reported once per Set<String> done = new HashSet<>(); for (Entry<String, ? extends OptionDescriptor> e : options.entrySet()) { if (done.contains(e.getValue().description())) { continue; } done.add(e.getValue().description()); buffer.append("|"); for (String o : e.getValue().options()) { buffer.append("--").append(o).append(", "); } buffer.deleteCharAt(buffer.length() - 1); buffer.deleteCharAt(buffer.length() - 1); buffer.append("|"); buffer.append(e.getValue().description()); buffer.append("|"); buffer.append("\n"); } return buffer.toString(); } } public static void main(String[] args) { MarkdownPlugin mp = new MarkdownPlugin(); mp.init(); List<String> arr = new ArrayList<>(); arr.add("-m"); mp.setParams(arr); mp.parse_parameters(); mp.do_run(); } public class ModuleComparator implements Comparator<ModuleInterface> { @Override public int compare(ModuleInterface t, ModuleInterface t1) { return (t.getClass().getSimpleName().compareTo(t1.getClass().getSimpleName())); } } public class PluginComparator implements Comparator<PluginInterface> { @Override public int compare(PluginInterface t, PluginInterface t1) { return (t.getClass().getSimpleName().compareTo(t1.getClass().getSimpleName())); } } }