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 2017/10/04 15:49:40 UTC

[3/3] karaf git commit: [KARAF-5407] Allow feature:info to print the xml for a given feature

[KARAF-5407] Allow feature:info to print the xml for a given feature


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/880b838c
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/880b838c
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/880b838c

Branch: refs/heads/master
Commit: 880b838cb014120eb327f51baf37887a4ede6d9c
Parents: d586939
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Wed Oct 4 16:09:37 2017 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Wed Oct 4 16:55:33 2017 +0200

----------------------------------------------------------------------
 features/command/pom.xml                        |   4 +
 .../features/command/InfoFeatureCommand.java    | 117 ++++++++++++++++++-
 .../apache/karaf/features/FeaturesService.java  |   2 +
 .../features/internal/model/Dependency.java     |  12 +-
 .../internal/service/FeaturesServiceImpl.java   |  24 ++++
 5 files changed, 149 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/880b838c/features/command/pom.xml
----------------------------------------------------------------------
diff --git a/features/command/pom.xml b/features/command/pom.xml
index 9a02a27..d258d37 100644
--- a/features/command/pom.xml
+++ b/features/command/pom.xml
@@ -57,6 +57,10 @@
             <artifactId>org.apache.karaf.shell.core</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.eclipse.equinox</groupId>
             <artifactId>org.eclipse.equinox.region</artifactId>
             <scope>provided</scope>

http://git-wip-us.apache.org/repos/asf/karaf/blob/880b838c/features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java
----------------------------------------------------------------------
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java
index a6cdccd..1ecc0a0 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/InfoFeatureCommand.java
@@ -16,9 +16,17 @@
  */
 package org.apache.karaf.features.command;
 
+import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
+import org.apache.felix.service.command.Process;
 import org.apache.karaf.features.BundleInfo;
 import org.apache.karaf.features.Conditional;
 import org.apache.karaf.features.ConfigFileInfo;
@@ -31,12 +39,19 @@ import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Completion;
 import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.Session;
