* [JGIT PATCH 0/9] List commonly used (or recognized) commands @ 2008-07-25 19:45 Shawn O. Pearce 2008-07-25 19:45 ` [JGIT PATCH 1/9] Switch jgit.pgm to J2SE-1.5 execution environment Shawn O. Pearce 2008-07-25 19:52 ` [JGIT PATCH 0/9] List commonly used (or recognized) commands Shawn O. Pearce 0 siblings, 2 replies; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:45 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git This series adds support to jgit to list commonly used subcommands if the user just executes `jgit` with no subcommand requested: $ jgit jgit --git-dir GIT_DIR --help (-h) --show-stack-trace command [ARG ...] The most commonly used commands are: fetch Update remote refs from another repository log View commit history push Update remote repository from local refs tag Create a tag Commands inside of the pgm.debug package are automatically given the debug- prefix, allowing debug-show-commands to be used to show the command table. Commands must be listed in the META-INF/services/org...TextBuiltin file in order to be considered for execution. This means that we must add (or remove) command class names from the listing each time we introduce or remove a command line subcommand. One advantage to this structure is additional commands can defined in other packages, and are available so long as the classes are reachable through the CLASSPATH. Since jgit.sh hardcodes the CLASSPATH to only itself this is not fully supported yet, but does open the door for users to extend jgit's command line support. Shawn O. Pearce (9): Switch jgit.pgm to J2SE-1.5 execution environment Remove unnecessary duplicate if (help) test inside TextBuiltin Create an optional documentation annotation for TextBuiltin Create a lightweight registration wrapper for TextBuiltin Create a catalog of CommandRefs for lookup and enumeration Document some common commands with the new Command annotation Include commonly used commands in main help output Refactor SubcommandHandler to use CommandCatalog instead of reflection Add debug-show-commands to display the command table org.spearce.jgit.pgm/.classpath | 2 +- .../services/org.spearce.jgit.pgm.TextBuiltin | 14 ++ .../src/org/spearce/jgit/pgm/Command.java | 72 ++++++++ .../src/org/spearce/jgit/pgm/CommandCatalog.java | 188 ++++++++++++++++++++ .../src/org/spearce/jgit/pgm/CommandRef.java | 158 ++++++++++++++++ .../src/org/spearce/jgit/pgm/Fetch.java | 1 + .../src/org/spearce/jgit/pgm/Log.java | 1 + .../src/org/spearce/jgit/pgm/Main.java | 18 ++ .../src/org/spearce/jgit/pgm/Push.java | 1 + .../src/org/spearce/jgit/pgm/Tag.java | 1 + .../src/org/spearce/jgit/pgm/TextBuiltin.java | 17 +-- .../org/spearce/jgit/pgm/debug/ShowCommands.java | 78 ++++++++ .../spearce/jgit/pgm/opt/SubcommandHandler.java | 65 +------ 13 files changed, 543 insertions(+), 73 deletions(-) create mode 100644 org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java ^ permalink raw reply [flat|nested] 12+ messages in thread
* [JGIT PATCH 1/9] Switch jgit.pgm to J2SE-1.5 execution environment 2008-07-25 19:45 [JGIT PATCH 0/9] List commonly used (or recognized) commands Shawn O. Pearce @ 2008-07-25 19:45 ` Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 2/9] Remove unnecessary duplicate if (help) test inside TextBuiltin Shawn O. Pearce 2008-07-25 19:52 ` [JGIT PATCH 0/9] List commonly used (or recognized) commands Shawn O. Pearce 1 sibling, 1 reply; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:45 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git We have been keeping the jgit library itself on Java 5, and the jgit command line tools should also be on Java 5 and avoid using any Java 6 APIs (for now). Not all of our target platforms have a Java 6 virtual machine available out of the box. Since the pgm project broke out of the library project our code already conforms to Java 5 APIs, so we just have to switch the build path settings. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- org.spearce.jgit.pgm/.classpath | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/org.spearce.jgit.pgm/.classpath b/org.spearce.jgit.pgm/.classpath index cc861d2..50dd6d3 100644 --- a/org.spearce.jgit.pgm/.classpath +++ b/org.spearce.jgit.pgm/.classpath @@ -2,7 +2,7 @@ <classpath> <classpathentry kind="src" path="src"/> <classpathentry exported="true" kind="lib" path="lib/args4j-2.0.9.jar" sourcepath="lib/args4j-2.0.9.zip"/> - <classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> <classpathentry combineaccessrules="false" exported="true" kind="src" path="/org.spearce.jgit"/> <classpathentry kind="output" path="bin"/> </classpath> -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 2/9] Remove unnecessary duplicate if (help) test inside TextBuiltin 2008-07-25 19:45 ` [JGIT PATCH 1/9] Switch jgit.pgm to J2SE-1.5 execution environment Shawn O. Pearce @ 2008-07-25 19:46 ` Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 3/9] Create an optional documentation annotation for TextBuiltin Shawn O. Pearce 0 siblings, 1 reply; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:46 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git This was caused by a copy-and-paste error as I borrowed the global argument handling code in Main to help start writing the command specific argument handling in TextBuiltin. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- .../src/org/spearce/jgit/pgm/TextBuiltin.java | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-) diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java index d02a0a2..e2eef84 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java @@ -145,11 +145,10 @@ public abstract class TextBuiltin { clp.printSingleLineUsage(System.err); System.err.println(); - if (help) { - System.err.println(); - clp.printUsage(System.err); - System.err.println(); - } + System.err.println(); + clp.printUsage(System.err); + System.err.println(); + System.exit(1); } -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 3/9] Create an optional documentation annotation for TextBuiltin 2008-07-25 19:46 ` [JGIT PATCH 2/9] Remove unnecessary duplicate if (help) test inside TextBuiltin Shawn O. Pearce @ 2008-07-25 19:46 ` Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 4/9] Create a lightweight registration wrapper " Shawn O. Pearce 0 siblings, 1 reply; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:46 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git This new annotation can be used for automatic command list creation, or additional help generation by the default help generator. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- .../src/org/spearce/jgit/pgm/Command.java | 72 ++++++++++++++++++++ 1 files changed, 72 insertions(+), 0 deletions(-) create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java new file mode 100644 index 0000000..40be1f7 --- /dev/null +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Git Development Community nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spearce.jgit.pgm; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Annotation to document a {@link TextBuiltin}. + * <p> + * This is an optional annotation for TextBuiltin subclasses and it carries + * documentation forward into the runtime system describing what the command is + * and why users may want to invoke it. + */ +@Retention(RUNTIME) +@Target( { TYPE }) +public @interface Command { + /** + * @return name the command is invoked as from the command line. If the + * (default) empty string is supplied the name will be generated + * from the class name. + */ + public String name() default ""; + + /** + * @return one line description of the command's feature set. + */ + public String usage() default ""; + + /** + * @return true if this command is considered to be commonly used. + */ + public boolean common() default false; +} -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 4/9] Create a lightweight registration wrapper for TextBuiltin 2008-07-25 19:46 ` [JGIT PATCH 3/9] Create an optional documentation annotation for TextBuiltin Shawn O. Pearce @ 2008-07-25 19:46 ` Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 5/9] Create a catalog of CommandRefs for lookup and enumeration Shawn O. Pearce 0 siblings, 1 reply; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:46 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git Automatic command list generation requires knowing what commands are available to this runtime, and what name those commands can be called as by the end-user. This lightweight wrappers carries the data from the Command annotation, possibly filling in the name of the command by generating it from the implementation class name. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- .../src/org/spearce/jgit/pgm/CommandRef.java | 151 ++++++++++++++++++++ 1 files changed, 151 insertions(+), 0 deletions(-) create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java new file mode 100644 index 0000000..40400a7 --- /dev/null +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Git Development Community nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spearce.jgit.pgm; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * Description of a command (a {@link TextBuiltin} subclass. + * <p> + * These descriptions are lightweight compared to creating a command instance + * and are therefore suitable for catalogs of "known" commands without linking + * the command's implementation and creating a dummy instance of the command. + */ +public class CommandRef { + private final Class<? extends TextBuiltin> impl; + + private final String name; + + private String usage; + + boolean common; + + CommandRef(final Class<? extends TextBuiltin> clazz) { + this(clazz, guessName(clazz)); + } + + CommandRef(final Class<? extends TextBuiltin> clazz, final Command cmd) { + this(clazz, cmd.name().length() > 0 ? cmd.name() : guessName(clazz)); + usage = cmd.usage(); + common = cmd.common(); + } + + private CommandRef(final Class<? extends TextBuiltin> clazz, final String cn) { + impl = clazz; + name = cn; + usage = ""; + } + + private static String guessName(final Class<? extends TextBuiltin> clazz) { + final StringBuilder s = new StringBuilder(); + if (clazz.getName().startsWith("org.spearce.jgit.pgm.debug.")) + s.append("debug-"); + + boolean lastWasDash = true; + for (final char c : clazz.getSimpleName().toCharArray()) { + if (Character.isUpperCase(c)) { + if (!lastWasDash) + s.append('-'); + lastWasDash = !lastWasDash; + s.append(Character.toLowerCase(c)); + } else { + s.append(c); + } + } + return s.toString(); + } + + /** + * @return name the command is invoked as from the command line. + */ + public String getName() { + return name; + } + + /** + * @return one line description of the command's feature set. + */ + public String getUsage() { + return usage; + } + + /** + * @return true if this command is considered to be commonly used. + */ + public boolean isCommon() { + return common; + } + + /** + * @return name of the Java class which implements this command. + */ + public String getImplementationClassName() { + return impl.getName(); + } + + /** + * @return a new instance of the command implementation. + */ + public TextBuiltin create() { + final Constructor<? extends TextBuiltin> c; + try { + c = impl.getDeclaredConstructor(); + } catch (SecurityException e) { + throw new RuntimeException("Cannot create command " + getName(), e); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Cannot create command " + getName(), e); + } + c.setAccessible(true); + + final TextBuiltin r; + try { + r = c.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException("Cannot create command " + getName(), e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot create command " + getName(), e); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Cannot create command " + getName(), e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Cannot create command " + getName(), e); + } + r.setCommandName(getName()); + return r; + } +} -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 5/9] Create a catalog of CommandRefs for lookup and enumeration 2008-07-25 19:46 ` [JGIT PATCH 4/9] Create a lightweight registration wrapper " Shawn O. Pearce @ 2008-07-25 19:46 ` Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 6/9] Document some common commands with the new Command annotation Shawn O. Pearce 2008-07-25 20:02 ` [JGIT PATCH 5/9 v2] Create a catalog of CommandRefs for lookup and enumeration Shawn O. Pearce 0 siblings, 2 replies; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:46 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git The command catalog supports enumerating commands registered through a services list, converting each entry into a CommandRef and making that available to callers on demand. The CommandRef can be later used to create a command instance or just to obtain documentation about it. All current commands are listed in the service registration. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- .../services/org.spearce.jgit.pgm.TextBuiltin | 12 ++ .../src/org/spearce/jgit/pgm/CommandCatalog.java | 188 ++++++++++++++++++++ 2 files changed, 200 insertions(+), 0 deletions(-) create mode 100644 org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java diff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin new file mode 100644 index 0000000..69ef046 --- /dev/null +++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin @@ -0,0 +1,12 @@ +org.spearce.jgit.pgm.DiffTree +org.spearce.jgit.pgm.Fetch +org.spearce.jgit.pgm.Glog +org.spearce.jgit.pgm.IndexPack +org.spearce.jgit.pgm.Log +org.spearce.jgit.pgm.LsRemote +org.spearce.jgit.pgm.LsTree +org.spearce.jgit.pgm.MergeBase +org.spearce.jgit.pgm.Push +org.spearce.jgit.pgm.RevList +org.spearce.jgit.pgm.ShowRev +org.spearce.jgit.pgm.Tag diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java new file mode 100644 index 0000000..13c585c --- /dev/null +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Git Development Community nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spearce.jgit.pgm; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +/** + * List of all commands known by jgit's command line tools. + * <p> + * Commands are implementations of {@link TextBuiltin}, with an optional + * {@link Command} class annotation to insert additional documentation or + * override the default command name (which is guessed from the class name). + * <p> + * Commands may be registered by adding them to a services file in the same JAR + * (or classes directory) as the command implementation. The service file name + * is <code>META-INF/services/org.spearce.jgit.pgm.TextBuiltin</code> and it + * contains one concrete implementation class name per line. + * <p> + * Command registration is identical to Java 6's services, however the catalog + * uses a lightweight wrapper to delay creating a command instance as much as + * possible. This avoids initializing the AWT or SWT GUI toolkits even if the + * command's constructor might require them. + */ +public class CommandCatalog { + private static final CommandCatalog INSTANCE = new CommandCatalog(); + + /** + * Locate a single command by its user friendly name. + * + * @param name + * name of the command. Typically in dash-lower-case-form, which + * was derived from the DashLowerCaseForm class name. + * @return the command instance; null if no command exists by that name. + */ + public static CommandRef get(final String name) { + return INSTANCE.commands.get(name); + } + + /** + * @return all known commands, sorted by command name. + */ + public static CommandRef[] all() { + return toSortedArray(INSTANCE.commands.values()); + } + + /** + * @return all common commands, sorted by command name. + */ + public static CommandRef[] common() { + final ArrayList<CommandRef> common = new ArrayList<CommandRef>(); + for (final CommandRef c : INSTANCE.commands.values()) + if (c.isCommon()) + common.add(c); + return toSortedArray(common); + } + + private static CommandRef[] toSortedArray(final Collection<CommandRef> c) { + final CommandRef[] r = c.toArray(new CommandRef[c.size()]); + Arrays.sort(r, new Comparator<CommandRef>() { + public int compare(final CommandRef o1, final CommandRef o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + return r; + } + + private final ClassLoader ldr; + + private final Map<String, CommandRef> commands; + + private CommandCatalog() { + ldr = Thread.currentThread().getContextClassLoader(); + commands = new HashMap<String, CommandRef>(); + + final Enumeration<URL> catalogs = catalogs(); + while (catalogs.hasMoreElements()) + scan(catalogs.nextElement()); + } + + private Enumeration<URL> catalogs() { + try { + final String pfx = "META-INF/services/"; + return ldr.getResources(pfx + TextBuiltin.class.getName()); + } catch (IOException err) { + return new Vector<URL>().elements(); + } + } + + private void scan(final URL cUrl) { + final BufferedReader cIn; + try { + final InputStream in = cUrl.openStream(); + cIn = new BufferedReader(new InputStreamReader(in, "UTF-8")); + } catch (IOException err) { + // If we cannot read from the service list, go to the next. + // + return; + } + + try { + String line; + while ((line = cIn.readLine()) != null) { + if (line.length() > 0 && !line.startsWith("#")) + load(line); + } + } catch (IOException err) { + // If we failed during a read, ignore the error. + // + } finally { + try { + cIn.close(); + } catch (IOException e) { + // Ignore the close error; we are only reading. + } + } + } + + private void load(final String cn) { + final Class<? extends TextBuiltin> clazz; + try { + clazz = Class.forName(cn, false, ldr).asSubclass(TextBuiltin.class); + } catch (ClassNotFoundException notBuiltin) { + // Doesn't exist, even though the service entry is present. + // + return; + } catch (ClassCastException notBuiltin) { + // Isn't really a builtin, even though its listed as such. + // + return; + } + + final CommandRef cr; + final Command a = clazz.getAnnotation(Command.class); + if (a != null) + cr = new CommandRef(clazz, a); + else + cr = new CommandRef(clazz); + + commands.put(cr.getName(), cr); + } +} -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 6/9] Document some common commands with the new Command annotation 2008-07-25 19:46 ` [JGIT PATCH 5/9] Create a catalog of CommandRefs for lookup and enumeration Shawn O. Pearce @ 2008-07-25 19:46 ` Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 7/9] Include commonly used commands in main help output Shawn O. Pearce 2008-07-25 20:02 ` [JGIT PATCH 5/9 v2] Create a catalog of CommandRefs for lookup and enumeration Shawn O. Pearce 1 sibling, 1 reply; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:46 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git This way they are known to be common at runtime, by looking at the annotation associated with the class instance. Right now we do not use these annotations but they will be useful soon. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- .../src/org/spearce/jgit/pgm/Fetch.java | 1 + .../src/org/spearce/jgit/pgm/Log.java | 1 + .../src/org/spearce/jgit/pgm/Push.java | 1 + .../src/org/spearce/jgit/pgm/Tag.java | 1 + 4 files changed, 4 insertions(+), 0 deletions(-) diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java index 194f669..dcad972 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java @@ -47,6 +47,7 @@ import org.spearce.jgit.transport.RefSpec; import org.spearce.jgit.transport.TrackingRefUpdate; import org.spearce.jgit.transport.Transport; +@Command(common = true, usage = "Update remote refs from another repository") class Fetch extends TextBuiltin { @Argument(index = 0, metaVar = "uri-ish") private String remote = "origin"; diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Log.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Log.java index 780a63b..e16387b 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Log.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Log.java @@ -45,6 +45,7 @@ import java.util.TimeZone; import org.spearce.jgit.lib.PersonIdent; import org.spearce.jgit.revwalk.RevCommit; +@Command(common = true, usage = "View commit history") class Log extends RevWalkTextBuiltin { private final TimeZone myTZ = TimeZone.getDefault(); diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java index df6c664..6b35ab8 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java @@ -51,6 +51,7 @@ import org.spearce.jgit.transport.RemoteRefUpdate; import org.spearce.jgit.transport.Transport; import org.spearce.jgit.transport.RemoteRefUpdate.Status; +@Command(common = true, usage = "Update remote repository from local refs") class Push extends TextBuiltin { @Argument(index = 0, metaVar = "uri-ish") private String remote = "origin"; diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Tag.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Tag.java index a7bd037..6c73552 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Tag.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Tag.java @@ -45,6 +45,7 @@ import org.spearce.jgit.lib.ObjectId; import org.spearce.jgit.lib.ObjectLoader; import org.spearce.jgit.lib.PersonIdent; +@Command(common = true, usage = "Create a tag") class Tag extends TextBuiltin { @Option(name = "-f", usage = "force replacing an existing tag") private boolean force; -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 7/9] Include commonly used commands in main help output 2008-07-25 19:46 ` [JGIT PATCH 6/9] Document some common commands with the new Command annotation Shawn O. Pearce @ 2008-07-25 19:46 ` Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 8/9] Refactor SubcommandHandler to use CommandCatalog instead of reflection Shawn O. Pearce 0 siblings, 1 reply; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:46 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git If the main loop did not get a subcommand during parsing of the command line then we should offer up a list of commonly used commands and their one-line usage summary, to help the user make a decision about which command they should try to execute. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- .../src/org/spearce/jgit/pgm/Main.java | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Main.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Main.java index c069989..c8bade8 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Main.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Main.java @@ -121,6 +121,24 @@ public class Main { System.err.println(); clp.printUsage(System.err); System.err.println(); + } else if (subcommand == null) { + System.err.println(); + System.err.println("The most commonly used commands are:"); + final CommandRef[] common = CommandCatalog.common(); + int width = 0; + for (final CommandRef c : common) + width = Math.max(width, c.getName().length()); + width += 2; + + for (final CommandRef c : common) { + System.err.print(' '); + System.err.print(c.getName()); + for (int i = c.getName().length(); i < width; i++) + System.err.print(' '); + System.err.print(c.getUsage()); + System.err.println(); + } + System.err.println(); } System.exit(1); } -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 8/9] Refactor SubcommandHandler to use CommandCatalog instead of reflection 2008-07-25 19:46 ` [JGIT PATCH 7/9] Include commonly used commands in main help output Shawn O. Pearce @ 2008-07-25 19:46 ` Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 9/9] Add debug-show-commands to display the command table Shawn O. Pearce 0 siblings, 1 reply; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:46 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git Now that all commands are known to the CommandCatalog we do not need to perform direct reflection inside of the SubcommandHandler. Instead we can reuse the lookup table already known to the CommandCatalog. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- .../src/org/spearce/jgit/pgm/TextBuiltin.java | 8 +-- .../spearce/jgit/pgm/opt/SubcommandHandler.java | 65 ++------------------ 2 files changed, 6 insertions(+), 67 deletions(-) diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java index e2eef84..5c066cb 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java @@ -83,13 +83,7 @@ public abstract class TextBuiltin { /** RevWalk used during command line parsing, if it was required. */ protected RevWalk argWalk; - /** - * Set the name this command can be invoked as on the command line. - * - * @param name - * the name of the command. - */ - public void setCommandName(final String name) { + final void setCommandName(final String name) { commandName = name; } diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/SubcommandHandler.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/SubcommandHandler.java index c7e1bf6..86004bb 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/SubcommandHandler.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/SubcommandHandler.java @@ -37,8 +37,6 @@ package org.spearce.jgit.pgm.opt; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.text.MessageFormat; import org.kohsuke.args4j.CmdLineException; @@ -47,7 +45,8 @@ import org.kohsuke.args4j.OptionDef; import org.kohsuke.args4j.spi.OptionHandler; import org.kohsuke.args4j.spi.Parameters; import org.kohsuke.args4j.spi.Setter; -import org.spearce.jgit.pgm.Main; +import org.spearce.jgit.pgm.CommandCatalog; +import org.spearce.jgit.pgm.CommandRef; import org.spearce.jgit.pgm.TextBuiltin; /** @@ -57,12 +56,6 @@ import org.spearce.jgit.pgm.TextBuiltin; * we can execute at runtime with the remaining arguments of the parser. */ public class SubcommandHandler extends OptionHandler<TextBuiltin> { - private static String mypackage() { - final String p = Main.class.getName(); - final int dot = p.lastIndexOf('.'); - return p.substring(0, dot); - } - /** * Create a new handler for the command name. * <p> @@ -80,65 +73,17 @@ public class SubcommandHandler extends OptionHandler<TextBuiltin> { @Override public int parseArguments(final Parameters params) throws CmdLineException { final String name = params.getParameter(0); - final StringBuilder s = new StringBuilder(); - s.append(mypackage()); - s.append('.'); - boolean upnext = true; - for (int i = 0; i < name.length(); i++) { - final char c = name.charAt(i); - if (c == '-') { - upnext = true; - continue; - } - if (upnext) - s.append(Character.toUpperCase(c)); - else - s.append(c); - upnext = false; - } - - final Class<?> clazz; - try { - clazz = Class.forName(s.toString()); - } catch (ClassNotFoundException e) { - throw new CmdLineException(MessageFormat.format( - "{0} is not a jgit command", name)); - } - - if (!TextBuiltin.class.isAssignableFrom(clazz)) + final CommandRef cr = CommandCatalog.get(name); + if (cr == null) throw new CmdLineException(MessageFormat.format( "{0} is not a jgit command", name)); - final Constructor<?> cons; - try { - cons = clazz.getDeclaredConstructor(); - } catch (SecurityException e) { - throw new CmdLineException("Cannot create " + name, e); - } catch (NoSuchMethodException e) { - throw new CmdLineException("Cannot create " + name, e); - } - cons.setAccessible(true); - - final TextBuiltin cmd; - try { - cmd = (TextBuiltin) cons.newInstance(); - } catch (InstantiationException e) { - throw new CmdLineException("Cannot create " + name, e); - } catch (IllegalAccessException e) { - throw new CmdLineException("Cannot create " + name, e); - } catch (InvocationTargetException e) { - throw new CmdLineException("Cannot create " + name, e); - } - - cmd.setCommandName(name); - setter.addValue(cmd); - // Force option parsing to stop. Everything after us should // be arguments known only to this command and must not be // recognized by the current parser. // owner.stopOptionParsing(); - + setter.addValue(cr.create()); return 1; } -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 9/9] Add debug-show-commands to display the command table 2008-07-25 19:46 ` [JGIT PATCH 8/9] Refactor SubcommandHandler to use CommandCatalog instead of reflection Shawn O. Pearce @ 2008-07-25 19:46 ` Shawn O. Pearce 0 siblings, 0 replies; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:46 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git This can be useful to debug the command catalog. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- .../services/org.spearce.jgit.pgm.TextBuiltin | 2 + .../src/org/spearce/jgit/pgm/CommandRef.java | 7 ++ .../org/spearce/jgit/pgm/debug/ShowCommands.java | 78 ++++++++++++++++++++ 3 files changed, 87 insertions(+), 0 deletions(-) create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java diff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin index 69ef046..1ff5a30 100644 --- a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin +++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin @@ -10,3 +10,5 @@ org.spearce.jgit.pgm.Push org.spearce.jgit.pgm.RevList org.spearce.jgit.pgm.ShowRev org.spearce.jgit.pgm.Tag + +org.spearce.jgit.pgm.debug.ShowCommands diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java index 40400a7..8bf784b 100644 --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java @@ -120,6 +120,13 @@ public class CommandRef { } /** + * @return loader for {@link #getImplementationClassName()}. + */ + public ClassLoader getImplementationClassLoader() { + return impl.getClassLoader(); + } + + /** * @return a new instance of the command implementation. */ public TextBuiltin create() { diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java new file mode 100644 index 0000000..64ecf7f --- /dev/null +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java @@ -0,0 +1,78 @@ +package org.spearce.jgit.pgm.debug; + +import java.net.URL; + +import org.kohsuke.args4j.Option; +import org.spearce.jgit.pgm.Command; +import org.spearce.jgit.pgm.CommandCatalog; +import org.spearce.jgit.pgm.CommandRef; +import org.spearce.jgit.pgm.TextBuiltin; + +@Command(usage = "Display a list of all registered jgit commands") +class ShowCommands extends TextBuiltin { + @Option(name = "--pretty", usage = "alter the detail shown") + private Format pretty = Format.USAGE; + + @Override + protected void run() throws Exception { + final CommandRef[] list = CommandCatalog.all(); + + int width = 0; + for (final CommandRef c : list) + width = Math.max(width, c.getName().length()); + width += 2; + + for (final CommandRef c : list) { + System.err.print(c.isCommon() ? '*' : ' '); + System.err.print(' '); + + System.err.print(c.getName()); + for (int i = c.getName().length(); i < width; i++) + System.err.print(' '); + + pretty.print(c); + System.err.println(); + } + System.err.println(); + } + + static enum Format { + /** */ + USAGE { + void print(final CommandRef c) { + System.err.print(c.getUsage()); + } + }, + + /** */ + CLASSES { + void print(final CommandRef c) { + System.err.print(c.getImplementationClassName()); + } + }, + + /** */ + URLS { + void print(final CommandRef c) { + final ClassLoader ldr = c.getImplementationClassLoader(); + + String cn = c.getImplementationClassName(); + cn = cn.replace('.', '/') + ".class"; + + final URL url = ldr.getResource(cn); + if (url == null) { + System.err.print("!! NOT FOUND !!"); + return; + } + + String rn = url.toExternalForm(); + if (rn.endsWith(cn)) + rn = rn.substring(0, rn.length() - cn.length()); + + System.err.print(rn); + } + }; + + abstract void print(CommandRef c); + } +} -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [JGIT PATCH 5/9 v2] Create a catalog of CommandRefs for lookup and enumeration 2008-07-25 19:46 ` [JGIT PATCH 5/9] Create a catalog of CommandRefs for lookup and enumeration Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 6/9] Document some common commands with the new Command annotation Shawn O. Pearce @ 2008-07-25 20:02 ` Shawn O. Pearce 1 sibling, 0 replies; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 20:02 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git The command catalog supports enumerating commands registered through a services list, converting each entry into a CommandRef and making that available to callers on demand. The CommandRef can be later used to create a command instance or just to obtain documentation about it. All current commands are listed in the service registration. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> --- All that was missing was the services file entry in the built JAR. With this replacement for 5/9 the series should be fine. make_jgit.sh | 1 + .../services/org.spearce.jgit.pgm.TextBuiltin | 12 ++ .../src/org/spearce/jgit/pgm/CommandCatalog.java | 188 ++++++++++++++++++++ 3 files changed, 201 insertions(+), 0 deletions(-) create mode 100644 org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java diff --git a/make_jgit.sh b/make_jgit.sh index bcb2df0..89df80c 100755 --- a/make_jgit.sh +++ b/make_jgit.sh @@ -71,6 +71,7 @@ sed s/@@use_self@@/1/ jgit.sh >$O+ && java org.spearce.jgit.pgm.build.JarLinkUtil \ `for p in $JARS ; do printf %s " -include $p" ;done` \ `for p in $PLUGINS; do printf %s " -include $p/bin2";done` \ + -file META-INF/services/org.spearce.jgit.pgm.TextBuiltin=org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin \ -file META-INF/MANIFEST.MF=$T_MF \ >>$O+ && chmod 555 $O+ && diff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin new file mode 100644 index 0000000..69ef046 --- /dev/null +++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin @@ -0,0 +1,12 @@ +org.spearce.jgit.pgm.DiffTree +org.spearce.jgit.pgm.Fetch +org.spearce.jgit.pgm.Glog +org.spearce.jgit.pgm.IndexPack +org.spearce.jgit.pgm.Log +org.spearce.jgit.pgm.LsRemote +org.spearce.jgit.pgm.LsTree +org.spearce.jgit.pgm.MergeBase +org.spearce.jgit.pgm.Push +org.spearce.jgit.pgm.RevList +org.spearce.jgit.pgm.ShowRev +org.spearce.jgit.pgm.Tag diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java new file mode 100644 index 0000000..13c585c --- /dev/null +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Git Development Community nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spearce.jgit.pgm; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +/** + * List of all commands known by jgit's command line tools. + * <p> + * Commands are implementations of {@link TextBuiltin}, with an optional + * {@link Command} class annotation to insert additional documentation or + * override the default command name (which is guessed from the class name). + * <p> + * Commands may be registered by adding them to a services file in the same JAR + * (or classes directory) as the command implementation. The service file name + * is <code>META-INF/services/org.spearce.jgit.pgm.TextBuiltin</code> and it + * contains one concrete implementation class name per line. + * <p> + * Command registration is identical to Java 6's services, however the catalog + * uses a lightweight wrapper to delay creating a command instance as much as + * possible. This avoids initializing the AWT or SWT GUI toolkits even if the + * command's constructor might require them. + */ +public class CommandCatalog { + private static final CommandCatalog INSTANCE = new CommandCatalog(); + + /** + * Locate a single command by its user friendly name. + * + * @param name + * name of the command. Typically in dash-lower-case-form, which + * was derived from the DashLowerCaseForm class name. + * @return the command instance; null if no command exists by that name. + */ + public static CommandRef get(final String name) { + return INSTANCE.commands.get(name); + } + + /** + * @return all known commands, sorted by command name. + */ + public static CommandRef[] all() { + return toSortedArray(INSTANCE.commands.values()); + } + + /** + * @return all common commands, sorted by command name. + */ + public static CommandRef[] common() { + final ArrayList<CommandRef> common = new ArrayList<CommandRef>(); + for (final CommandRef c : INSTANCE.commands.values()) + if (c.isCommon()) + common.add(c); + return toSortedArray(common); + } + + private static CommandRef[] toSortedArray(final Collection<CommandRef> c) { + final CommandRef[] r = c.toArray(new CommandRef[c.size()]); + Arrays.sort(r, new Comparator<CommandRef>() { + public int compare(final CommandRef o1, final CommandRef o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + return r; + } + + private final ClassLoader ldr; + + private final Map<String, CommandRef> commands; + + private CommandCatalog() { + ldr = Thread.currentThread().getContextClassLoader(); + commands = new HashMap<String, CommandRef>(); + + final Enumeration<URL> catalogs = catalogs(); + while (catalogs.hasMoreElements()) + scan(catalogs.nextElement()); + } + + private Enumeration<URL> catalogs() { + try { + final String pfx = "META-INF/services/"; + return ldr.getResources(pfx + TextBuiltin.class.getName()); + } catch (IOException err) { + return new Vector<URL>().elements(); + } + } + + private void scan(final URL cUrl) { + final BufferedReader cIn; + try { + final InputStream in = cUrl.openStream(); + cIn = new BufferedReader(new InputStreamReader(in, "UTF-8")); + } catch (IOException err) { + // If we cannot read from the service list, go to the next. + // + return; + } + + try { + String line; + while ((line = cIn.readLine()) != null) { + if (line.length() > 0 && !line.startsWith("#")) + load(line); + } + } catch (IOException err) { + // If we failed during a read, ignore the error. + // + } finally { + try { + cIn.close(); + } catch (IOException e) { + // Ignore the close error; we are only reading. + } + } + } + + private void load(final String cn) { + final Class<? extends TextBuiltin> clazz; + try { + clazz = Class.forName(cn, false, ldr).asSubclass(TextBuiltin.class); + } catch (ClassNotFoundException notBuiltin) { + // Doesn't exist, even though the service entry is present. + // + return; + } catch (ClassCastException notBuiltin) { + // Isn't really a builtin, even though its listed as such. + // + return; + } + + final CommandRef cr; + final Command a = clazz.getAnnotation(Command.class); + if (a != null) + cr = new CommandRef(clazz, a); + else + cr = new CommandRef(clazz); + + commands.put(cr.getName(), cr); + } +} -- 1.6.0.rc0.182.gb96c7 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [JGIT PATCH 0/9] List commonly used (or recognized) commands 2008-07-25 19:45 [JGIT PATCH 0/9] List commonly used (or recognized) commands Shawn O. Pearce 2008-07-25 19:45 ` [JGIT PATCH 1/9] Switch jgit.pgm to J2SE-1.5 execution environment Shawn O. Pearce @ 2008-07-25 19:52 ` Shawn O. Pearce 1 sibling, 0 replies; 12+ messages in thread From: Shawn O. Pearce @ 2008-07-25 19:52 UTC (permalink / raw) To: Robin Rosenberg; +Cc: git "Shawn O. Pearce" <spearce@spearce.org> wrote: > This series adds support to jgit to list commonly used subcommands > if the user just executes `jgit` with no subcommand requested: > > $ jgit > jgit --git-dir GIT_DIR --help (-h) --show-stack-trace command [ARG ...] > > The most commonly used commands are: > fetch Update remote refs from another repository > log View commit history > push Update remote repository from local refs > tag Create a tag Scratch that. This series is busted if you install jgit and actually try to use it. No subcommands get registered. I suspect it is due to the shell script+ZIP file we have in the CLASSPATH confusing the JRE and making it impossible to read correctly. -- Shawn. ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2008-07-25 20:03 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-07-25 19:45 [JGIT PATCH 0/9] List commonly used (or recognized) commands Shawn O. Pearce 2008-07-25 19:45 ` [JGIT PATCH 1/9] Switch jgit.pgm to J2SE-1.5 execution environment Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 2/9] Remove unnecessary duplicate if (help) test inside TextBuiltin Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 3/9] Create an optional documentation annotation for TextBuiltin Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 4/9] Create a lightweight registration wrapper " Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 5/9] Create a catalog of CommandRefs for lookup and enumeration Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 6/9] Document some common commands with the new Command annotation Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 7/9] Include commonly used commands in main help output Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 8/9] Refactor SubcommandHandler to use CommandCatalog instead of reflection Shawn O. Pearce 2008-07-25 19:46 ` [JGIT PATCH 9/9] Add debug-show-commands to display the command table Shawn O. Pearce 2008-07-25 20:02 ` [JGIT PATCH 5/9 v2] Create a catalog of CommandRefs for lookup and enumeration Shawn O. Pearce 2008-07-25 19:52 ` [JGIT PATCH 0/9] List commonly used (or recognized) commands Shawn O. Pearce
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).