From f59fca36e7538cafbd6032003c7b06d64862a2bf Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Mon, 19 Jul 2010 02:50:53 +0000 Subject: Branch to experiment with dynamic component interfaces and implementations. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@965346 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/tuscany/sca/shell/Shell.java | 550 +++++++++++++++++++++ .../sca/shell/jline/CompositeURICompletor.java | 66 +++ .../tuscany/sca/shell/jline/ICURICompletor.java | 47 ++ .../org/apache/tuscany/sca/shell/jline/JLine.java | 88 ++++ .../tuscany/sca/shell/jline/TShellCompletor.java | 166 +++++++ 5 files changed, 917 insertions(+) create mode 100644 sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/Shell.java create mode 100644 sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/CompositeURICompletor.java create mode 100644 sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/ICURICompletor.java create mode 100644 sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/JLine.java create mode 100644 sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/TShellCompletor.java (limited to 'sandbox/sebastien/java/dynamic/modules/shell/src') diff --git a/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/Shell.java b/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/Shell.java new file mode 100644 index 0000000000..2987b1fb6c --- /dev/null +++ b/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/Shell.java @@ -0,0 +1,550 @@ +/* + * 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.tuscany.sca.shell; + +import static java.lang.System.in; +import static java.lang.System.out; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; + +import javax.xml.stream.XMLStreamException; + +import org.apache.tuscany.sca.assembly.Composite; +import org.apache.tuscany.sca.common.java.io.IOHelper; +import org.apache.tuscany.sca.contribution.Artifact; +import org.apache.tuscany.sca.contribution.Contribution; +import org.apache.tuscany.sca.contribution.processor.ContributionReadException; +import org.apache.tuscany.sca.monitor.ValidationException; +import org.apache.tuscany.sca.node2.Node; +import org.apache.tuscany.sca.node2.NodeFactory; +import org.apache.tuscany.sca.runtime.ActivationException; +import org.apache.tuscany.sca.runtime.Version; +import org.apache.tuscany.sca.shell.jline.JLine; + +/** + * A little SCA command shell. + */ +public class Shell { + + public Node node; + private boolean useJline; + final List history = new ArrayList(); + private NodeFactory factory; + public static final String[] COMMANDS = new String[] {"addDeploymentComposite", "help", + "install", "installed", + "printDomainLevelComposite", + "remove", "start", "status", "stop"}; + + public static void main(final String[] args) throws Exception { + boolean useJline = true; + String domainURI = "default"; + for (String s : args) { + if ("-nojline".equals(s)) { + useJline = false; + } else { + domainURI = s; + } + } + new Shell(domainURI, useJline).run(); + } + + public Shell(String domainURI, boolean useJLine) { + this.factory = NodeFactory.newInstance(); + this.node = factory.createNode(domainURI); + this.useJline = useJLine; + } + + boolean addDeploymentComposite(final String curi, String contentURL) throws ContributionReadException, XMLStreamException, ActivationException, ValidationException, IOException { + node.addDeploymentComposite(curi, new StringReader(readContents(contentURL))); + return true; + } + + boolean install(final List toks) throws ContributionReadException, ActivationException, ValidationException { + boolean runDeployables = !toks.contains("-norun"); + String metaDataURL = null; + if (toks.contains("-metadata")) { + metaDataURL = toks.get(toks.indexOf("-metadata")+1); + } + List duris = null; + if (toks.contains("-duris")) { + duris = Arrays.asList(toks.get(toks.indexOf("-duris")+1).split(",")); + } + + String first = null; + String second = null; + for (int i=1; i toks) { + List curis; + if (toks.size() > 1) { + curis = Arrays.asList(new String[]{toks.get(1)}); + } else { + curis =node.getInstalledContributions(); + } + for (String curi : curis) { + out.println(curi + " " + node.getInstalledContribution(curi).getLocation()); + Contribution c = node.getInstalledContribution(curi); + for (Artifact a : c.getArtifacts()) { + if (a.getModel() instanceof Composite) { + Composite composite = (Composite) a.getModel(); + out.println(" " + composite.getURI() + " " + composite.getName()); + } + } + } + return true; + } + boolean listComposites(final String curi) { + Contribution c = node.getInstalledContribution(curi); + for (Artifact a : c.getArtifacts()) { + if (a.getModel() instanceof Composite) { + out.println(((Composite)a.getModel()).getName()); + } + } + return true; + } + + + private String getDefaultURI(String contributionURL) { + String uri = null; + try { + File f = new File(contributionURL); + if ("classes".equals(f.getName()) && "target".equals(f.getParentFile().getName())) { + uri = f.getParentFile().getParentFile().getName(); + } else { + uri = f.getName(); + } + } catch (Exception e) { + // ignore + } + if (uri == null) { + uri = contributionURL; + } + int lastDot = uri.lastIndexOf('.'); + if (lastDot > -1) { + uri = uri.substring(0, lastDot); + } + return uri; + } + + boolean printDomainLevelComposite() throws ContributionReadException, ActivationException, ValidationException { + out.println("TODO"); + //out.println(node.getDomainLevelCompositeAsString()); + return true; + } + + boolean getQNameDefinition(final String curi, String definintion, String symbolSpace) throws ContributionReadException, ActivationException, ValidationException { + // TODO: + return true; + } + + boolean remove(final String curi) throws ContributionReadException, ActivationException, ValidationException { + node.removeContribution(curi); + return true; + } + + public boolean stop(List toks) throws ActivationException { + if (toks == null || toks.size() < 2) { + node.stop(); + factory.stop(); + return false; + } + String curi = toks.get(1); + if (toks.size() > 2) { + node.removeFromDomainLevelComposite(curi + "/" + toks.get(2)); + } else { + for (String compositeURI : node.getDeployedCompostes(curi)) { + node.removeFromDomainLevelComposite(curi + "/" + compositeURI); + } + } + + return true; + } + + boolean start(String curi, String compositeURI) throws ActivationException, ValidationException { + node.addToDomainLevelComposite(curi + "/" + compositeURI); + return true; + } + + boolean status(final List toks) { + out.println("Domain: " + node.getDomainName()); + List ics; + if (toks.size()>1) { + ics = new ArrayList(); + ics.add(toks.get(1)); + } else { + ics = node.getInstalledContributions(); + } + + for (String curi : ics) { + Contribution c = node.getInstalledContribution(curi); + List dcs = node.getDeployedCompostes(curi); + if (toks.size()>2) { + dcs = new ArrayList(); + dcs.add(toks.get(2)); + } else { + dcs = node.getDeployedCompostes(curi); + } + for (String compositeUri : dcs) { + for (Artifact a : c.getArtifacts()) { + if (compositeUri.equals(a.getURI())) { + out.println(" " + curi + " " + compositeUri + " " + ((Composite)a.getModel()).getName()); + } + } + } + } + return true; + } + + boolean history() { + for (String l: history) + out.println(l); + return true; + } + + List read(Object r) throws IOException { + out.print("=> "); + final String l; + if (useJline) { + l = JLine.readLine(r); + } else { + l = ((BufferedReader)r).readLine(); + history.add(l); + } + String[] toks = l != null? l.trim().split(" ") : "stop".split(" "); + List toksList = new ArrayList(); + for (String s : toks) { + if (s != null && s.trim().length()>0) { + toksList.add(s); + } + } + return toksList; + } + + Callable eval(final List toks) { + final String op = toks.size() > 0 ? toks.get(0) : ""; + + if (op.equalsIgnoreCase("addDeploymentComposite")) return new Callable() { public Boolean call() throws Exception { + return addDeploymentComposite(toks.get(1), toks.get(2)); + }}; + if (op.equalsIgnoreCase("install")) return new Callable() { public Boolean call() throws Exception { + return install(toks); + }}; + if (op.equalsIgnoreCase("installed")) return new Callable() { public Boolean call() throws Exception { + return installed(toks); + }}; + if (op.equalsIgnoreCase("printDomainLevelComposite")) return new Callable() { public Boolean call() throws Exception { + return printDomainLevelComposite(); + }}; + if (op.equalsIgnoreCase("getQNameDefinition")) return new Callable() { public Boolean call() throws Exception { + return getQNameDefinition(toks.get(1), toks.get(2), toks.get(3)); + }}; + if (op.equalsIgnoreCase("remove")) return new Callable() { public Boolean call() throws Exception { + return remove(toks.get(1)); + }}; + if (op.equalsIgnoreCase("help")) return new Callable() { public Boolean call() { + return help(toks); + }}; + if (op.equalsIgnoreCase("stop")) return new Callable() { public Boolean call() throws Exception { + return stop(toks); + }}; + if (op.equalsIgnoreCase("bye")) return new Callable() { public Boolean call() throws Exception { + return stop(null); + }}; + if (op.equalsIgnoreCase("start")) return new Callable() { public Boolean call() throws Exception { + return start(toks.get(1), toks.get(2)); + }}; + if (op.equalsIgnoreCase("status")) return new Callable() { public Boolean call() { + return status(toks); + }}; + if (op.equalsIgnoreCase("history")) return new Callable() { public Boolean call() { + return history(); + }}; + if (op.equalsIgnoreCase("")) return new Callable() { public Boolean call() { + return true; + }}; + return new Callable() { public Boolean call() { + out.println("unknown command"); + return true; + }}; + } + + boolean apply(final Callable func) { + try { + return func.call(); + } catch (Exception e) { + e.printStackTrace(); + return true; + } + } + + public void run() throws IOException { + help(null); + Object reader; + if (useJline) { + reader = JLine.createJLineReader(this); + } else { + reader = new BufferedReader(new InputStreamReader(in)); + } + while(apply(eval(read(reader)))); + } + + String readContents(String location) throws IOException { + URL url = IOHelper.getLocationAsURL(location); + InputStream is = IOHelper.openStream(url); + try { + BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + StringBuilder builder = new StringBuilder(8192); + for(String line=br.readLine(); line!=null; line=br.readLine()) { + builder.append(line); + builder.append('\n'); + } + return builder.toString(); + } finally { + IOHelper.close(is); + } + } + + boolean help(List toks) { + String command = (toks == null || toks.size() < 2) ? null : toks.get(1); + if (command == null) { + helpOverview(); + } else if ("help".equalsIgnoreCase(command)){ + helpHelp(); + } else if ("install".equalsIgnoreCase(command)) { + helpInstall(); + } else if ("installed".equalsIgnoreCase(command)) { + helpInstalled(); + } else if ("remove".equalsIgnoreCase(command)) { + helpRemove(); + } else if ("addDeploymentComposite".equalsIgnoreCase(command)) { + helpAddDeploymentComposite(); + } else if ("printDomainLevelComposite".equalsIgnoreCase(command)) { + helpPrintDomainLevelComposite(); + } else if ("start".equalsIgnoreCase(command)) { + helpStart(); + } else if ("status".equalsIgnoreCase(command)) { + helpStatus(); + } else if ("stop".equalsIgnoreCase(command)) { + helpStop(); + } else if ("startup".equalsIgnoreCase(command)){ + helpStartUp(); + } + return true; + } + + boolean helpOverview() { + out.println("Apache Tuscany Shell (" + Version.getVersion() + " " + Version.getRevsion() + " " + Version.getBuildTime() + ")"); + out.println("Commands:"); + out.println(); + out.println(" help"); + out.println(" install [] [-norun -metadata -duris ]"); + out.println(" installed []"); + out.println(" remove "); + out.println(" addDeploymentComposite "); + out.println(" printDomainLevelComposite"); + out.println(" start "); + out.println(" status [ ]"); + out.println(" stop [ ]"); + out.println(); + if (useJline) out.println("Use Tab key for command and argument completion"); + out.println("For detailed help on each command do 'help ', for help of startup options do 'help startup'"); + out.println(); + return true; + } + void helpHelp() { + out.println(" help []"); + out.println(); + out.println(" Outputs help on the Tuscany Shell"); + out.println(" If the command argument is used it provides detailed help on that command otherwise"); + out.println(" it provides an overview of available Shell commands"); + out.println(); + out.println(" To get help on starting the Tuscany Shell use 'help startup'"); + out.println(); + out.println(" Arguments:"); + out.println(" - (optional) the command to get detailed help on"); + } + + void helpAddDeploymentComposite() { + out.println(" addDeploymentComposite "); + out.println(); + out.println(" Adds a deployment composite using a supplied composite ('composite by value' - a data"); + out.println(" structure, not an existing resource in the Domain) to the contribution identified by a"); + out.println(" supplied contribution URI. The added deployment composite is given a relative URI that"); + out.println(" matches the @name attribute of the composite, with a '.composite' suffix. Since all composites"); + out.println(" run within the context of an installed contribution (any component implementations or other"); + out.println(" definitions are resolved within that contribution), this functionality makes it possible"); + out.println(" for the deployer to create a composite with final configuration and wiring decisions and add"); + out.println(" it to an installed contribution without having to modify the contents of the root contribution."); + out.println(); + out.println(" Arguments:"); + out.println(" - (required) the URI of an installed contribution"); + out.println(" - (required) the location of the composite"); + } + + void helpInstall() { + out.println(" install [] [-norun -metadata -duris ]"); + out.println(); + out.println(" Creates an installed contribution with a supplied root contribution, installed at abase URI."); + out.println(); + out.println(" Arguments:"); + out.println(" uri - (optional) the URI (name) to use for the contribution. When no uri is specified"); + out.println(" a default URI is used derived from the contribution URL"); + out.println(" contributionURL - (required) the URL to the contribution to install"); + out.println(" -norun - (optional) do not start any composites listed as deployable in the sca-contribution.xml file"); + out.println(" -metadata - (optional) the URL to an external contribution meta data document that should be"); + out.println(" merged into any existing sca-contributions.xml file within the contribution."); + out.println(" -duris - (optional) specifies the URIs of contributions that are used to resolve the"); + out.println(" dependencies of the root contribution and other dependent contributions."); + out.println(" When not specified all installed contributions are used to resolve dependencies."); + } + + void helpInstalled() { + out.println(" installed []"); + out.println(); + out.println(" Shows information about the contributions installed on this node,"); + out.println(" including the contribution URI and location along with the URI"); + out.println(" and QName of any composites within the contribution"); + out.println(); + out.println(" Arguments:"); + out.println(" contributionURI - (optional) the URI of an installed contribution"); + } + + void helpRemove() { + out.println(" remove "); + out.println(); + out.println(" Removes an installed contribution"); + out.println(); + out.println(" Arguments:"); + out.println(" contributionURI - (required) the URI of an installed contribution"); + } + + void helpPrintDomainLevelComposite() { + out.println(" printDomainLevelComposite"); + out.println(); + out.println(" Not yet implemented"); + out.println(); + out.println(" Arguments:"); + out.println(" none"); + } + + void helpStart() { + out.println(" start "); + out.println(); + out.println(" Starts a composite."); + out.println(" The composite is added to the domain composite with semantics that correspond to the domain-level"); + out.println(" composite having an statement that references the supplied composite. All of the composites"); + out.println(" components become top-level components and the component services become externally visible"); + out.println(" services (eg. they would be present in a WSDL description of the Domain)."); + out.println(); + out.println(" Arguments:"); + out.println(" curi - (required) the URI of an installed contribution"); + out.println(" compositeUri - (required) the URI of a composite"); + } + + void helpStatus() { + out.println(" status [ ]"); + out.println(); + out.println(" Shows the status of the Node, listing for each deployed composite its"); + out.println(" contribution URI, the composite URI, and the composite QName."); + out.println(); + out.println(" Arguments:"); + out.println(" curi - (optional) the URI of an installed contribution"); + out.println(" compositeUri - (optional) the URI of a composite"); + } + + void helpStop() { + out.println(" stop [ ]"); + out.println(); + out.println(" Stops this Node or individual composites and contributions in the Node."); + out.println(" If a composite URI is specified then the composite is removed from the Domain Level composite"); + out.println(" This means that the removal of the components, wires, services and references originally added"); + out.println(" to the domain level composite by the identified composite. If a contribution URI is specified"); + out.println(" without a composite URI then all deployed composites composites in the contribution are stopped."); + out.println(" If no contribution URI is specified then the entire Node is stopped and the Shell exits."); + out.println(); + out.println(" Arguments:"); + out.println(" curi - (optional) the URI of an installed contribution"); + out.println(" compositeUri - (optional) the URI of a composite"); + } + + void helpStartUp() { + out.println(" Tuscany Shell StartUp Options "); + out.println(); + out.println(" When starting the Tuscany Shell there are optional arguments that can configure the Shell."); + out.println(); + out.println(" Arguments:"); + out.println(" (optional) the URI of the domain."); + out.println(" When the domainURI is a simple string then the Shell starts a standalone"); + out.println(" Node using the string as the domain name or 'default' if no name is specified."); + out.println(" When the domainURI starts with 'uri:' the Shell starts a distributed Node "); + out.println(" and the URI can encode parameters to configure the domain as follows:"); + out.println(" uri: cus = new ArrayList(); + for (Artifact a : c.getArtifacts()) { + if (a.getModel() instanceof Composite) { + cus.add(((Composite)a.getModel()).getURI()); + } + } + setCandidateStrings(cus.toArray(new String[cus.size()])); + return super.complete(buffer, cursor, clist); + } + + protected String getContributionURI() { + /* A little hacky to use a static but i can't see how else to get the contribution URI */ + return TShellCompletor.lastArg; + } +} diff --git a/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/ICURICompletor.java b/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/ICURICompletor.java new file mode 100644 index 0000000000..c0273320df --- /dev/null +++ b/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/ICURICompletor.java @@ -0,0 +1,47 @@ +/* + * 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.tuscany.sca.shell.jline; + +import java.util.List; + +import jline.SimpleCompletor; + +import org.apache.tuscany.sca.node2.Node; + +/** + * An Installed Contribution URI Completor + */ +public class ICURICompletor extends SimpleCompletor { + + private Node node; + + public ICURICompletor(Node node) { + super(""); + this.node = node; + } + + @Override + public int complete(final String buffer, final int cursor, final List clist) { + List ics = node.getInstalledContributions(); + setCandidateStrings(ics.toArray(new String[ics.size()])); + return super.complete(buffer, cursor, clist); + } + +} diff --git a/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/JLine.java b/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/JLine.java new file mode 100644 index 0000000000..3acf69b4bd --- /dev/null +++ b/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/JLine.java @@ -0,0 +1,88 @@ +/* + * 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.tuscany.sca.shell.jline; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.LinkedList; +import java.util.List; + +import jline.ArgumentCompletor; +import jline.Completor; +import jline.ConsoleReader; +import jline.FileNameCompletor; +import jline.SimpleCompletor; + +import org.apache.tuscany.sca.runtime.ActivationException; +import org.apache.tuscany.sca.shell.Shell; + +/** + * Keep all the JLine specific code out of the Shell class so that it runs ok + * when jline isn't on the classpath. + */ +public class JLine { + + public static String readLine(Object r) throws IOException { + return ((ConsoleReader)r).readLine(); + } + + public static Object createJLineReader(final Shell shell) throws IOException { + ConsoleReader reader = new ConsoleReader(); + fixCtrlC(reader); + // Add a Ctrl-c listener + reader.addTriggeredAction((char)3, new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + shell.stop(null); + } catch (ActivationException e1) { + e1.printStackTrace(); + } + System.exit(0); + } + }); + reader.setBellEnabled(false); + // TODO: write a Completor specific to this that can handle the individual command arguments + List completors = new LinkedList(); +// completors.add(new SimpleCompletor(Shell.COMMANDS)); +// completors.add(new ICURICompletor(shell.node)); +// completors.add(new FileNameCompletor()); +// reader.addCompletor(new ArgumentCompletor(completors)); + reader.addCompletor(new TShellCompletor(shell.node)); + return reader; + } + + /** + * The windowsbindings.properties shipped inside jline maps ctrl-c to INSERT + * with the comment "(frankly, I wasn't sure where to bind this)". That does not + * seem a great choice as it disables ctrl-c interupt so this resets that binding. + */ + private static void fixCtrlC(ConsoleReader reader) { + try { + Field f = ConsoleReader.class.getDeclaredField("keybindings"); + f.setAccessible(true); + short[] keybindings = (short[])f.get(reader); + if (keybindings[3] == -48) keybindings[3] = 3; + } catch (Exception e) { + e.printStackTrace(); // shouldnt happen + } + } +} diff --git a/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/TShellCompletor.java b/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/TShellCompletor.java new file mode 100644 index 0000000000..ad6fb74e08 --- /dev/null +++ b/sandbox/sebastien/java/dynamic/modules/shell/src/main/java/org/apache/tuscany/sca/shell/jline/TShellCompletor.java @@ -0,0 +1,166 @@ +/* + * 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.tuscany.sca.shell.jline; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import jline.ArgumentCompletor; +import jline.Completor; +import jline.ConsoleReader; +import jline.FileNameCompletor; +import jline.NullCompletor; +import jline.SimpleCompletor; + +import org.apache.tuscany.sca.node2.Node; +import org.apache.tuscany.sca.shell.Shell; + +/** + * A Completor thats specific to the Tuscany Shell that knows about + * each command and has an argument specific Completor for each command argument. + */ +public class TShellCompletor extends ArgumentCompletor { + + Map completors; + final Completor commandCompletor = new SimpleCompletor(Shell.COMMANDS); + final ArgumentDelimiter delim = new WhitespaceArgumentDelimiter(); + final Node node; + + static String lastArg; + + public TShellCompletor(Node node) { + super((Completor)null); + this.node = node; + completors = new HashMap(); + completors.put("help", new Completor[]{commandCompletor, commandCompletor, new NullCompletor()}); +// completors.put("install", new Completor[]{commandCompletor, new InstallCompletor(), new NullCompletor()}); + completors.put("install", new Completor[]{commandCompletor, new FileNameCompletor(), new FileNameCompletor(), new NullCompletor()}); + completors.put("installed", new Completor[]{commandCompletor, new ICURICompletor(node), new NullCompletor()}); + completors.put("remove", new Completor[]{commandCompletor, new ICURICompletor(node), new NullCompletor()}); + completors.put("addDeploymentComposite", new Completor[]{commandCompletor, new ICURICompletor(node), new FileNameCompletor(), new NullCompletor()}); + completors.put("printDomainLevelComposite", new Completor[]{commandCompletor, new NullCompletor()}); + completors.put("start", new Completor[]{commandCompletor, new ICURICompletor(node), new CompositeURICompletor(node), new NullCompletor()}); + completors.put("status", new Completor[]{commandCompletor, new ICURICompletor(node), new CompositeURICompletor(node), new NullCompletor()}); + completors.put("stop", new Completor[]{commandCompletor, new ICURICompletor(node), new CompositeURICompletor(node), new NullCompletor()}); + } + + @Override + /** + * Copied from JLine ArgumentCompletor class. The only change is to + * get the completors by using the getCompletors method and setting the lastArg static. + */ + public int complete(final String buffer, final int cursor, + final List candidates) { + ArgumentList list = delim.delimit(buffer, cursor); + int argpos = list.getArgumentPosition(); + int argIndex = list.getCursorArgumentIndex(); + + if (argIndex < 0) { + return -1; + } + + if (argIndex > 0) { + /* set the last argument in a static for the CompositeURICompletor */ + lastArg = list.getArguments()[argIndex-1]; + if (lastArg != null) lastArg = lastArg.trim(); + } + + final Completor comp; + + Completor[] completors = getCompletors(buffer); + + // if we are beyond the end of the completors, just use the last one + if (argIndex >= completors.length) { + comp = completors[completors.length - 1]; + } else { + comp = completors[argIndex]; + } + + // ensure that all the previous completors are successful before + // allowing this completor to pass (only if strict is true). + for (int i = 0; getStrict() && (i < argIndex); i++) { + Completor sub = + completors[(i >= completors.length) ? (completors.length - 1) : i]; + String[] args = list.getArguments(); + String arg = ((args == null) || (i >= args.length)) ? "" : args[i]; + + List subCandidates = new LinkedList(); + + if (sub.complete(arg, arg.length(), subCandidates) == -1) { + return -1; + } + + if (subCandidates.size() == 0) { + return -1; + } + } + + int ret = comp.complete(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". + */ + if ((cursor != buffer.length()) && delim.isDelimiter(buffer, cursor)) { + for (int i = 0; i < candidates.size(); i++) { + String val = candidates.get(i).toString(); + + while ((val.length() > 0) + && delim.isDelimiter(val, val.length() - 1)) { + val = val.substring(0, val.length() - 1); + } + + candidates.set(i, val); + } + } + + ConsoleReader.debug("Completing " + buffer + "(pos=" + cursor + ") " + + "with: " + candidates + ": offset=" + pos); + + return pos; + } + + protected Completor[] getCompletors(String buffer) { + StringBuilder sb = new StringBuilder(); + for (int i=0; i