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 2012/04/10 17:26:20 UTC
svn commit: r1311786 - in /karaf/branches/karaf-2.3.x:
assemblies/apache-karaf/src/main/distribution/text/etc/ shell/console/
shell/console/src/main/java/org/apache/felix/gogo/commands/
shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ ...
Author: gnodet
Date: Tue Apr 10 15:26:18 2012
New Revision: 1311786
URL: http://svn.apache.org/viewvc?rev=1311786&view=rev
Log:
[KARAF-1045] Improve help system for subshells or other pages
Conflicts:
assemblies/apache-karaf/src/main/distribution/text/etc/shell.init.script
util/pom.xml
Added:
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/CommandListHelpProvider.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpAction.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpSystem.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SimpleHelpProvider.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SingleCommandHelpProvider.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SubShellHelpProvider.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/util/
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/util/Branding.java
karaf/branches/karaf-2.3.x/shell/console/src/test/java/org/apache/karaf/shell/console/help/
karaf/branches/karaf-2.3.x/shell/console/src/test/java/org/apache/karaf/shell/console/help/TestFormatting.java
karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/
karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/
karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/karaf/
karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/karaf/shell/
karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/karaf/shell/osgi/
karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/karaf/shell/osgi/osgi.txt
karaf/branches/karaf-2.3.x/util/src/main/java/org/apache/karaf/util/InterpolationHelper.java
Removed:
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/HelpAction.java
Modified:
karaf/branches/karaf-2.3.x/assemblies/apache-karaf/src/main/distribution/text/etc/shell.init.script
karaf/branches/karaf-2.3.x/shell/console/pom.xml
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java
karaf/branches/karaf-2.3.x/shell/console/src/main/resources/META-INF/services/org/apache/karaf/shell/commands
karaf/branches/karaf-2.3.x/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/OSGI-INF/blueprint/shell-osgi.xml
karaf/branches/karaf-2.3.x/util/pom.xml
Modified: karaf/branches/karaf-2.3.x/assemblies/apache-karaf/src/main/distribution/text/etc/shell.init.script
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/assemblies/apache-karaf/src/main/distribution/text/etc/shell.init.script?rev=1311786&r1=1311785&r2=1311786&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/assemblies/apache-karaf/src/main/distribution/text/etc/shell.init.script (original)
+++ karaf/branches/karaf-2.3.x/assemblies/apache-karaf/src/main/distribution/text/etc/shell.init.script Tue Apr 10 15:26:18 2012
@@ -24,6 +24,7 @@ ld = { log:display $args } ;
lde = { log:display-exception $args } ;
la = { osgi:list -t 0 $args } ;
cl = { config:list "(service.pid=$args)" } ;
+help = { *:help $args | more } ;
man = { help $args } ;
// system:* aliases
@@ -62,4 +63,4 @@ feature:list = { features:list $args } ;
feature:uninstall = { features:uninstall $args } ;
feature:list-repository = { features:listrepositories $args } ;
feature:list-url = { features:listurl $args } ;
-feature:list-version = { features:listversions $args } ;
\ No newline at end of file
+feature:list-version = { features:listversions $args } ;
Modified: karaf/branches/karaf-2.3.x/shell/console/pom.xml
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/pom.xml?rev=1311786&r1=1311785&r2=1311786&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/pom.xml (original)
+++ karaf/branches/karaf-2.3.x/shell/console/pom.xml Tue Apr 10 15:26:18 2012
@@ -64,6 +64,10 @@
<artifactId>org.apache.karaf.jaas.modules</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>org.apache.karaf.util</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.apache.aries.blueprint</groupId>
<artifactId>org.apache.aries.blueprint</artifactId>
</dependency>
@@ -111,6 +115,7 @@
<instructions>
<Import-Package>
!org.apache.karaf.shell.console*,
+ !org.apache.karaf.util*,
!org.apache.felix.gogo.commands*,
!org.fusesource.jansi*,
!javax.swing,
@@ -132,6 +137,7 @@
<Private-Package>
org.fusesource.jansi.internal,
org.apache.felix.gogo.runtime*,
+ org.apache.karaf.util*,
META-INF.native.*
</Private-Package>
<Bundle-NativeCode>
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java Tue Apr 10 15:26:18 2012
@@ -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.felix.gogo.commands;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface SubShell {
+
+ /**
+ * 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
+ */
+ String detailedDescription() default "";
+
+}
Modified: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java?rev=1311786&r1=1311785&r2=1311786&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java (original)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java Tue Apr 10 15:26:18 2012
@@ -570,13 +570,33 @@ public class DefaultActionPreparator imp
// TODO move this to a helper class?
public static void printFormatted(String prefix, String str, int termWidth, PrintStream out) {
+ printFormatted(prefix, str, termWidth, out, true);
+ }
+
+ public static void printFormatted(String prefix, String str, int termWidth, PrintStream out, boolean prefixFirstLine) {
int pfxLen = length(prefix);
int maxwidth = termWidth - pfxLen;
Pattern wrap = Pattern.compile("(\\S\\S{" + maxwidth + ",}|.{1," + maxwidth + "})(\\s+|$)");
- Matcher m = wrap.matcher(str);
- while (m.find()) {
- out.print(prefix);
- out.println(m.group());
+ int cur = 0;
+ while (cur >= 0) {
+ int lst = str.indexOf('\n', cur);
+ String s = (lst >= 0) ? str.substring(cur, lst) : str.substring(cur);
+ if (s.length() == 0) {
+ out.println();
+ } else {
+ Matcher m = wrap.matcher(s);
+ while (m.find()) {
+ if (cur > 0 || prefixFirstLine) {
+ out.print(prefix);
+ }
+ out.println(m.group());
+ }
+ }
+ if (lst >= 0) {
+ cur = lst + 1;
+ } else {
+ break;
+ }
}
}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,26 @@
+/**
+ *
+ * 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.console;
+
+import org.apache.felix.service.command.CommandSession;
+
+public interface HelpProvider {
+
+ String getHelp(CommandSession session, String path);
+
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java Tue Apr 10 15:26:18 2012
@@ -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.console;
+
+public interface SubShell {
+
+ /**
+ * Returns the name of the command if used inside a shell
+ */
+ String getName();
+
+ /**
+ * Returns the description of the command which is used to generate command line help
+ */
+ String getDescription();
+
+ /**
+ * Returns a detailed description of the command
+ */
+ String getDetailedDescription();
+
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,43 @@
+/**
+ *
+ * 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.console.commands;
+
+import org.apache.karaf.shell.console.SubShell;
+
+public class AnnotatedSubShell implements SubShell {
+
+ public String getName() {
+ return getAnnotation().name();
+ }
+
+ public String getDescription() {
+ return getAnnotation().description();
+ }
+
+ public String getDetailedDescription() {
+ return getAnnotation().detailedDescription();
+ }
+
+ org.apache.felix.gogo.commands.SubShell getAnnotation() {
+ org.apache.felix.gogo.commands.SubShell ann = getClass().getAnnotation(org.apache.felix.gogo.commands.SubShell.class);
+ if (ann == null) {
+ throw new IllegalStateException("The class should be annotated with the org.apache.felix.gogo.commands.SubShell annotation");
+ }
+ return ann;
+ }
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java Tue Apr 10 15:26:18 2012
@@ -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.console.commands;
+
+import org.apache.karaf.shell.console.SubShell;
+
+public class BasicSubShell implements SubShell {
+
+ private String name;
+ private String description;
+ private String detailedDescription;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getDetailedDescription() {
+ return detailedDescription;
+ }
+
+ public void setDetailedDescription(String detailedDescription) {
+ this.detailedDescription = detailedDescription;
+ }
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,98 @@
+/*
+ * 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.console.completer;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.felix.gogo.runtime.CommandSessionImpl;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.jline.CommandSessionHolder;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+/**
+ * Completes command names
+ */
+public class CommandNamesCompleter implements Completer {
+
+ private CommandSession session;
+ private final Set<String> commands = new CopyOnWriteArraySet<String>();
+
+ public CommandNamesCompleter() {
+ this(CommandSessionHolder.getSession());
+ }
+
+ public CommandNamesCompleter(CommandSession session) {
+ this.session = session;
+
+ try {
+ new CommandTracker();
+ } catch (Throwable t) {
+ // Ignore in case we're not in OSGi
+ }
+ }
+
+
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ if (session == null) {
+ session = CommandSessionHolder.getSession();
+ }
+ checkData();
+ int res = new StringsCompleter(commands).complete(buffer, cursor, candidates);
+ Collections.sort(candidates);
+ return res;
+ }
+
+ protected void checkData() {
+ if (commands.isEmpty()) {
+ Set<String> names = new HashSet<String>((Set<String>) session.get(CommandSessionImpl.COMMANDS));
+ for (String name : names) {
+ commands.add(name);
+ if (name.indexOf(':') > 0) {
+ commands.add(name.substring(0, name.indexOf(':')));
+ }
+ }
+ }
+ }
+
+ private class CommandTracker {
+ public CommandTracker() throws Exception {
+ BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
+ ServiceListener listener = new ServiceListener() {
+ public void serviceChanged(ServiceEvent event) {
+ commands.clear();
+ }
+ };
+ context.addServiceListener(listener,
+ String.format("(&(%s=*)(%s=*))",
+ CommandProcessor.COMMAND_SCOPE,
+ CommandProcessor.COMMAND_FUNCTION));
+ }
+ }
+
+}
+
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/CommandListHelpProvider.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/CommandListHelpProvider.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/CommandListHelpProvider.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/CommandListHelpProvider.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,152 @@
+/**
+ *
+ * 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.console.help;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import jline.Terminal;
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.basic.AbstractCommand;
+import org.apache.felix.gogo.commands.basic.DefaultActionPreparator;
+import org.apache.felix.gogo.runtime.CommandSessionImpl;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Function;
+import org.apache.karaf.shell.console.HelpProvider;
+import org.apache.karaf.shell.console.NameScoping;
+import org.fusesource.jansi.Ansi;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public class CommandListHelpProvider implements HelpProvider {
+
+ public String getHelp(CommandSession session, String path) {
+ if (path.indexOf('|') > 0) {
+ if (path.startsWith("command-list|")) {
+ path = path.substring("command-list|".length());
+ } else {
+ return null;
+ }
+ }
+ SortedMap<String, String> commands = getCommandDescriptions(session, path);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ printMethodList(session, new PrintStream(baos), commands);
+ return baos.toString();
+ }
+
+ private SortedMap<String, String> getCommandDescriptions(CommandSession session, String command) {
+ Set<String> names = (Set<String>) session.get(CommandSessionImpl.COMMANDS);
+ SortedMap<String,String> commands = new TreeMap<String,String>();
+ for (String name : names) {
+ if (command != null && !name.startsWith(command)) {
+ continue;
+ }
+ String description = null;
+ Function function = (Function) session.get(name);
+ function = unProxy(function);
+ if (function instanceof AbstractCommand) {
+ try {
+ Method mth = AbstractCommand.class.getDeclaredMethod("createNewAction");
+ mth.setAccessible(true);
+ Action action = (Action) mth.invoke(function);
+ Class<? extends Action> clazz = action.getClass();
+ Command ann = clazz.getAnnotation(Command.class);
+ description = ann.description();
+ } catch (Throwable e) {
+ }
+ if (name.startsWith("*:")) {
+ name = name.substring(2);
+ }
+ commands.put(name, description);
+ }
+ }
+ return commands;
+ }
+
+ protected void printMethodList(CommandSession session, PrintStream out, SortedMap<String, String> commands) {
+ Terminal term = (Terminal) session.get(".jline.terminal");
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("COMMANDS").a(Ansi.Attribute.RESET));
+ int max = 0;
+ for (Map.Entry<String,String> entry : commands.entrySet()) {
+ String key = NameScoping.getCommandNameWithoutGlobalPrefix(session, entry.getKey());
+ max = Math.max(max,key.length());
+ }
+ int margin = 4;
+ String prefix1 = " ";
+ if (term != null && term.getWidth() - max - prefix1.length() - margin > 50) {
+ String prefix2 = prefix1;
+ for (int i = 0; i < max + margin; i++) {
+ prefix2 += " ";
+ }
+ for (Map.Entry<String,String> entry : commands.entrySet()) {
+ out.print(prefix1);
+ String key = NameScoping.getCommandNameWithoutGlobalPrefix(session, entry.getKey());
+ out.print(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(key).a(Ansi.Attribute.RESET));
+ for (int i = 0; i < max - key.length() + margin; i++) {
+ out.print(' ');
+ }
+ if (entry.getValue() != null) {
+ DefaultActionPreparator.printFormatted(prefix2, entry.getValue(), term.getWidth(), out, false);
+ }
+ }
+ } else {
+ String prefix2 = prefix1 + prefix1;
+ for (Map.Entry<String,String> entry : commands.entrySet()) {
+ out.print(prefix1);
+ String key = NameScoping.getCommandNameWithoutGlobalPrefix(session, entry.getKey());
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(key).a(Ansi.Attribute.RESET));
+ if (entry.getValue() != null) {
+ DefaultActionPreparator.printFormatted(prefix2, entry.getValue(),
+ term != null ? term.getWidth() : 80, out);
+ }
+ }
+ }
+ out.println();
+ }
+
+ protected Function unProxy(Function function) {
+ try {
+ if (function.getClass().getName().contains("CommandProxy")) {
+ Field contextField = function.getClass().getDeclaredField("context");
+ Field referenceField = function.getClass().getDeclaredField("reference");
+ contextField.setAccessible(true);
+ referenceField.setAccessible(true);
+ BundleContext context = (BundleContext) contextField.get(function);
+ ServiceReference reference = (ServiceReference) referenceField.get(function);
+ Object target = context.getService(reference);
+ try {
+ if (target instanceof Function) {
+ function = (Function) target;
+ }
+ } finally {
+ context.ungetService(reference);
+ }
+ }
+ } catch (Throwable t) {
+ }
+ return function;
+ }
+
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpAction.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpAction.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpAction.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpAction.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,192 @@
+/**
+ *
+ * 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.console.help;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import jline.Terminal;
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.basic.AbstractCommand;
+import org.apache.felix.gogo.commands.basic.DefaultActionPreparator;
+import org.apache.felix.service.command.Function;
+import org.apache.karaf.shell.console.AbstractAction;
+import org.apache.karaf.shell.console.NameScoping;
+import org.apache.karaf.shell.console.SubShell;
+import org.fusesource.jansi.Ansi;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+import static org.apache.felix.gogo.commands.basic.DefaultActionPreparator.printFormatted;
+
+/**
+ * Displays help on the available commands
+ */
+@Command(scope = "*", name = "help", description = "Displays this help or help about a command")
+public class HelpAction extends AbstractAction {
+
+ @Argument(name = "command", required = false, description = "The command to get help for")
+ private String command;
+
+ private HelpSystem provider;
+
+ public void setProvider(HelpSystem provider) {
+ this.provider = provider;
+ }
+
+ public Object doExecute() throws Exception {
+ String help = provider.getHelp(session, command);
+ if (help != null) {
+ System.out.println(help);
+ }
+ return null;
+ }
+
+ private SortedMap<String, String> getCommandDescriptions(Set<String> names) {
+ SortedMap<String,String> commands = new TreeMap<String,String>();
+ for (String name : names) {
+ if (command != null && !name.startsWith(command)) {
+ continue;
+ }
+ String description = null;
+ Function function = (Function) session.get(name);
+ function = unProxy(function);
+ if (function instanceof AbstractCommand) {
+ try {
+ Method mth = AbstractCommand.class.getDeclaredMethod("createNewAction");
+ mth.setAccessible(true);
+ Action action = (Action) mth.invoke(function);
+ Class<? extends Action> clazz = action.getClass();
+ Command ann = clazz.getAnnotation(Command.class);
+ description = ann.description();
+ } catch (Throwable e) {
+ }
+ if (name.startsWith("*:")) {
+ name = name.substring(2);
+ }
+ commands.put(name, description);
+ }
+ }
+ return commands;
+ }
+
+ private void printMethodList(Terminal term, PrintStream out, SortedMap<String, String> commands) {
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("COMMANDS").a(Ansi.Attribute.RESET));
+// int max = 0;
+// for (Map.Entry<String,String> entry : commands.entrySet()) {
+// String key = NameScoping.getCommandNameWithoutGlobalPrefix(session, entry.getKey());
+// max = Math.max(max,key.length());
+// }
+ for (Map.Entry<String,String> entry : commands.entrySet()) {
+ out.print(" ");
+ String key = NameScoping.getCommandNameWithoutGlobalPrefix(session, entry.getKey());
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(key).a(Ansi.Attribute.RESET));
+ if (entry.getValue() != null) {
+ DefaultActionPreparator.printFormatted(" ", entry.getValue(),
+ term != null ? term.getWidth() : 80, out);
+ }
+ }
+ out.println();
+ }
+
+ private void printSubShellHelp(Bundle bundle, SubShell subShell, PrintStream out) {
+ Terminal term = session != null ? (Terminal) session.get(".jline.terminal") : null;
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("SUBSHELL").a(Ansi.Attribute.RESET));
+ out.print(" ");
+ if (subShell.getName() != null) {
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(subShell.getName()).a(Ansi.Attribute.RESET));
+ out.println();
+ }
+ out.print("\t");
+ out.println(subShell.getDescription());
+ out.println();
+ if (subShell.getDetailedDescription() != null) {
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("DETAILS").a(Ansi.Attribute.RESET));
+ String desc = loadDescription(bundle, subShell.getDetailedDescription());
+ printFormatted(" ", desc, term != null ? term.getWidth() : 80, out);
+ }
+ }
+
+ protected String loadDescription(Bundle bundle, String desc) {
+ if (desc.startsWith("classpath:")) {
+ URL url = bundle.getResource(desc.substring("classpath:".length()));
+ if (url == null) {
+ desc = "Unable to load description from " + desc;
+ } else {
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ Reader r = new InputStreamReader(is);
+ StringWriter sw = new StringWriter();
+ int c;
+ while ((c = r.read()) != -1) {
+ sw.append((char) c);
+ }
+ desc = sw.toString();
+ } catch (IOException e) {
+ desc = "Unable to load description from " + desc;
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+ return desc;
+ }
+
+ protected Function unProxy(Function function) {
+ try {
+ if (function.getClass().getName().contains("CommandProxy")) {
+ Field contextField = function.getClass().getDeclaredField("context");
+ Field referenceField = function.getClass().getDeclaredField("reference");
+ contextField.setAccessible(true);
+ referenceField.setAccessible(true);
+ BundleContext context = (BundleContext) contextField.get(function);
+ ServiceReference reference = (ServiceReference) referenceField.get(function);
+ Object target = context.getService(reference);
+ try {
+ if (target instanceof Function) {
+ function = (Function) target;
+ }
+ } finally {
+ context.ungetService(reference);
+ }
+ }
+ } catch (Throwable t) {
+ }
+ return function;
+ }
+
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpSystem.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpSystem.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpSystem.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/HelpSystem.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,85 @@
+/**
+ *
+ * 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.console.help;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.HelpProvider;
+import org.apache.karaf.util.InterpolationHelper;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class HelpSystem implements HelpProvider {
+
+ private BundleContext context;
+ private ServiceTracker tracker;
+
+ public void setContext(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ tracker = new ServiceTracker(context, HelpProvider.class.getName(), null);
+ tracker.open();
+ }
+
+ public void stop() {
+ tracker.close();
+ }
+
+ public synchronized List<HelpProvider> getProviders() {
+ ServiceReference[] refs = tracker.getServiceReferences();
+ Arrays.sort(refs);
+ List<HelpProvider> providers = new ArrayList<HelpProvider>();
+ for (int i = refs.length - 1; i >= 0; i--) {
+ providers.add((HelpProvider) tracker.getService(refs[i]));
+ }
+ return providers;
+ }
+
+ public String getHelp(final CommandSession session, String path) {
+ if (path == null) {
+ path = "%root%";
+ }
+ Map<String,String> props = new HashMap<String,String>();
+ props.put("data", "${" + path + "}");
+ final List<HelpProvider> providers = getProviders();
+ InterpolationHelper.performSubstitution(props, new InterpolationHelper.SubstitutionCallback() {
+ public String getValue(final String key) {
+ for (HelpProvider hp : providers) {
+ String help = hp.getHelp(session, key);
+ if (help != null) {
+ if (help.endsWith("\n")) {
+ help = help.substring(0, help.length() -1);
+ }
+ return help;
+ }
+ }
+ return null;
+ }
+ });
+ return props.get("data");
+ }
+
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SimpleHelpProvider.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SimpleHelpProvider.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SimpleHelpProvider.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SimpleHelpProvider.java Tue Apr 10 15:26:18 2012
@@ -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.console.help;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Map;
+
+import jline.Terminal;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.HelpProvider;
+
+import static org.apache.felix.gogo.commands.basic.DefaultActionPreparator.printFormatted;
+
+public class SimpleHelpProvider implements HelpProvider {
+
+ private Map<String, String> help;
+
+ public Map<String, String> getHelp() {
+ return help;
+ }
+
+ public void setHelp(Map<String, String> help) {
+ this.help = help;
+ }
+
+ public String getHelp(CommandSession session, String path) {
+ if (path.indexOf('|') > 0) {
+ if (path.startsWith("simple|")) {
+ path = path.substring("simple|".length());
+ } else {
+ return null;
+ }
+ }
+ String str = help.get(path);
+ if (str != null) {
+ Terminal term = (Terminal) session.get(".jline.terminal");
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ printFormatted("", str, term != null ? term.getWidth() : 80, new PrintStream(baos, true));
+ str = baos.toString();
+ }
+ return str;
+ }
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SingleCommandHelpProvider.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SingleCommandHelpProvider.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SingleCommandHelpProvider.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SingleCommandHelpProvider.java Tue Apr 10 15:26:18 2012
@@ -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.console.help;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Set;
+
+import org.apache.felix.gogo.runtime.CommandSessionImpl;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.threadio.ThreadIO;
+import org.apache.karaf.shell.console.HelpProvider;
+
+public class SingleCommandHelpProvider implements HelpProvider {
+
+ private ThreadIO io;
+
+ public void setIo(ThreadIO io) {
+ this.io = io;
+ }
+
+ public String getHelp(CommandSession session, String path) {
+ if (path.indexOf('|') > 0) {
+ if (path.startsWith("command|")) {
+ path = path.substring("command|".length());
+ } else {
+ return null;
+ }
+ }
+ Set<String> names = (Set<String>) session.get(CommandSessionImpl.COMMANDS);
+ if (path != null && names.contains(path)) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ io.setStreams(new ByteArrayInputStream(new byte[0]), new PrintStream(baos, true), new PrintStream(baos, true));
+ try {
+ session.execute(path + " --help");
+ } catch (Throwable t) {
+ t.printStackTrace();
+ } finally {
+ io.close();
+ }
+ return baos.toString();
+ }
+ return null;
+ }
+}
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SubShellHelpProvider.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SubShellHelpProvider.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SubShellHelpProvider.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/help/SubShellHelpProvider.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,130 @@
+/**
+ *
+ * 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.console.help;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.net.URL;
+
+import jline.Terminal;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.HelpProvider;
+import org.apache.karaf.shell.console.SubShell;
+import org.fusesource.jansi.Ansi;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+import static org.apache.felix.gogo.commands.basic.DefaultActionPreparator.printFormatted;
+
+public class SubShellHelpProvider implements HelpProvider {
+
+ private BundleContext context;
+ private ServiceTracker tracker;
+
+ public void setContext(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ tracker = new ServiceTracker(context, SubShell.class.getName(), null);
+ tracker.open();
+ }
+
+ public void stop() {
+ tracker.close();
+ }
+
+ public String getHelp(CommandSession session, String path) {
+ if (path.indexOf('|') > 0) {
+ if (path.startsWith("subshell|")) {
+ path = path.substring("subshell|".length());
+ } else {
+ return null;
+ }
+ }
+ for (ServiceReference ref : tracker.getServiceReferences()) {
+ if (path.equals(ref.getProperty("name"))) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ printSubShellHelp(session, ref.getBundle(), (SubShell) tracker.getService(ref), new PrintStream(baos, true));
+ return baos.toString();
+ }
+ }
+ return null;
+ }
+
+ private void printSubShellHelp(CommandSession session, Bundle bundle, SubShell subShell, PrintStream out) {
+ Terminal term = session != null ? (Terminal) session.get(".jline.terminal") : null;
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("SUBSHELL").a(Ansi.Attribute.RESET));
+ out.print(" ");
+ if (subShell.getName() != null) {
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(subShell.getName()).a(Ansi.Attribute.RESET));
+ out.println();
+ }
+ out.print("\t");
+ out.println(subShell.getDescription());
+ out.println();
+ if (subShell.getDetailedDescription() != null) {
+ out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("DETAILS").a(Ansi.Attribute.RESET));
+ String desc = loadDescription(bundle, subShell.getDetailedDescription());
+ while (desc.endsWith("\n")) {
+ desc = desc.substring(0, desc.length() -1);
+ }
+ printFormatted(" ", desc, term != null ? term.getWidth() : 80, out);
+ }
+ out.println();
+ out.println("${command-list|" + subShell.getName() + ":}");
+ }
+
+ protected String loadDescription(Bundle bundle, String desc) {
+ if (desc.startsWith("classpath:")) {
+ URL url = bundle.getResource(desc.substring("classpath:".length()));
+ if (url == null) {
+ desc = "Unable to load description from " + desc;
+ } else {
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ Reader r = new InputStreamReader(is);
+ StringWriter sw = new StringWriter();
+ int c;
+ while ((c = r.read()) != -1) {
+ sw.append((char) c);
+ }
+ desc = sw.toString();
+ } catch (IOException e) {
+ desc = "Unable to load description from " + desc;
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+ return desc;
+ }
+
+}
Modified: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java?rev=1311786&r1=1311785&r2=1311786&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java (original)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/jline/Console.java Tue Apr 10 15:26:18 2012
@@ -49,6 +49,7 @@ import org.apache.felix.service.command.
import org.apache.karaf.shell.console.CloseShellException;
import org.apache.karaf.shell.console.Completer;
import org.apache.karaf.shell.console.completer.CommandsCompleter;
+import org.apache.karaf.shell.console.util.Branding;
import org.fusesource.jansi.Ansi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -297,7 +298,7 @@ public class Console implements Runnable
}
protected void welcome() {
- Properties props = loadBrandingProperties();
+ Properties props = Branding.loadBrandingProperties();
String welcome = props.getProperty("welcome");
if (welcome != null && welcome.length() > 0) {
session.getConsole().println(welcome);
@@ -305,7 +306,7 @@ public class Console implements Runnable
}
protected void setSessionProperties() {
- Properties props = loadBrandingProperties();
+ Properties props = Branding.loadBrandingProperties();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
String key = (String) entry.getKey();
if (key.startsWith("session.")) {
@@ -318,33 +319,6 @@ public class Console implements Runnable
return new CommandsCompleter(session);
}
- protected Properties loadBrandingProperties() {
- Properties props = new Properties();
- loadProps(props, "org/apache/karaf/shell/console/branding.properties");
- loadProps(props, "org/apache/karaf/branding/branding.properties");
- return props;
- }
-
- protected void loadProps(Properties props, String resource) {
- InputStream is = null;
- try {
- is = getClass().getClassLoader().getResourceAsStream(resource);
- if (is != null) {
- props.load(is);
- }
- } catch (IOException e) {
- // ignore
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- }
- }
-
protected String getPrompt() {
try {
String prompt;
@@ -353,7 +327,7 @@ public class Console implements Runnable
if (p != null) {
prompt = p.toString();
} else {
- Properties properties = loadBrandingProperties();
+ Properties properties = Branding.loadBrandingProperties();
if (properties.getProperty("prompt") != null) {
prompt = properties.getProperty("prompt");
// we put the PROMPT in ConsoleSession to avoid to read
Added: karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/util/Branding.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/util/Branding.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/util/Branding.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/java/org/apache/karaf/shell/console/util/Branding.java Tue Apr 10 15:26:18 2012
@@ -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.console.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+public final class Branding {
+
+ private Branding() { }
+
+ public static Properties loadBrandingProperties() {
+ Properties props = new Properties();
+ loadProps(props, "org/apache/karaf/shell/console/branding.properties");
+ loadProps(props, "org/apache/karaf/branding/branding.properties");
+ return props;
+ }
+
+ protected static void loadProps(Properties props, String resource) {
+ InputStream is = null;
+ try {
+ is = Branding.class.getClassLoader().getResourceAsStream(resource);
+ if (is != null) {
+ props.load(is);
+ }
+ } catch (IOException e) {
+ // ignore
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+}
Modified: karaf/branches/karaf-2.3.x/shell/console/src/main/resources/META-INF/services/org/apache/karaf/shell/commands
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/resources/META-INF/services/org/apache/karaf/shell/commands?rev=1311786&r1=1311785&r2=1311786&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/resources/META-INF/services/org/apache/karaf/shell/commands (original)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/resources/META-INF/services/org/apache/karaf/shell/commands Tue Apr 10 15:26:18 2012
@@ -14,4 +14,4 @@
## See the License for the specific language governing permissions and
## limitations under the License.
##---------------------------------------------------------------------------
-org.apache.karaf.shell.console.HelpAction
+org.apache.karaf.shell.console.help.HelpAction
Modified: karaf/branches/karaf-2.3.x/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml?rev=1311786&r1=1311785&r2=1311786&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml (original)
+++ karaf/branches/karaf-2.3.x/shell/console/src/main/resources/OSGI-INF/blueprint/karaf-console.xml Tue Apr 10 15:26:18 2012
@@ -59,9 +59,45 @@
<property name="blueprintContainer" ref="blueprintContainer"/>
<property name="blueprintConverter" ref="blueprintConverter"/>
<property name="actionId" value="help"/>
+ <property name="completers">
+ <list>
+ <bean class="org.apache.karaf.shell.console.completer.CommandNamesCompleter"/>
+ </list>
+ </property>
+ </bean>
+ </service>
+ <bean id="help" class="org.apache.karaf.shell.console.help.HelpAction" activation="lazy" scope="prototype">
+ <property name="provider" ref="helpSystem"/>
+ </bean>
+
+ <bean id="helpSystem" class="org.apache.karaf.shell.console.help.HelpSystem" init-method="start" destroy-method="stop">
+ <property name="context" ref="blueprintBundleContext"/>
+ </bean>
+
+ <service auto-export="interfaces" ranking="-20">
+ <bean class="org.apache.karaf.shell.console.help.CommandListHelpProvider" />
+ </service>
+ <service auto-export="interfaces" ranking="-10">
+ <bean class="org.apache.karaf.shell.console.help.SingleCommandHelpProvider">
+ <property name="io">
+ <reference interface="org.apache.felix.service.threadio.ThreadIO"/>
+ </property>
+ </bean>
+ </service>
+ <service auto-export="interfaces" ref="subShellHelpProvider" ranking="-10"/>
+ <bean id="subShellHelpProvider" class="org.apache.karaf.shell.console.help.SubShellHelpProvider" init-method="start" destroy-method="stop">
+ <property name="context" ref="blueprintBundleContext"/>
+ </bean>
+ <service auto-export="interfaces" ranking="-5">
+ <bean class="org.apache.karaf.shell.console.help.SimpleHelpProvider">
+ <property name="help">
+ <map>
+ <entry key="%root%"><value><![CDATA[${command-list|}]]></value></entry>
+ <entry key="all"><value><![CDATA[${command-list|}]]></value></entry>
+ </map>
+ </property>
</bean>
</service>
- <bean id="help" class="org.apache.karaf.shell.console.HelpAction" activation="lazy" scope="prototype" />
</blueprint>
Added: karaf/branches/karaf-2.3.x/shell/console/src/test/java/org/apache/karaf/shell/console/help/TestFormatting.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/console/src/test/java/org/apache/karaf/shell/console/help/TestFormatting.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/console/src/test/java/org/apache/karaf/shell/console/help/TestFormatting.java (added)
+++ karaf/branches/karaf-2.3.x/shell/console/src/test/java/org/apache/karaf/shell/console/help/TestFormatting.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,35 @@
+/*
+ * 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.console.help;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import junit.framework.TestCase;
+import org.apache.felix.gogo.commands.basic.DefaultActionPreparator;
+
+public class TestFormatting extends TestCase {
+
+ public void testFormat() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DefaultActionPreparator.printFormatted(" ",
+ " This is a test with a long paragraph\n\n with an indented paragraph\nAnd another one\n", 20, new PrintStream(baos, true));
+ System.err.println(baos.toString());
+ }
+}
Modified: karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/OSGI-INF/blueprint/shell-osgi.xml
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/OSGI-INF/blueprint/shell-osgi.xml?rev=1311786&r1=1311785&r2=1311786&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/OSGI-INF/blueprint/shell-osgi.xml (original)
+++ karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/OSGI-INF/blueprint/shell-osgi.xml Tue Apr 10 15:26:18 2012
@@ -74,6 +74,18 @@
</command>
</command-bundle>
+ <service auto-export="interfaces">
+ <service-properties>
+ <entry key="name" value="osgi"/>
+ <entry key="description" value="Commands to manage the OSGi framework"/>
+ </service-properties>
+ <bean class="org.apache.karaf.shell.console.commands.BasicSubShell">
+ <property name="name" value="osgi"/>
+ <property name="description" value="Commands to manage the OSGi framework"/>
+ <property name="detailedDescription" value="classpath:/org/apache/karaf/shell/osgi/osgi.txt"/>
+ </bean>
+ </service>
+
<bean id="blueprintListener" class="org.apache.karaf.shell.osgi.BlueprintListener" />
<service ref="blueprintListener" interface="org.osgi.service.blueprint.container.BlueprintListener" />
Added: karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/karaf/shell/osgi/osgi.txt
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/karaf/shell/osgi/osgi.txt?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/karaf/shell/osgi/osgi.txt (added)
+++ karaf/branches/karaf-2.3.x/shell/osgi/src/main/resources/org/apache/karaf/shell/osgi/osgi.txt Tue Apr 10 15:26:18 2012
@@ -0,0 +1,5 @@
+The commands in this subshell can be used to inspect or modify the OSGi framework.
+
+The full list of commands is available below and you can obtain a more detailed help for a given command by using the help command followed by the name of the command:
+ > help osgi:list
+
Modified: karaf/branches/karaf-2.3.x/util/pom.xml
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/util/pom.xml?rev=1311786&r1=1311785&r2=1311786&view=diff
==============================================================================
--- karaf/branches/karaf-2.3.x/util/pom.xml (original)
+++ karaf/branches/karaf-2.3.x/util/pom.xml Tue Apr 10 15:26:18 2012
@@ -37,4 +37,11 @@
<appendedResourcesDirectory>${basedir}/../etc/appended-resources</appendedResourcesDirectory>
</properties>
-</project>
\ No newline at end of file
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
Added: karaf/branches/karaf-2.3.x/util/src/main/java/org/apache/karaf/util/InterpolationHelper.java
URL: http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/util/src/main/java/org/apache/karaf/util/InterpolationHelper.java?rev=1311786&view=auto
==============================================================================
--- karaf/branches/karaf-2.3.x/util/src/main/java/org/apache/karaf/util/InterpolationHelper.java (added)
+++ karaf/branches/karaf-2.3.x/util/src/main/java/org/apache/karaf/util/InterpolationHelper.java Tue Apr 10 15:26:18 2012
@@ -0,0 +1,220 @@
+/*
+ * 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.util;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * <p>
+ * Enhancement of the standard <code>Properties</code>
+ * managing the maintain of comments, etc.
+ * </p>
+ *
+ * @author gnodet, jbonofre
+ */
+public class InterpolationHelper {
+
+ private InterpolationHelper() {
+ }
+
+ private static final char ESCAPE_CHAR = '\\';
+ private static final String DELIM_START = "${";
+ private static final String DELIM_STOP = "}";
+
+
+ /**
+ * Callback for substitution
+ */
+ public interface SubstitutionCallback {
+
+ public String getValue(String key);
+
+ }
+
+ /**
+ * Perform substitution on a property set
+ *
+ * @param properties the property set to perform substitution on
+ */
+ public static void performSubstitution(Map<String, String> properties) {
+ performSubstitution(properties, (BundleContext) null);
+ }
+
+ /**
+ * Perform substitution on a property set
+ *
+ * @param properties the property set to perform substitution on
+ */
+ public static void performSubstitution(Map<String, String> properties, final BundleContext context) {
+ performSubstitution(properties, new SubstitutionCallback() {
+ public String getValue(String key) {
+ String value = null;
+ if (context != null) {
+ value = context.getProperty(key);
+ }
+ if (value == null) {
+ value = System.getProperty(value, "");
+ }
+ return value;
+ }
+ });
+ }
+
+ /**
+ * Perform substitution on a property set
+ *
+ * @param properties the property set to perform substitution on
+ */
+ public static void performSubstitution(Map<String, String> properties, SubstitutionCallback callback) {
+ for (String name : properties.keySet()) {
+ String value = properties.get(name);
+ properties.put(name, substVars(value, name, null, properties, callback));
+ }
+ }
+
+
+ /**
+ * <p>
+ * This method performs property variable substitution on the
+ * specified value. If the specified value contains the syntax
+ * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt>
+ * refers to either a configuration property or a system property,
+ * then the corresponding property value is substituted for the variable
+ * placeholder. Multiple variable placeholders may exist in the
+ * specified value as well as nested variable placeholders, which
+ * are substituted from inner most to outer most. Configuration
+ * properties override system properties.
+ * </p>
+ *
+ * @param val The string on which to perform property substitution.
+ * @param currentKey The key of the property being evaluated used to
+ * detect cycles.
+ * @param cycleMap Map of variable references used to detect nested cycles.
+ * @param configProps Set of configuration properties.
+ * @param callback the callback to obtain substitution values
+ * @return The value of the specified string after system property substitution.
+ * @throws IllegalArgumentException If there was a syntax error in the
+ * property placeholder syntax or a recursive variable reference.
+ */
+ public static String substVars(String val,
+ String currentKey,
+ Map<String, String> cycleMap,
+ Map<String, String> configProps,
+ SubstitutionCallback callback)
+ throws IllegalArgumentException {
+ if (cycleMap == null) {
+ cycleMap = new HashMap<String, String>();
+ }
+
+ // Put the current key in the cycle map.
+ cycleMap.put(currentKey, currentKey);
+
+ // Assume we have a value that is something like:
+ // "leading ${foo.${bar}} middle ${baz} trailing"
+
+ // Find the first ending '}' variable delimiter, which
+ // will correspond to the first deepest nested variable
+ // placeholder.
+ int stopDelim = val.indexOf(DELIM_STOP);
+ while (stopDelim > 0 && val.charAt(stopDelim - 1) == ESCAPE_CHAR) {
+ stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
+ }
+
+ // Find the matching starting "${" variable delimiter
+ // by looping until we find a start delimiter that is
+ // greater than the stop delimiter we have found.
+ int startDelim = val.indexOf(DELIM_START);
+ while (stopDelim >= 0) {
+ int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
+ if ((idx < 0) || (idx > stopDelim)) {
+ break;
+ } else if (idx < stopDelim) {
+ startDelim = idx;
+ }
+ }
+
+ // If we do not have a start or stop delimiter, then just
+ // return the existing value.
+ if ((startDelim < 0) || (stopDelim < 0)) {
+ return unescape(val);
+ }
+
+ // At this point, we have found a variable placeholder so
+ // we must perform a variable substitution on it.
+ // Using the start and stop delimiter indices, extract
+ // the first, deepest nested variable placeholder.
+ String variable = val.substring(startDelim + DELIM_START.length(), stopDelim);
+
+ // Verify that this is not a recursive variable reference.
+ if (cycleMap.get(variable) != null) {
+ throw new IllegalArgumentException("recursive variable reference: " + variable);
+ }
+
+ // Get the value of the deepest nested variable placeholder.
+ // Try to configuration properties first.
+ String substValue = (String) ((configProps != null) ? configProps.get(variable) : null);
+ if (substValue == null) {
+ if (variable.length() <= 0) {
+ substValue = "";
+ } else {
+ if (callback != null) {
+ substValue = callback.getValue(variable);
+ }
+ if (substValue == null) {
+ substValue = System.getProperty(variable, "");
+ }
+ }
+ }
+
+ // Remove the found variable from the cycle map, since
+ // it may appear more than once in the value and we don't
+ // want such situations to appear as a recursive reference.
+ cycleMap.remove(variable);
+
+ // Append the leading characters, the substituted value of
+ // the variable, and the trailing characters to get the new
+ // value.
+ val = val.substring(0, startDelim) + substValue + val.substring(stopDelim + DELIM_STOP.length(), val.length());
+
+ // Now perform substitution again, since there could still
+ // be substitutions to make.
+ val = substVars(val, currentKey, cycleMap, configProps, callback);
+
+ // Remove escape characters preceding {, } and \
+ val = unescape(val);
+
+ // Return the value.
+ return val;
+ }
+
+ private static String unescape(String val) {
+ int escape = val.indexOf(ESCAPE_CHAR);
+ while (escape >= 0 && escape < val.length() - 1) {
+ char c = val.charAt(escape + 1);
+ if (c == '{' || c == '}' || c == ESCAPE_CHAR) {
+ val = val.substring(0, escape) + val.substring(escape + 1);
+ }
+ escape = val.indexOf(ESCAPE_CHAR, escape + 1);
+ }
+ return val;
+ }
+
+}