Add missing options to requirement-add/-remove, and format the json output repository so that it’s readable
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
index abcff31..9df243b 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/InstallFeatureCommand.java
@@ -31,8 +31,6 @@
 @Service
 public class InstallFeatureCommand extends FeaturesCommandSupport {
 
-    private static String DEFAULT_VERSION = "0.0.0";
-
     @Argument(index = 0, name = "features", description = "The name and version of the features to install. A feature id looks like name/version. The version is optional.", required = true, multiValued = true)
     @Completion(AvailableFeatureCompleter.class)
     List<String> features;
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/RequirementAdd.java b/features/command/src/main/java/org/apache/karaf/features/command/RequirementAdd.java
index 12b8917..78a6289 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/RequirementAdd.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/RequirementAdd.java
@@ -50,6 +50,15 @@
     @Option(name = "-t", aliases = "--simulate", description = "Perform a simulation only")
     boolean simulate;
 
+    @Option(name = "--store", description = "Store the resolution into the given file and result for offline analysis")
+    String outputFile;
+
+    @Option(name = "--features-wiring", description = "Print the wiring between features")
+    boolean featuresWiring;
+
+    @Option(name = "--all-wiring", description = "Print the full wiring")
+    boolean allWiring;
+
     @Option(name = "-g", aliases = "--region", description = "Region to install to")
     String region = FeaturesService.ROOT_REGION;
 
@@ -60,8 +69,11 @@
         addOption(FeaturesService.Option.NoAutoRefreshBundles, noRefresh);
         addOption(FeaturesService.Option.NoAutoManageBundles, noManage);
         addOption(FeaturesService.Option.Verbose, verbose);
+        addOption(FeaturesService.Option.DisplayFeaturesWiring, featuresWiring);
+        addOption(FeaturesService.Option.DisplayAllWiring, allWiring);
         Map<String, Set<String>> reqs = new HashMap<>();
         reqs.put(region, new HashSet<>(requirements));
+        featuresService.setResolutionOutputFile(outputFile);
         featuresService.addRequirements(reqs, options);
     }
 
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/RequirementRemove.java b/features/command/src/main/java/org/apache/karaf/features/command/RequirementRemove.java
index ad800e0..11f700b 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/RequirementRemove.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/RequirementRemove.java
@@ -50,6 +50,15 @@
     @Option(name = "-t", aliases = "--simulate", description = "Perform a simulation only")
     boolean simulate;
 
+    @Option(name = "--store", description = "Store the resolution into the given file and result for offline analysis")
+    String outputFile;
+
+    @Option(name = "--features-wiring", description = "Print the wiring between features")
+    boolean featuresWiring;
+
+    @Option(name = "--all-wiring", description = "Print the full wiring")
+    boolean allWiring;
+
     @Option(name = "-g", aliases = "--region", description = "Region to apply to")
     String region = FeaturesService.ROOT_REGION;
 
@@ -60,8 +69,11 @@
         addOption(FeaturesService.Option.NoAutoRefreshBundles, noRefresh);
         addOption(FeaturesService.Option.NoAutoManageBundles, noManage);
         addOption(FeaturesService.Option.Verbose, verbose);
+        addOption(FeaturesService.Option.DisplayFeaturesWiring, featuresWiring);
+        addOption(FeaturesService.Option.DisplayAllWiring, allWiring);
         Map<String, Set<String>> reqs = new HashMap<>();
         reqs.put(region, new HashSet<>(requirements));
+        featuresService.setResolutionOutputFile(outputFile);
         featuresService.removeRequirements(reqs, options);
     }
 
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
index d162e9e..3f83733 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java
@@ -209,7 +209,7 @@
                         Paths.get(outputFile),
                         StandardCharsets.UTF_8,
                         StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
-                    JsonWriter.write(writer, json);
+                    JsonWriter.write(writer, json, true);
                 }
             }
         } else {
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
index fb27346..b65ea27 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/JsonWriter.java
@@ -20,6 +20,7 @@
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Map;
 
@@ -31,16 +32,29 @@
     }
 
     public static void write(OutputStream stream, Object value) throws IOException {
+        write(stream, value, false);
+    }
+
+    public static void write(OutputStream stream, Object value, boolean format) throws IOException {
         Writer writer = new OutputStreamWriter(stream);
-        write(writer, value);
+        write(writer, value, format);
         writer.flush();
     }
 
     public static void write(Writer writer, Object value) throws IOException {
+        write(writer, value, false);
+    }
+
+    public static void write(Writer writer, Object value, boolean format) throws IOException {
+        int indent = format ? 0 : -1;
+        write(writer, value, indent);
+    }
+
+    private static void write(Writer writer, Object value, int indent) throws IOException {
         if (value instanceof Map) {
-            writeObject(writer, (Map) value);
+            writeObject(writer, (Map) value, indent);
         } else if (value instanceof Collection) {
-            writeArray(writer, (Collection) value);
+            writeArray(writer, (Collection) value, indent);
         } else if (value instanceof Number) {
             writeNumber(writer, (Number) value);
         } else if (value instanceof String) {
@@ -54,7 +68,7 @@
         }
     }
 
-    private static void writeObject(Writer writer, Map<?, ?> value) throws IOException {
+    private static void writeObject(Writer writer, Map<?, ?> value, int indent) throws IOException {
         writer.append('{');
         boolean first = true;
         for (Map.Entry entry : value.entrySet()) {
@@ -63,9 +77,21 @@
             } else {
                 first = false;
             }
+            if (indent >= 0) {
+                indent(writer, indent + 1);
+            }
             writeString(writer, (String) entry.getKey());
+            if (indent >= 0) {
+                writer.append(' ');
+            }
             writer.append(':');
-            write(writer, entry.getValue());
+            if (indent >= 0) {
+                writer.append(' ');
+            }
+            write(writer, entry.getValue(), indent + 1);
+        }
+        if (indent >= 0) {
+            indent(writer, indent);
         }
         writer.append('}');
     }
@@ -122,7 +148,7 @@
         writer.append(Boolean.toString(value));
     }
 
-    private static void writeArray(Writer writer, Collection<?> value) throws IOException {
+    private static void writeArray(Writer writer, Collection<?> value, int indent) throws IOException {
         writer.append('[');
         boolean first = true;
         for (Object obj : value) {
@@ -131,7 +157,13 @@
             } else {
                 first = false;
             }
-            write(writer, obj);
+            if (indent >= 0) {
+                indent(writer, indent + 1);
+            }
+            write(writer, obj, indent + 1);
+        }
+        if (indent >= 0) {
+            indent(writer, indent);
         }
         writer.append(']');
     }
@@ -139,4 +171,20 @@
     private static void writeNull(Writer writer) throws IOException {
         writer.append("null");
     }
+
+    static char[] INDENT;
+    static {
+        INDENT = new char[1];
+        Arrays.fill(INDENT, '\t');
+    }
+
+    private static void indent(Writer writer, int indent) throws IOException {
+        writer.write("\n");
+        while (indent > INDENT.length) {
+            char[] a = new char[INDENT.length * 2];
+            Arrays.fill(a, '\t');
+            INDENT = a;
+        }
+        writer.write(INDENT, 0, indent);
+    }
 }