+import org.jline.terminal.Terminal;
+import org.jline.utils.AttributedStringBuilder;
+import org.jline.utils.AttributedStyle;
 
 @Command(scope = "feature", name = "info", description = "Shows information about selected feature.")
 @Service
 public class InfoFeatureCommand extends FeaturesCommandSupport {
 
+    public static final String DEFAULT_XML_COLORS = "el=34;1:at=36;1:av=32;1:cm=37:cd=37";
+
     private static final String INDENT = "  ";
     private static final String FEATURE_CONTENT = "Feature";
     private static final String CONDITIONAL_CONTENT = "Conditional(%s)";
@@ -63,6 +78,15 @@ public class InfoFeatureCommand extends FeaturesCommandSupport {
     @Option(name = "-t", aliases={"--tree"}, description="Display feature tree", required = false, multiValued = false)
     private boolean tree;
 
+    @Option(name = "-x", aliases={"--xml"}, description="Display feature xml", required = false, multiValued = false)
+    private boolean xml;
+
+    @Option(name = "--color", description="Colorize output (`always', `never' or `auto')", required = false, multiValued = false)
+    private String color;
+
+    @Reference
+    Session session;
+
     protected void doExecute(FeaturesService admin) throws Exception {
         Feature[] features = null;
 
@@ -77,8 +101,39 @@ public class InfoFeatureCommand extends FeaturesCommandSupport {
             return;
         }
 
+        if (xml) {
+            boolean colored;
+            switch (color != null ? color : "auto") {
+                case "always":
+                case "yes":
+                case "force":
+                    colored = true;
+                    break;
+                case "never":
+                case "no":
+                case "none":
+                    colored = false;
+                    break;
+                case "auto":
+                case "tty":
+                case "if-tty":
+                    colored = Process.Utils.current().isTty(1);
+                    break;
+                default:
+                    throw new IllegalArgumentException("invalid argument ‘" + color + "’ for ‘--color’");
+            }
+            for (Feature feature : features) {
+                String xml = admin.getFeatureXml(feature);
+                if (colored) {
+                    xml = colorize(session, xml);
+                }
+                System.out.println(xml);
+            }
+            return;
+        }
+
         // default behavior
-        if (!config && !dependency && !bundle && !conditional) {
+        if (!config && !dependency && !bundle && !conditional && !tree) {
             config = true;
             dependency = true;
             bundle = true;
@@ -121,7 +176,7 @@ public class InfoFeatureCommand extends FeaturesCommandSupport {
             }
 
             if (tree) {
-                if (config || dependency || bundle) {
+                if (config || dependency || bundle || conditional) {
                     System.out.println("\nFeature tree");
                 }
 
@@ -241,7 +296,7 @@ public class InfoFeatureCommand extends FeaturesCommandSupport {
                 prefix += "   ";
                 List<Dependency> dependencies = resolved.getDependencies();
                 for (Dependency toDisplay : dependencies) {
-                    unresolved += displayFeatureTree(admin, toDisplay.getName(), toDisplay.getVersion(), prefix + 1);
+                    unresolved += displayFeatureTree(admin, toDisplay.getName(), toDisplay.getVersion(), prefix);
                 }
 
                 if (conditional) {
@@ -249,7 +304,7 @@ public class InfoFeatureCommand extends FeaturesCommandSupport {
                         List<Dependency> conditionDependencies = cond.getDependencies();
                         for (int i = 0, j = conditionDependencies.size(); i < j; i++) {
                             Dependency toDisplay = dependencies.get(i);
-                            unresolved += displayFeatureTree(admin, toDisplay.getName(), toDisplay.getVersion(), prefix + 1);
+                            unresolved += displayFeatureTree(admin, toDisplay.getName(), toDisplay.getVersion(), prefix);
                         }
                     }
                 }
@@ -295,4 +350,58 @@ public class InfoFeatureCommand extends FeaturesCommandSupport {
         return sb.toString();
     }
 
+    private static Map<String, String> getColorMap(Session session, String name, String def) {
+        Object obj = session.get(name + "_COLORS");
+        String str = obj != null ? obj.toString() : null;
+        if (str == null || !str.matches("[a-z]{2}=[0-9]*(;[0-9]+)*(:[a-z]{2}=[0-9]*(;[0-9]+)*)*")) {
+            str = def;
+        }
+        return Arrays.stream(str.split(":"))
+                .collect(Collectors.toMap(s -> s.substring(0, s.indexOf('=')),
+                        s -> s.substring(s.indexOf('=') + 1)));
+    }
+
+    private static String colorize(Session session, String xml) {
+        Terminal terminal = (Terminal) session.get(".jline.terminal");
+        Map<String, String> colorMap = getColorMap(session, "XML", DEFAULT_XML_COLORS);
+        Map<Pattern, String> patternColors = new LinkedHashMap<>();
+        patternColors.put(Pattern.compile("(</?[a-z]*)\\s?>?"),         "el");
+        patternColors.put(Pattern.compile("(/?>)"),                     "el");
+        patternColors.put(Pattern.compile("\\s([a-z-]*)\\="),           "at");
+        patternColors.put(Pattern.compile("[a-z-]*\\=(\"[^\"]*\")"),    "av");
+        patternColors.put(Pattern.compile("(<!--.*-->)"),               "cm");
+        patternColors.put(Pattern.compile("(\\<!\\[CDATA\\[).*"),       "cd");
+        patternColors.put(Pattern.compile(".*(]]>)"),                   "cd");
+        String[] styles = new String[xml.length()];
+        // Match all regexes on this snippet, store positions
+        for (Map.Entry<Pattern, String> entry : patternColors.entrySet()) {
+            Matcher matcher = entry.getKey().matcher(xml);
+            while (matcher.find()) {
+                int s = matcher.start(1);
+                int e = matcher.end();
+                String c = entry.getValue();
+                Arrays.fill(styles, s, e, c);
+            }
+        }
+        AttributedStringBuilder asb = new AttributedStringBuilder();
+        String prev = null;
+        for (int i = 0; i < xml.length(); i++) {
+            String s = styles[i];
+            if (!Objects.equals(s, prev)) {
+                applyStyle(asb, colorMap, s);
+                prev = s;
+            }
+            asb.append(xml.charAt(i));
+        }
+        return asb.toAnsi(terminal);
+    }
+
+    private static void applyStyle(AttributedStringBuilder sb, Map<String, String> colors, String type) {
+        String col = colors.get(type);
+        sb.style(AttributedStyle.DEFAULT);
+        if (col != null && !col.isEmpty()) {
+            sb.appendAnsi("\033[" + col + "m");
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/880b838c/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
index 29c9093..da8643b 100644
--- a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
+++ b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java
@@ -170,4 +170,6 @@ public interface FeaturesService {
 
     FeatureState getState(String featureId);
 
+    String getFeatureXml(Feature feature);
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/880b838c/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java b/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
index 3d81425..d9b9272 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/model/Dependency.java
@@ -46,9 +46,9 @@ public class Dependency implements org.apache.karaf.features.Dependency {
     @XmlAttribute
     protected String version;
     @XmlAttribute
-    protected boolean prerequisite;
+    protected Boolean prerequisite;
     @XmlAttribute
-    protected boolean dependency;
+    protected Boolean dependency;
 
     public Dependency() {
         // Nothing to do
@@ -115,19 +115,19 @@ public class Dependency implements org.apache.karaf.features.Dependency {
 
     @Override
     public boolean isPrerequisite() {
-        return prerequisite;
+        return prerequisite == null ? false : prerequisite;
     }
 
-    public void setPrerequisite(boolean prerequisite) {
+    public void setPrerequisite(Boolean prerequisite) {
         this.prerequisite = prerequisite;
     }
 
     @Override
     public boolean isDependency() {
-        return dependency;
+        return dependency == null ? false : dependency;
     }
 
-    public void setDependency(boolean dependency) {
+    public void setDependency(Boolean dependency) {
         this.dependency = dependency;
     }
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/880b838c/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
index cbb93c3..3e38a6d 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
@@ -21,6 +21,7 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.StringWriter;
 import java.net.URI;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -37,6 +38,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
+import java.util.StringJoiner;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.ExecutionException;
@@ -60,6 +62,8 @@ import org.apache.karaf.features.Repository;
 import org.apache.karaf.features.RepositoryEvent;
 import org.apache.karaf.features.internal.download.DownloadManager;
 import org.apache.karaf.features.internal.download.DownloadManagers;
+import org.apache.karaf.features.internal.model.Features;
+import org.apache.karaf.features.internal.model.JaxbUtil;
 import org.apache.karaf.features.internal.region.DigraphHelper;
 import org.apache.karaf.features.internal.service.BundleInstallSupport.FrameworkInfo;
 import org.apache.karaf.util.ThreadUtils;
@@ -80,6 +84,8 @@ import org.osgi.service.resolver.Resolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.xml.bind.JAXBException;
+
 import static java.util.Collections.emptyMap;
 import static java.util.stream.Collectors.toSet;
 import static org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION;
@@ -1103,4 +1109,22 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
     private String join(Collection<FeatureReq> reqs) {
         return reqs.stream().map(FeatureReq::toString).collect(Collectors.joining(","));
     }
+
+    @Override
+    public String getFeatureXml(Feature feature) {
+        try {
+            StringWriter sw = new StringWriter();
+            Features r = new Features();
+            r.getFeature().add((org.apache.karaf.features.internal.model.Feature) feature);
+            JaxbUtil.marshal(r, sw);
+            String[] strs = sw.toString().split("\n");
+            StringJoiner joiner = new StringJoiner("\n");
+            for (int i = 2; i < strs.length - 1; i++) {
+                joiner.add(strs[i]);
+            }
+            return joiner.toString();
+        } catch (JAXBException e) {
+            return null;
+        }
+    }
 }