package com.lyndir.omicron.cli.command; import com.google.common.base.Throwables; import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.omicron.cli.OmicronCLI; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import org.reflections.Reflections; import org.slf4j.IMarkerFactory; import org.slf4j.helpers.BasicMarkerFactory; /** * <i>10 07, 2012</i> * * @author lhunath */ public abstract class Command { static final Logger logger = Logger.get( Command.class ); static final Reflections packageReflections = new Reflections( Command.class.getPackage().getName() ); private static final IMarkerFactory markers = new BasicMarkerFactory(); private final OmicronCLI omicron; protected Command(final OmicronCLI omicron) { this.omicron = omicron; } /** * Evaluate the given tokens in the context of this command. * * @param tokens The tokens given to this command in order to define how it should operate. */ public void evaluate(final Iterator<String> tokens) { if (!tokens.hasNext()) { self(); return; } String subCommand = tokens.next(); // Find the sub command to invoke by looking at our methods. for (final Method method : getClass().getMethods()) { SubCommand annotation = method.getAnnotation( SubCommand.class ); if (annotation != null) if (method.getName().equals( subCommand ) || annotation.abbr().equals( subCommand )) { try { if (method.getParameterTypes().length == 0) method.invoke( this ); else method.invoke( this, tokens ); } catch (IllegalAccessException | InvocationTargetException e) { throw logger.bug( e ); } return; } } // Find the sub command to invoke by looking at other Command classes. for (final Class<? extends Command> commandGroup : packageReflections.getSubTypesOf( Command.class )) { CommandGroup annotation = commandGroup.getAnnotation( CommandGroup.class ); if (annotation.parent() == getClass() && annotation.name().equals( subCommand ) || annotation.abbr().equals( subCommand )) try { commandGroup.getConstructor( OmicronCLI.class ).newInstance( omicron ).evaluate( tokens ); return; } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw Throwables.propagate( e ); } } err( "Don't know how to handle: %s", subCommand ); } protected void self() { err( "Missing sub command." ); help(); } public OmicronCLI getOmicron() { return omicron; } protected void dbg(final String format, final Object... args) { logger.dbg( markers.getMarker( commandPrefix() ), null, format, args ); } protected void inf(final String format, final Object... args) { logger.inf( markers.getMarker( commandPrefix() ), null, format, args ); } protected void err(final String format, final Object... args) { logger.err( markers.getMarker( commandPrefix() ), null, format, args ); } private String commandPrefix() { CommandGroup commandGroup = getClass().getAnnotation( CommandGroup.class ); return commandGroup.name() + (commandGroup.name().isEmpty()? "": ": "); } @SubCommand(abbr = "h", desc = "Enumerate all the sub commands of this command.") public void help() { inf( "Available sub commands are:" ); enumerateSubCommands(); } private void enumerateSubCommands() { SortedMap<String, String> commandDescriptions = new TreeMap<>(); for (final Method method : getClass().getMethods()) { SubCommand annotation = method.getAnnotation( SubCommand.class ); if (annotation == null) // Not a sub command. continue; if ("help".equals( method.getName() )) // Hide special commands. continue; commandDescriptions.put( method.getName(), annotation.desc() ); } for (final Class<? extends Command> commandGroup : packageReflections.getSubTypesOf( Command.class )) { CommandGroup annotation = commandGroup.getAnnotation( CommandGroup.class ); if (annotation.parent() != getClass()) // Only show help for sub commands of this command. continue; if (annotation.name().isEmpty() || "help".equals( annotation.name() )) // Hide special commands. continue; // Figure out the full name for this command group. commandDescriptions.put( String.format( "%s/%s", annotation.name(), annotation.abbr() ), annotation.desc() ); } for (final Map.Entry<String, String> commandDescriptionEntry : commandDescriptions.entrySet()) inf( " %s: %s", commandDescriptionEntry.getKey(), commandDescriptionEntry.getValue() ); } }