You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/03/05 16:08:27 UTC
[05/10] git commit: [KARAF-2805] Clean console and commands model
[KARAF-2805] Clean console and commands model
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/e7d23bef
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/e7d23bef
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/e7d23bef
Branch: refs/heads/master
Commit: e7d23bef3856780ab07aa813f8feea56572eb2f2
Parents: bd56b49
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Wed Mar 5 14:39:38 2014 +0100
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Wed Mar 5 15:10:37 2014 +0100
----------------------------------------------------------------------
pom.xml | 5 +
shell/core/NOTICE | 71 +++
shell/core/pom.xml | 179 ++++++
.../karaf/shell/console/branding-ssh.properties | 34 ++
.../karaf/shell/console/branding.properties | 33 ++
.../apache/karaf/shell/api/action/Action.java | 56 ++
.../apache/karaf/shell/api/action/Argument.java | 73 +++
.../apache/karaf/shell/api/action/Command.java | 61 ++
.../karaf/shell/api/action/Completion.java | 59 ++
.../apache/karaf/shell/api/action/Option.java | 71 +++
.../shell/api/action/lifecycle/Destroy.java | 34 ++
.../karaf/shell/api/action/lifecycle/Init.java | 34 ++
.../shell/api/action/lifecycle/Manager.java | 49 ++
.../shell/api/action/lifecycle/Reference.java | 39 ++
.../shell/api/action/lifecycle/Service.java | 36 ++
.../apache/karaf/shell/api/console/Command.java | 51 ++
.../karaf/shell/api/console/CommandLine.java | 61 ++
.../karaf/shell/api/console/Completer.java | 38 ++
.../karaf/shell/api/console/Function.java | 38 ++
.../apache/karaf/shell/api/console/History.java | 45 ++
.../karaf/shell/api/console/Registry.java | 77 +++
.../apache/karaf/shell/api/console/Session.java | 153 +++++
.../karaf/shell/api/console/SessionFactory.java | 65 +++
.../karaf/shell/api/console/Terminal.java | 51 ++
.../impl/action/command/ActionCommand.java | 109 ++++
.../impl/action/command/ArgumentCompleter.java | 385 ++++++++++++
.../action/command/DefaultActionPreparator.java | 481 +++++++++++++++
.../shell/impl/action/command/HelpOption.java | 57 ++
.../shell/impl/action/command/ManagerImpl.java | 168 ++++++
.../shell/impl/action/osgi/CommandExtender.java | 94 +++
.../impl/action/osgi/CommandExtension.java | 202 +++++++
.../impl/action/osgi/MultiServiceTracker.java | 106 ++++
.../shell/impl/action/osgi/RegistryImpl.java | 159 +++++
.../shell/impl/action/osgi/Satisfiable.java | 30 +
.../impl/action/osgi/SingleServiceTracker.java | 168 ++++++
.../karaf/shell/impl/console/Branding.java | 72 +++
.../impl/console/CommandNamesCompleter.java | 47 ++
.../shell/impl/console/CommandWrapper.java | 61 ++
.../shell/impl/console/CommandsCompleter.java | 256 ++++++++
.../impl/console/CompleterAsCompletor.java | 41 ++
.../shell/impl/console/ConsoleSessionImpl.java | 583 +++++++++++++++++++
.../shell/impl/console/HeadlessSessionImpl.java | 146 +++++
.../shell/impl/console/HistoryWrapper.java | 48 ++
.../karaf/shell/impl/console/JLineTerminal.java | 62 ++
.../shell/impl/console/KarafFileHistory.java | 64 ++
.../karaf/shell/impl/console/KarafTerminal.java | 57 ++
.../karaf/shell/impl/console/RegistryImpl.java | 159 +++++
.../shell/impl/console/SessionFactoryImpl.java | 144 +++++
.../shell/impl/console/TerminalFactory.java | 47 ++
.../impl/console/commands/ExitCommand.java | 51 ++
.../impl/console/commands/SubShellCommand.java | 58 ++
.../impl/console/commands/TopLevelCommand.java | 86 +++
.../commands/help/CommandListHelpProvider.java | 117 ++++
.../impl/console/commands/help/HelpCommand.java | 225 +++++++
.../console/commands/help/HelpProvider.java | 27 +
.../commands/help/SimpleHelpProvider.java | 47 ++
.../help/SingleCommandHelpProvider.java | 54 ++
.../shell/impl/console/osgi/Activator.java | 78 +++
.../shell/impl/console/osgi/Converters.java | 279 +++++++++
.../shell/impl/console/osgi/DelayedStarted.java | 87 +++
.../impl/console/osgi/LocalConsoleManager.java | 133 +++++
.../shell/impl/console/osgi/StreamWrapUtil.java | 91 +++
.../console/osgi/secured/SecuredCommand.java | 85 +++
.../osgi/secured/SecuredSessionFactoryImpl.java | 239 ++++++++
.../osgi/secured/SingleServiceTracker.java | 171 ++++++
.../impl/console/parsing/CommandLineImpl.java | 91 +++
.../shell/impl/console/parsing/Parser.java | 396 +++++++++++++
.../shell/impl/console/standalone/Main.java | 278 +++++++++
.../karaf/shell/support/CommandException.java | 64 ++
.../karaf/shell/support/MultiException.java | 95 +++
.../apache/karaf/shell/support/NameScoping.java | 79 +++
.../apache/karaf/shell/support/ShellUtil.java | 214 +++++++
.../karaf/shell/support/ansi/SimpleAnsi.java | 30 +
.../support/completers/AggregateCompleter.java | 96 +++
.../completers/CommandNamesCompleter.java | 27 +
.../support/completers/CommandsCompleter.java | 27 +
.../shell/support/completers/FileCompleter.java | 147 +++++
.../shell/support/completers/NullCompleter.java | 34 ++
.../support/completers/StringsCompleter.java | 113 ++++
.../support/converter/DefaultConverter.java | 403 +++++++++++++
.../shell/support/converter/GenericType.java | 195 +++++++
.../shell/support/converter/ReifiedType.java | 116 ++++
.../karaf/shell/support/table/AnsiColumn.java | 54 ++
.../apache/karaf/shell/support/table/Col.java | 113 ++++
.../karaf/shell/support/table/HAlign.java | 71 +++
.../apache/karaf/shell/support/table/Row.java | 69 +++
.../karaf/shell/support/table/ShellTable.java | 143 +++++
.../karaf/shell/support/table/StringUtil.java | 48 ++
.../src/main/resources/OSGI-INF/bundle.info | 16 +
shell/pom.xml | 1 +
90 files changed, 9907 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index f505153..032442a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -659,6 +659,11 @@
<artifactId>org.apache.karaf.shell.table</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.karaf.jaas</groupId>
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/NOTICE
----------------------------------------------------------------------
diff --git a/shell/core/NOTICE b/shell/core/NOTICE
new file mode 100644
index 0000000..b70f1f9
--- /dev/null
+++ b/shell/core/NOTICE
@@ -0,0 +1,71 @@
+Apache Karaf
+Copyright 2010-2014 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2010).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+OW2 (http://www.ow2.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+OPS4J (http://www.ops4j.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Eclipse Foundation (http://www.eclipse.org/).
+Licensed under the EPL.
+
+This product includes software written by
+Antony Lesuisse.
+Licensed under Public Domain.
+
+
+II. Used Software
+
+This product uses software developed at
+FUSE Source (http://www.fusesource.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+AOP Alliance (http://aopalliance.sourceforge.net/).
+Licensed under the Public Domain.
+
+This product uses software developed at
+Tanuki Software (http://www.tanukisoftware.com/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+Jasypt (http://jasypt.sourceforge.net/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+JLine (http://jline.sourceforge.net).
+Licensed under the BSD License.
+
+This product uses software developed at
+SLF4J (http://www.slf4j.org/).
+Licensed under the MIT License.
+
+This product uses software developed at
+SpringSource (http://www.springsource.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+
+
+III. License Summary
+- Apache License 2.0
+- BSD License
+- EPL License
+- MIT License
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/pom.xml
----------------------------------------------------------------------
diff --git a/shell/core/pom.xml b/shell/core/pom.xml
new file mode 100644
index 0000000..c41b0b2
--- /dev/null
+++ b/shell/core/pom.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>shell</artifactId>
+ <version>3.1.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.karaf.shell.core</artifactId>
+ <packaging>bundle</packaging>
+ <name>Apache Karaf :: Shell :: Core</name>
+ <description>This bundle provides OSGi shell integration and console support.</description>
+
+ <properties>
+ <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>jline</groupId>
+ <artifactId>jline</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.jaas</groupId>
+ <artifactId>org.apache.karaf.jaas.modules</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.jaas</groupId>
+ <artifactId>org.apache.karaf.jaas.boot</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.aries.blueprint</groupId>
+ <artifactId>org.apache.aries.blueprint.api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.aries.blueprint</groupId>
+ <artifactId>org.apache.aries.blueprint.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.gogo.runtime</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.utils</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>org.apache.karaf.util</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.service</groupId>
+ <artifactId>org.apache.karaf.service.guard</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/../../client/src/main/key</directory>
+ <filtering>false</filtering>
+ </resource>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*.info</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${project.basedir}/src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>Main</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ org.osgi.service.event;resolution:=optional,
+ org.apache.karaf.branding;resolution:=optional,
+ *
+ </Import-Package>
+ <Export-Package>
+ org.apache.karaf.shell.api.*;version=${project.version},
+ org.apache.karaf.shell.support.*;version=${project.version},
+ </Export-Package>
+ <Private-Package>
+ org.apache.karaf.service.guard.tools,
+ org.apache.karaf.shell.impl.*,
+ org.apache.karaf.util.properties,
+ org.apache.felix.utils.extender,
+ org.apache.felix.utils.manifest,
+ org.apache.felix.gogo.api,
+ org.apache.felix.gogo.runtime,
+ org.apache.felix.gogo.runtime.threadio,
+ org.apache.felix.service.command,
+ org.apache.felix.service.threadio,
+ </Private-Package>
+ <Bundle-Activator>
+ org.apache.karaf.shell.impl.console.osgi.Activator
+ </Bundle-Activator>
+ <Main-Class>
+ org.apache.karaf.shell.impl.console.standalone.Main
+ </Main-Class>
+ </instructions>
+ <unpackBundle>true</unpackBundle>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
----------------------------------------------------------------------
diff --git a/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties b/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
new file mode 100644
index 0000000..978484b
--- /dev/null
+++ b/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
@@ -0,0 +1,34 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+welcome = \
+\u001B[36m __ __ ____ \u001B[0m\r\n\
+\u001B[36m / //_/____ __________ _/ __/ \u001B[0m\r\n\
+\u001B[36m / ,< / __ `/ ___/ __ `/ /_ \u001B[0m\r\n\
+\u001B[36m / /| |/ /_/ / / / /_/ / __/ \u001B[0m\r\n\
+\u001B[36m /_/ |_|\\__,_/_/ \\__,_/_/ \u001B[0m\r\n\
+\r\n\
+\u001B[1m Apache Karaf\u001B[0m (${project.version})\r\n\
+\r\n\
+Hit '\u001B[1m<tab>\u001B[0m' for a list of available commands\r\n\
+ and '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\r\n\
+Hit '\u001B[1msystem:shutdown\u001B[0m' to shutdown Karaf.\r\n\
+Hit '\u001B[1m<ctrl-d>\u001B[0m' or type '\u001B[1mlogout\u001B[0m' to disconnect shell from current session.\r\n
+
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
----------------------------------------------------------------------
diff --git a/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties b/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
new file mode 100644
index 0000000..3fbcb0a
--- /dev/null
+++ b/shell/core/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
@@ -0,0 +1,33 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+welcome = \
+\u001B[36m __ __ ____ \u001B[0m\r\n\
+\u001B[36m / //_/____ __________ _/ __/ \u001B[0m\r\n\
+\u001B[36m / ,< / __ `/ ___/ __ `/ /_ \u001B[0m\r\n\
+\u001B[36m / /| |/ /_/ / / / /_/ / __/ \u001B[0m\r\n\
+\u001B[36m /_/ |_|\\__,_/_/ \\__,_/_/ \u001B[0m\r\n\
+\r\n\
+\u001B[1m Apache Karaf\u001B[0m (${project.version})\r\n\
+\r\n\
+Hit '\u001B[1m<tab>\u001B[0m' for a list of available commands\r\n\
+ and '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\r\n\
+Hit '\u001B[1m<ctrl-d>\u001B[0m' or type '\u001B[1msystem:shutdown\u001B[0m' or '\u001B[1mlogout\u001B[0m' to shutdown Karaf.\r\n
+
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Action.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Action.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Action.java
new file mode 100644
index 0000000..955c837
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Action.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action;
+
+/**
+ * An action is the default implementation of the commands in karaf.
+ * In OSGi, Actions are discovered using an extender and a new instance
+ * of the class is created when the command is invoked, so that the
+ * implementation does not need to be thread safe.
+ *
+ * Before the call to the execute method the action is checked for
+ * fields annotated with @Reference and injected with services coming
+ * from the SessionFactory's Registry or from the OSGi registry.
+ * Methods annotated with @Init are then called. The next step is to
+ * inject command line parameters into fields annotated with @Option
+ * and @Argument and then call the execute method.
+ *
+ * Any class implementing Action must have a no argument constructor. This
+ * is necessary so the help generator can instantiate the class and get the
+ * default values.
+ *
+ * In order to make commands available from the non-OSGi shell,
+ * the commands must be listed in a file available at
+ * META-INF/services/org/apache/karaf/shell/commmands.
+ *
+ * @see org.apache.karaf.shell.api.action.Command
+ * @see org.apache.karaf.shell.api.action.lifecycle.Service
+ */
+public interface Action {
+
+ /**
+ * Execute the action which has been injected with services from the
+ * registry, options and arguments from the command line.
+ *
+ * @return <code>null</code> or the result of the action execution
+ * @throws Exception
+ */
+ Object execute() throws Exception;
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Argument.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Argument.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Argument.java
new file mode 100644
index 0000000..8cb1e51
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Argument.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Represents a positional argument on a command line (as opposed to an optional named {@link Option}
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Argument
+{
+ public static final String DEFAULT_STRING= "DEFAULT";
+
+ String DEFAULT = "##default";
+
+ /**
+ * Name of the argument.
+ * By default, the field name will be used.
+ */
+ String name() default DEFAULT;
+
+ /**
+ * A textual description of the argument.
+ */
+ String description() default "";
+
+ /**
+ * Whether this argument is mandatory or not.
+ */
+ boolean required() default false;
+
+ /**
+ * Position of the argument in the command line.
+ * When using multiple arguments, the indices must be
+ * starting from 0 and incrementing without any holes.
+ */
+ int index() default 0;
+
+ /**
+ * The last argument can be multi-valued in which case
+ * the field type must be a List.
+ */
+ boolean multiValued() default false;
+
+ /**
+ * The generated help displays default values for arguments.
+ * In case the value displayed in the help to the user should
+ * be different that the default value of the field, one
+ * can use this property to specify the value to display.
+ */
+ String valueToShowInHelp() default DEFAULT_STRING;
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Command.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Command.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Command.java
new file mode 100644
index 0000000..7622b33
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Command.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to denote a class represents a command which is executable
+ * within a shell/scope or as a command line process.
+ *
+ * All classes annotated with @Command should implement the
+ * {@link org.apache.karaf.shell.api.action.Action} interface.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Command
+{
+ /**
+ * Returns the scope or sub shell of the command
+ */
+ String scope();
+
+ /**
+ * Returns the name of the command if used inside a shell
+ */
+ String name();
+
+ /**
+ * Returns the description of the command which is used to generate command line help
+ */
+ String description() default "";
+
+ /**
+ * Returns a detailed description of the command.
+ * This description will be shown in the help for the command.
+ * Longer descriptions can be externalized using a
+ * <code>classpath:[location]</code> url, in which case the
+ * descrition will be loaded from the bundle at the given location,
+ * relatively to the implementation of the command.
+ */
+ String detailedDescription() default "";
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Completion.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Completion.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Completion.java
new file mode 100644
index 0000000..d9a3dca
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Completion.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.shell.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The @Completion annotation can be used on a field annotated with
+ * {@link Option} or {@link Argument} to specify the completion
+ * method to use for this field.
+ *
+ * @see org.apache.karaf.shell.api.console.Completer
+ * @see org.apache.karaf.shell.support.completers.StringsCompleter
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Completion {
+
+ /**
+ * The completer class to use for this field.
+ * The console registry will be used to look for
+ * a completer of this class.
+ *
+ * A special case for simple static completions is to use
+ * {@link org.apache.karaf.shell.support.completers.StringsCompleter},
+ * in which case, the <code>values</code> property will be used
+ * as the list of possible completions.
+ */
+ Class<?> value();
+
+ /**
+ * When using a static completer, returns the possible values.
+ */
+ String[] values() default { };
+
+ /**
+ * When using a static completer, indicates if completion
+ * should be done case sensitive or not.
+ */
+ boolean caseSensitive() default false;
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/Option.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Option.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Option.java
new file mode 100644
index 0000000..97e7557
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Option.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to mark an optional named command line option who's name typically starts with "--" or "-".
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Option
+{
+ public static final String DEFAULT_STRING= "DEFAULT";
+
+ /**
+ * The name of this option. Usually starting with a '-'.
+ */
+ String name();
+
+ /**
+ * Specify a list of aliases for this option.
+ * Useful when using an option with short or long names.
+ */
+ String[] aliases() default {};
+
+ /**
+ * A textual description of the option.
+ */
+ String description() default "";
+
+ /**
+ * Whether this argument is mandatory or not.
+ */
+ boolean required() default false;
+
+ /**
+ * The last argument can be multi-valued in which case
+ * the field type must be a List. On the command line,
+ * multi-valued options are used with specifying the option
+ * multiple times with different values.
+ */
+ boolean multiValued() default false;
+
+ /**
+ * The generated help displays default values for arguments.
+ * In case the value displayed in the help to the user should
+ * be different that the default value of the field, one
+ * can use this property to specify the value to display.
+ */
+ String valueToShowInHelp() default DEFAULT_STRING;
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Destroy.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Destroy.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Destroy.java
new file mode 100644
index 0000000..86199c0
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Destroy.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A class annotated with {@link @Service} can have a method
+ * annotation with <code>@Destroy</code> in which case the annotated
+ * method will be called when the object is destroyed.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Destroy {
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Init.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Init.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Init.java
new file mode 100644
index 0000000..d0cd3c0
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Init.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A class annotated with {@link @Service} can have a method
+ * annotation with <code>@Init</code> in which case the annotated
+ * method will be called after a successful injection.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Init {
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Manager.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Manager.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Manager.java
new file mode 100644
index 0000000..2b26e81
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Manager.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action.lifecycle;
+
+/**
+ * The <code>Manager</code> service can be used to programmatically
+ * register {@link org.apache.karaf.shell.api.action.Action}s or
+ * {@link org.apache.karaf.shell.api.console.Completer}s.
+ *
+ * Registered objects must be annotated with the {@link Service} annotation.
+ *
+ * Objects will be registered in the {@link org.apache.karaf.shell.api.console.Registry}
+ * associated with this <code>Manager</code>.
+ *
+ * @see org.apache.karaf.shell.api.console.Registry
+ * @see org.apache.karaf.shell.api.action.lifecycle.Service
+ */
+public interface Manager {
+
+ /**
+ * Register a service.
+ * If the given class is an {@link org.apache.karaf.shell.api.action.Action},
+ * a {@link org.apache.karaf.shell.api.console.Command} will be created and registered,
+ * else, an instance of the class will be created, injected and registered.
+ */
+ void register(Class<?> clazz);
+
+ /**
+ * Unregister a previously registered class.
+ */
+ void unregister(Class<?> clazz);
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Reference.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Reference.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Reference.java
new file mode 100644
index 0000000..4d8a73c
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Reference.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A class annotated with {@link @Service} can have fields
+ * annotated with <code>@Service</code> in which case matching
+ * services will be retrieved from the
+ * {@link org.apache.karaf.shell.api.console.Registry} and
+ * injected.
+ *
+ * If a field has a {@link java.util.List} type, it will be injected
+ * with a list containing all matching services.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Reference {
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Service.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Service.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Service.java
new file mode 100644
index 0000000..d5d62a3
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/lifecycle/Service.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.action.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Classes that need to be managed must be annotated
+ * with <code>@Service</code> annotation.
+ *
+ * Services can either implement {@link org.apache.karaf.shell.api.action.Action}
+ * or {@link org.apache.karaf.shell.api.console.Completer}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Service {
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
new file mode 100644
index 0000000..4bb1db0
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+/**
+ * A <code>Command</code> is a named
+ * {@link org.apache.karaf.shell.api.console.Function}
+ * which also provides completion.
+ */
+public interface Command extends Function {
+
+ /**
+ * Retrieve the scope of this command.
+ */
+ String getScope();
+
+ /**
+ * Retrieve the name of this command.
+ */
+ String getName();
+
+ /**
+ * Retrieve the description of this command.
+ * This short command description will be printed
+ * when using the <code>help</code> command.
+ */
+ String getDescription();
+
+ /**
+ * Retrieve the completer associated with this command.
+ *
+ * @param scoped whether the command is invoked from a subshell or not
+ * @return the {@link Completer} to use
+ */
+ Completer getCompleter(boolean scoped);
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/CommandLine.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/CommandLine.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/CommandLine.java
new file mode 100644
index 0000000..2ed7697
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/CommandLine.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+/**
+ * A <code>CommandLine</code> object will be created and
+ * given the {@link org.apache.karaf.shell.api.console.Completer}s to ease
+ * their work. Arguments are separated and the cursor position within the
+ * current argument is given.
+ */
+public interface CommandLine {
+
+ /**
+ * Retrieve the argument index for the cursor position
+ */
+ int getCursorArgumentIndex();
+
+ /**
+ * Retrieve the argument for the cursor position
+ */
+ String getCursorArgument();
+
+ /**
+ * Retrieve the position of the cursor within the argument
+ */
+ int getArgumentPosition();
+
+ /**
+ * List of arguments on the current command.
+ * If the command line contains multiple commands, only the command corresponding
+ * to the cursor position is available.
+ */
+ String[] getArguments();
+
+ /**
+ * Retrieve the position of the cursor within the command line.
+ */
+ int getBufferPosition();
+
+ /**
+ * Retrieve the full buffer.
+ */
+ String getBuffer();
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Completer.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Completer.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Completer.java
new file mode 100644
index 0000000..4f9f3d1
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Completer.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+import java.util.List;
+
+/**
+ * A <code>Completer</code> is used by the console to complete the command line.
+ */
+public interface Completer {
+
+ /**
+ * populate possible completion candidates.
+ *
+ * @param session the current {@link Session}
+ * @param commandLine the pre-parsed {@link CommandLine}
+ * @param candidates a list to fill with possible completion candidates
+ * @return the index of the{@link CommandLine} for which the completion will be relative
+ */
+ int complete(Session session, CommandLine commandLine, List<String> candidates);
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Function.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Function.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Function.java
new file mode 100644
index 0000000..209039f
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Function.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+import java.util.List;
+
+/**
+ * This interface represents some code that can be executed in the
+ * {@link Session}.
+ */
+public interface Function {
+
+ /**
+ * Execute this function within the given Session and with the given
+ * arguments.
+ *
+ * @param session the current session
+ * @param arguments the arguments of this function
+ * @return the result
+ * @throws Exception if any exception occurs
+ */
+ Object execute(Session session, List<Object> arguments) throws Exception;
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/History.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/History.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/History.java
new file mode 100644
index 0000000..10b1b11
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/History.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+/**
+ * Session history.
+ */
+public interface History {
+
+ /**
+ * First available index.
+ */
+ int first();
+
+ /**
+ * Last available index.
+ */
+ int last();
+
+ /**
+ * Command at the given index.
+ * Indices can range from <code>first()</code> to <code>last()</code>.
+ */
+ CharSequence get(int index);
+
+ /**
+ * Clear the history.
+ */
+ void clear();
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
new file mode 100644
index 0000000..b768d69
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Registry.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Service registry.
+ *
+ * The registry can be used to register various services used during injection along
+ * with {@link Command}s.
+ *
+ * @see org.apache.karaf.shell.api.console.SessionFactory
+ * @see org.apache.karaf.shell.api.console.Session
+ */
+public interface Registry {
+
+ /**
+ * Return a list of available commands.
+ */
+ List<Command> getCommands();
+
+ /**
+ * Register a delayed service (or factory).
+ * In cases where instances must be created for each injection,
+ * a {@link Callable} can be registered and each injection will
+ * call it to obtain the actual service implementation.
+ *
+ * @param factory
+ * @param clazz
+ * @param <T>
+ */
+ <T> void register(Callable<T> factory, Class<T> clazz);
+
+ /**
+ * Register a service.
+ */
+ void register(Object service);
+
+ /**
+ * Unregister a service.
+ * If the registration has been done using a factory, the same
+ * factory should be used to unregister.
+ */
+ void unregister(Object service);
+
+ /**
+ * Obtain a service implementing the given class.
+ */
+ <T> T getService(Class<T> clazz);
+
+ /**
+ * Obtain a list of services implementing the given class.
+ */
+ <T> List<T> getServices(Class<T> clazz);
+
+ /**
+ * Check whether the registry has a service of the given class.
+ */
+ boolean hasService(Class<?> clazz);
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Session.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Session.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Session.java
new file mode 100644
index 0000000..cf7f795
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Session.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+
+/**
+ * A <code>Session</code> can be used to execute commands.
+ *
+ * The {@link org.apache.karaf.shell.api.console.Registry} associated
+ * with this <code>Session</code> will contain: <ul>
+ * <li>{@link SessionFactory}</li>
+ * <li>{@link Command}s</li>
+ * <li>{@link Session}</li>
+ * <li>{@link Registry}</li>
+ * <li>{@link History}</li>
+ * <li>{@link Terminal}</li>
+ * </ul>
+ */
+public interface Session extends Runnable {
+
+ //
+ // Session properties
+ //
+ // Property names starting with "karaf." are reserved for karaf
+ //
+
+ String SCOPE = "SCOPE";
+ String SUBSHELL = "SUBSHELL";
+
+ String PRINT_STACK_TRACES = "karaf.printStackTraces";
+ String LAST_EXCEPTION = "karaf.lastException";
+ String IGNORE_INTERRUPTS = "karaf.ignoreInterrupts";
+ String COMPLETION_MODE = "karaf.completionMode";
+
+ String COMPLETION_MODE_GLOBAL = "global";
+ String COMPLETION_MODE_SUBSHELL = "subshell";
+ String COMPLETION_MODE_FIRST = "first";
+
+ String SCOPE_GLOBAL = "*";
+
+ /**
+ * Execute a program in this session.
+ *
+ * @param commandline
+ * @return the result of the execution
+ */
+ Object execute(CharSequence commandline) throws Exception;
+
+ /**
+ * Get the value of a variable.
+ *
+ * @param name
+ * @return
+ */
+ Object get(String name);
+
+ /**
+ * Set the value of a variable.
+ *
+ * @param name Name of the variable.
+ * @param value Value of the variable
+ */
+ void put(String name, Object value);
+
+ /**
+ * Return the input stream that is the first of the pipeline. This stream is
+ * sometimes necessary to communicate directly to the end user. For example,
+ * a "less" or "more" command needs direct input from the keyboard to
+ * control the paging.
+ *
+ * @return InputStream used closest to the user or null if input is from a
+ * file.
+ */
+ InputStream getKeyboard();
+
+ /**
+ * Return the PrintStream for the console. This must always be the stream
+ * "closest" to the user. This stream can be used to post messages that
+ * bypass the piping. If the output is piped to a file, then the object
+ * returned must be null.
+ *
+ * @return
+ */
+ PrintStream getConsole();
+
+ /**
+ * Prompt the user for a line.
+ *
+ * @param prompt
+ * @param mask
+ * @return
+ * @throws java.io.IOException
+ */
+ String readLine(String prompt, final Character mask) throws IOException;
+
+ /**
+ * Retrieve the {@link org.apache.karaf.shell.api.console.Terminal} associated
+ * with this <code>Session</code> or <code>null</code> if this <code>Session</code>
+ * is headless.
+ */
+ Terminal getTerminal();
+
+ /**
+ * Retrieve the {@link org.apache.karaf.shell.api.console.History} associated
+ * with this <code>Session</code> or <code>null</code> if this <code>Session</code>
+ * is headless.
+ */
+ History getHistory();
+
+ /**
+ * Retrieve the {@link org.apache.karaf.shell.api.console.Registry} associated
+ * with this <code>Session</code>.
+ */
+ Registry getRegistry();
+
+ /**
+ * Retrieve the {@link org.apache.karaf.shell.api.console.SessionFactory} associated
+ * with this <code>Session</code>.
+ */
+ SessionFactory getFactory();
+
+ /**
+ * Resolve a command name. If the command name has no specified scope, the fully
+ * qualified command name will be returned, depending on the scopes and current
+ * subshell.
+ */
+ String resolveCommand(String name);
+
+ /**
+ * Close this session. After the session is closed, it will throw
+ * IllegalStateException when it is used.
+ */
+ void close();
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/SessionFactory.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/SessionFactory.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/SessionFactory.java
new file mode 100644
index 0000000..7b02fd6
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/SessionFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+
+/**
+ * The <code>SessionFactory</code> can be used to create
+ * {@link Session} to execute commands.
+ *
+ * The {@link org.apache.karaf.shell.api.console.Registry} associated
+ * with this <code>SessionFactory</code> will contain: <ul>
+ * <li>{@link SessionFactory}</li>
+ * <li>{@link Registry}</li>
+ * <li>{@link Command}s</li>
+ * </ul>
+ */
+public interface SessionFactory {
+
+ /**
+ * Retrieve the {@link Registry} used by this <code>SessionFactory</code>.
+ */
+ Registry getRegistry();
+
+ /**
+ * Create new interactive session.
+ *
+ * @param in the input stream, can be <code>null</code> if the session is only used to execute a command using {@link Session#execute(CharSequence)}
+ * @param out the output stream
+ * @param err the error stream
+ * @param term the {@link Terminal} to use, may be <code>null</code>
+ * @param encoding the encoding to use for the input stream, may be <code>null</code>
+ * @param closeCallback a callback to be called when the session is closed, may be <code>null</code>
+ * @return the new session
+ */
+ Session create(InputStream in, PrintStream out, PrintStream err, Terminal term, String encoding, Runnable closeCallback);
+
+ /**
+ * Create a new headless session.
+ * Headless session can only be used to execute commands, so that
+ * {@link org.apache.karaf.shell.api.console.Session#run()} can not be used.
+ *
+ * @param in the input stream, can be <code>null</code> if the session is only used to execute a command using {@link Session#execute(CharSequence)}
+ * @param out the output stream
+ * @param err the error stream
+ * @return the new session
+ */
+ Session create(InputStream in, PrintStream out, PrintStream err);
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
new file mode 100644
index 0000000..3549ec7
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Terminal.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.api.console;
+
+/**
+ * Session terminal.
+ */
+public interface Terminal {
+
+ /**
+ * Width of the terminal.
+ */
+ int getWidth();
+
+ /**
+ * Height of the terminal.
+ */
+ int getHeight();
+
+ /**
+ * Whether ansi sequences are supported or not.
+ */
+ boolean isAnsiSupported();
+
+ /**
+ * Whether echo is enabled or not.
+ */
+ boolean isEchoEnabled();
+
+ /**
+ * Enable or disable echo.
+ */
+ void setEchoEnabled(boolean enabled);
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
new file mode 100644
index 0000000..4cd58ca
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.impl.action.command;
+
+import java.util.List;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+
+public class ActionCommand implements org.apache.karaf.shell.api.console.Command {
+
+ private final ManagerImpl manager;
+ private final Class<? extends Action> actionClass;
+
+ public ActionCommand(ManagerImpl manager, Class<? extends Action> actionClass) {
+ this.manager = manager;
+ this.actionClass = actionClass;
+ }
+
+ public Class<? extends Action> getActionClass() {
+ return actionClass;
+ }
+
+ @Override
+ public String getScope() {
+ return actionClass.getAnnotation(Command.class).scope();
+ }
+
+ @Override
+ public String getName() {
+ return actionClass.getAnnotation(Command.class).name();
+ }
+
+ @Override
+ public String getDescription() {
+ return actionClass.getAnnotation(Command.class).description();
+ }
+
+ @Override
+ public Completer getCompleter(boolean scoped) {
+ return new ArgumentCompleter(this, scoped);
+ }
+
+ protected Completer getCompleter(Class<?> clazz) {
+ return new DelayedCompleter(clazz);
+ }
+
+ @Override
+ public Object execute(Session session, List<Object> arguments) throws Exception {
+ Action action = createNewAction(session);
+ try {
+ if (new DefaultActionPreparator().prepare(action, session, arguments)) {
+ return action.execute();
+ }
+ } finally {
+ releaseAction(action);
+ }
+ return null;
+ }
+
+ protected Action createNewAction(Session session) {
+ try {
+ return manager.instantiate(actionClass, session.getRegistry());
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to creation command action " + actionClass.getName(), e);
+ }
+ }
+
+ protected void releaseAction(Action action) throws Exception {
+ manager.release(action);
+ }
+
+ public static class DelayedCompleter implements Completer {
+ private final Class<?> clazz;
+
+ public DelayedCompleter(Class<?> clazz) {
+ this.clazz = clazz;
+ }
+
+ @Override
+ public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+ Object service = session.getRegistry().getService(clazz);
+ if (service instanceof Completer) {
+ return ((Completer) service).complete(session, commandLine, candidates);
+ }
+ return -1;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e7d23bef/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
new file mode 100644
index 0000000..2868e6f
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
@@ -0,0 +1,385 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.shell.impl.action.command;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.FileCompleter;
+import org.apache.karaf.shell.support.completers.NullCompleter;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ArgumentCompleter implements Completer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ArgumentCompleter.class);
+
+ final ActionCommand command;
+ final Completer commandCompleter;
+ final Completer optionsCompleter;
+ final List<Completer> argsCompleters;
+ final Map<String, Completer> optionalCompleters;
+ final Map<Option, Field> fields = new HashMap<Option, Field>();
+ final Map<String, Option> options = new HashMap<String, Option>();
+ final Map<Integer, Field> arguments = new HashMap<Integer, Field>();
+ boolean strict = true;
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public ArgumentCompleter(ActionCommand command, boolean scoped) {
+ this.command = command;
+ Class<?> actionClass = command.getActionClass();
+ // Command name completer
+ Command cmd = actionClass.getAnnotation(Command.class);
+ String[] names = scoped || Session.SCOPE_GLOBAL.equals(cmd.scope()) ? new String[] { cmd.name() } : new String[] { cmd.name(), cmd.scope() + ":" + cmd.name() };
+ commandCompleter = new StringsCompleter(names);
+ // Build options completer
+ for (Class<?> type = actionClass; type != null; type = type.getSuperclass()) {
+ for (Field field : type.getDeclaredFields()) {
+ Option option = field.getAnnotation(Option.class);
+ if (option != null) {
+ fields.put(option, field);
+ options.put(option.name(), option);
+ String[] aliases = option.aliases();
+ if (aliases != null) {
+ for (String alias : aliases) {
+ options.put(alias, option);
+ }
+ }
+ }
+ Argument argument = field.getAnnotation(Argument.class);
+ if (argument != null) {
+ Integer key = argument.index();
+ if (arguments.containsKey(key)) {
+ LOGGER.warn("Duplicate @Argument annotations on class " + type.getName() + " for index: " + key + " see: " + field);
+ } else {
+ arguments.put(key, field);
+ }
+ }
+ }
+ }
+ options.put(HelpOption.HELP.name(), HelpOption.HELP);
+
+ argsCompleters = new ArrayList<Completer>();
+ optionsCompleter = new StringsCompleter(options.keySet());
+
+ boolean multi = false;
+ for (int key = 0; key < arguments.size(); key++) {
+ Completer completer = null;
+ Field field = arguments.get(key);
+ if (field != null) {
+ Argument argument = field.getAnnotation(Argument.class);
+ multi = (argument != null && argument.multiValued());
+ Completion ann = field.getAnnotation(Completion.class);
+ if (ann != null) {
+ Class<?> clazz = ann.value();
+ String[] value = ann.values();
+ if (clazz != null) {
+ if (value.length > 0 && clazz == StringsCompleter.class) {
+ completer = new StringsCompleter(value, ann.caseSensitive());
+ } else {
+ completer = command.getCompleter(clazz);
+ }
+ }
+ } else {
+ completer = getDefaultCompleter(field);
+ }
+ }
+ if (completer == null) {
+ completer = NullCompleter.INSTANCE;
+ }
+ argsCompleters.add(completer);
+ }
+ if (argsCompleters.isEmpty() || !multi) {
+ argsCompleters.add(NullCompleter.INSTANCE);
+ }
+ optionalCompleters = new HashMap<String, Completer>();
+ for (Option option : fields.keySet()) {
+ Completer completer = null;
+ Field field = fields.get(option);
+ if (field != null) {
+ Completion ann = field.getAnnotation(Completion.class);
+ if (ann != null) {
+ Class clazz = ann.value();
+ String[] value = ann.values();
+ if (clazz != null) {
+ if (clazz == StringsCompleter.class) {
+ completer = new StringsCompleter(value, ann.caseSensitive());
+ } else {
+ completer = command.getCompleter(clazz);
+ }
+ }
+ }
+ }
+ if (completer == null) {
+ completer = NullCompleter.INSTANCE;
+ }
+ optionalCompleters.put(option.name(), completer);
+ if (option.aliases() != null) {
+ for (String alias : option.aliases()) {
+ optionalCompleters.put(alias, completer);
+ }
+ }
+ }
+ }
+
+ private Completer getDefaultCompleter(Field field) {
+ Completer completer = null;
+ Class<?> type = field.getType();
+ if (type.isAssignableFrom(File.class)) {
+ completer = new FileCompleter();
+ } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
+ completer = new StringsCompleter(new String[] {"false", "true"}, false);
+ } else if (type.isAssignableFrom(Enum.class)) {
+ Set<String> values = new HashSet<String>();
+ for (Object o : EnumSet.allOf((Class<Enum>) type)) {
+ values.add(o.toString());
+ }
+ completer = new StringsCompleter(values, false);
+ } else {
+ // TODO any other completers we can add?
+ }
+ return completer;
+ }
+
+ /**
+ * If true, a completion at argument index N will only succeed
+ * if all the completions from 0-(N-1) also succeed.
+ */
+ public void setStrict(final boolean strict) {
+ this.strict = strict;
+ }
+
+ /**
+ * Returns whether a completion at argument index N will succees
+ * if all the completions from arguments 0-(N-1) also succeed.
+ */
+ public boolean getStrict() {
+ return this.strict;
+ }
+
+ public int complete(Session session, final CommandLine list, final List<String> candidates) {
+ int argpos = list.getArgumentPosition();
+ int argIndex = list.getCursorArgumentIndex();
+
+ Completer comp = null;
+ String[] args = list.getArguments();
+ int index = 0;
+ // First argument is command name
+ if (index < argIndex) {
+ // Verify scope
+ if (!Session.SCOPE_GLOBAL.equals(command.getScope()) && !session.resolveCommand(args[index]).equals(command.getScope() + ":" + command.getName())) {
+ return -1;
+ }
+ // Verify command name
+ if (!verifyCompleter(session, commandCompleter, args[index])) {
+ return -1;
+ }
+ index++;
+ } else {
+ comp = commandCompleter;
+ }
+ // Now, check options
+ if (comp == null) {
+ while (index < argIndex && args[index].startsWith("-")) {
+ if (!verifyCompleter(session, optionsCompleter, args[index])) {
+ return -1;
+ }
+ Option option = options.get(args[index]);
+ if (option == null) {
+ return -1;
+ }
+ Field field = fields.get(option);
+ if (field != null && field.getType() != boolean.class && field.getType() != Boolean.class) {
+ if (++index == argIndex) {
+ comp = NullCompleter.INSTANCE;
+ }
+ }
+ index++;
+ }
+ if (comp == null && index >= argIndex && index < args.length && args[index].startsWith("-")) {
+ comp = optionsCompleter;
+ }
+ }
+ //Now check for if last Option has a completer
+ int lastAgurmentIndex = argIndex - 1;
+ if (lastAgurmentIndex >= 1) {
+ Option lastOption = options.get(args[lastAgurmentIndex]);
+ if (lastOption != null) {
+ Field lastField = fields.get(lastOption);
+ if (lastField != null && lastField.getType() != boolean.class && lastField.getType() != Boolean.class) {
+ Option option = lastField.getAnnotation(Option.class);
+ if (option != null) {
+ Completer optionValueCompleter = null;
+ String name = option.name();
+ if (name != null) {
+ optionValueCompleter = optionalCompleters.get(name);
+ if (optionValueCompleter == null) {
+ String[] aliases = option.aliases();
+ if (aliases.length > 0) {
+ for (int i = 0; i < aliases.length && optionValueCompleter == null; i++) {
+ optionValueCompleter = optionalCompleters.get(option.aliases()[i]);
+ }
+ }
+ }
+ }
+ if(optionValueCompleter != null) {
+ comp = optionValueCompleter;
+ }
+ }
+ }
+ }
+ }
+
+ // Check arguments
+ if (comp == null) {
+ int indexArg = 0;
+ while (index < argIndex) {
+ Completer sub = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+ if (!verifyCompleter(session, sub, args[index])) {
+ return -1;
+ }
+ index++;
+ indexArg++;
+ }
+ comp = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+ }
+
+ int ret = comp.complete(session, new ArgumentCommandLine(list.getCursorArgument(), argpos), candidates);
+
+ if (ret == -1) {
+ return -1;
+ }
+
+ int pos = ret + (list.getBufferPosition() - argpos);
+
+ /**
+ * Special case: when completing in the middle of a line, and the
+ * area under the cursor is a delimiter, then trim any delimiters
+ * from the candidates, since we do not need to have an extra
+ * delimiter.
+ *
+ * E.g., if we have a completion for "foo", and we
+ * enter "f bar" into the buffer, and move to after the "f"
+ * and hit TAB, we want "foo bar" instead of "foo bar".
+ */
+
+ String buffer = list.getBuffer();
+ int cursor = list.getBufferPosition();
+ if ((buffer != null) && (cursor != buffer.length()) && isDelimiter(buffer, cursor)) {
+ for (int i = 0; i < candidates.size(); i++) {
+ String val = candidates.get(i);
+
+ while ((val.length() > 0)
+ && isDelimiter(val, val.length() - 1)) {
+ val = val.substring(0, val.length() - 1);
+ }
+
+ candidates.set(i, val);
+ }
+ }
+
+ return pos;
+ }
+
+ protected boolean verifyCompleter(Session session, Completer completer, String argument) {
+ List<String> candidates = new ArrayList<String>();
+ return completer.complete(session, new ArgumentCommandLine(argument, argument.length()), candidates) != -1 && !candidates.isEmpty();
+ }
+
+ /**
+ * Returns true if the specified character is a whitespace
+ * parameter. Check to ensure that the character is not
+ * escaped and returns true from
+ * {@link #isDelimiterChar}.
+ *
+ * @param buffer the complete command buffer
+ * @param pos the index of the character in the buffer
+ * @return true if the character should be a delimiter
+ */
+ public boolean isDelimiter(final String buffer, final int pos) {
+ return !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos);
+ }
+
+ public boolean isEscaped(final String buffer, final int pos) {
+ return pos > 0 && buffer.charAt(pos) == '\\' && !isEscaped(buffer, pos - 1);
+ }
+
+ /**
+ * The character is a delimiter if it is whitespace, and the
+ * preceeding character is not an escape character.
+ */
+ public boolean isDelimiterChar(String buffer, int pos) {
+ return Character.isWhitespace(buffer.charAt(pos));
+ }
+
+ static class ArgumentCommandLine implements CommandLine {
+ private final String argument;
+ private final int position;
+
+ ArgumentCommandLine(String argument, int position) {
+ this.argument = argument;
+ this.position = position;
+ }
+
+ @Override
+ public int getCursorArgumentIndex() {
+ return 0;
+ }
+
+ @Override
+ public String getCursorArgument() {
+ return argument;
+ }
+
+ @Override
+ public int getArgumentPosition() {
+ return position;
+ }
+
+ @Override
+ public String[] getArguments() {
+ return new String[] { argument };
+ }
+
+ @Override
+ public int getBufferPosition() {
+ return position;
+ }
+
+ @Override
+ public String getBuffer() {
+ return argument;
+ }
+ }
+}