Initial pass at implementing a consistent "inspect" command for pre/post
OSGi R4.3. (FELIX-3050)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1160444 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/command/pom.xml b/gogo/command/pom.xml
index 29fba69..ce070d3 100644
--- a/gogo/command/pom.xml
+++ b/gogo/command/pom.xml
@@ -39,7 +39,7 @@
     <dependency>
       <groupId>org.osgi</groupId>
       <artifactId>org.osgi.core</artifactId>
-      <version>4.2.0</version>
+      <version>4.3.0</version>
     </dependency>
     <dependency>
       <groupId>org.osgi</groupId>
diff --git a/gogo/command/src/main/java/org/apache/felix/gogo/command/Activator.java b/gogo/command/src/main/java/org/apache/felix/gogo/command/Activator.java
index 5028803..47bc06c 100644
--- a/gogo/command/src/main/java/org/apache/felix/gogo/command/Activator.java
+++ b/gogo/command/src/main/java/org/apache/felix/gogo/command/Activator.java
@@ -33,7 +33,7 @@
         props.put("osgi.command.scope", "felix");
         props.put("osgi.command.function", new String[] {
             "bundlelevel", "frameworklevel", "headers",
-            "help", "install", "inspect", "lb", "log", "refresh",
+            "help", "install", "in", "inspect", "lb", "log", "refresh",
             "resolve", "start", "stop", "uninstall", "update",
             "which" });
         bc.registerService(
diff --git a/gogo/command/src/main/java/org/apache/felix/gogo/command/Basic.java b/gogo/command/src/main/java/org/apache/felix/gogo/command/Basic.java
index 88931cd..b2f3916 100644
--- a/gogo/command/src/main/java/org/apache/felix/gogo/command/Basic.java
+++ b/gogo/command/src/main/java/org/apache/felix/gogo/command/Basic.java
@@ -414,13 +414,22 @@
         return commands;
     }
 
-    @Descriptor("inspects bundle dependency information")
-    public void inspect(
-        @Descriptor("(package | bundle | fragment | service)") String type,
-        @Descriptor("(capability | requirement)") String direction,
+    @Descriptor("inspects bundle capabilities and requirements")
+    public void in(
+        @Descriptor("('capability' | 'requirement')") String direction,
+        @Descriptor("(<namespace> | 'service')") String namespace,
         @Descriptor("target bundles") Bundle[] bundles)
     {
-        Inspect.inspect(m_bc, type, direction, bundles);
+        Inspect.in(m_bc, direction, namespace, bundles);
+    }
+
+    @Descriptor("inspects bundle capabilities and requirements")
+    public void inspect(
+        @Descriptor("('capability' | 'requirement')") String direction,
+        @Descriptor("('package' | 'bundle' | 'host' | 'service')") String namespace,
+        @Descriptor("target bundles") Bundle[] bundles)
+    {
+        Inspect.inspect(m_bc, direction, namespace, bundles);
     }
 
     @Descriptor("install bundle using URLs")
diff --git a/gogo/command/src/main/java/org/apache/felix/gogo/command/Inspect.java b/gogo/command/src/main/java/org/apache/felix/gogo/command/Inspect.java
index 6f21baf..fec6c0d 100644
--- a/gogo/command/src/main/java/org/apache/felix/gogo/command/Inspect.java
+++ b/gogo/command/src/main/java/org/apache/felix/gogo/command/Inspect.java
@@ -19,84 +19,57 @@
 package org.apache.felix.gogo.command;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
+import java.util.Map;
+import java.util.Map.Entry;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.service.packageadmin.RequiredBundle;
 
 public class Inspect
 {
-    public static final String PACKAGE_TYPE = "package";
-    public static final String BUNDLE_TYPE = "bundle";
-    public static final String FRAGMENT_TYPE = "fragment";
-    public static final String SERVICE_TYPE = "service";
+    public static final String LEGACY_PACKAGE_NAMESPACE = "package";
+    public static final String LEGACY_BUNDLE_NAMESPACE = "bundle";
+    public static final String LEGACY_HOST_NAMESPACE = "host";
+    public static final String NONSTANDARD_SERVICE_NAMESPACE = "service";
 
     public static final String CAPABILITY = "capability";
     public static final String REQUIREMENT = "requirement";
 
-    public static void inspect(
-        BundleContext bc, String type, String direction, Bundle[] bundles)
+    private static final String EMPTY_MESSAGE = "[EMPTY]";
+    private static final String UNUSED_MESSAGE = "[UNUSED]";
+    private static final String UNRESOLVED_MESSAGE = "[UNRESOLVED]";
+
+    public static void in(
+        BundleContext bc, String direction, String namespace, Bundle[] bundles)
     {
         // Verify arguments.
-        if (isValidType(type) && isValidDirection(direction))
+        if (isValidDirection(direction))
         {
-            // Now determine what needs to be printed.
-            if (PACKAGE_TYPE.startsWith(type))
+            bundles = ((bundles == null) || (bundles.length == 0))
+                ? bc.getBundles() : bundles;
+
+            if (CAPABILITY.startsWith(direction))
             {
-                if (CAPABILITY.startsWith(direction))
-                {
-                    printExportedPackages(bc, bundles);
-                }
-                else
-                {
-                    printImportedPackages(bc, bundles);
-                }
-            }
-            else if (BUNDLE_TYPE.startsWith(type))
-            {
-                if (CAPABILITY.startsWith(direction))
-                {
-                    printRequiringBundles(bc, bundles);
-                }
-                else
-                {
-                    printRequiredBundles(bc, bundles);
-                }
-            }
-            else if (FRAGMENT_TYPE.startsWith(type))
-            {
-                if (CAPABILITY.startsWith(direction))
-                {
-                    printFragmentHosts(bc, bundles);
-                }
-                else
-                {
-                    printHostedFragments(bc, bundles);
-                }
+                printCapabilities(bc, Util.parseSubstring(namespace), bundles);
             }
             else
             {
-                if (CAPABILITY.startsWith(direction))
-                {
-                    printExportedServices(bc, bundles);
-                }
-                else
-                {
-                    printImportedServices(bc, bundles);
-                }
+                printRequirements(bc, Util.parseSubstring(namespace), bundles);
             }
         }
         else
         {
-            if (!isValidType(type))
-            {
-                System.out.println("Invalid argument: " + type);
-            }
             if (!isValidDirection(direction))
             {
                 System.out.println("Invalid argument: " + direction);
@@ -104,199 +77,492 @@
         }
     }
 
-    public static void printExportedPackages(BundleContext bc, Bundle[] bundles)
+    public static void printCapabilities(
+        BundleContext bc, List<String> namespace, Bundle[] bundles)
     {
-        // Keep track of service references.
-        List<ServiceReference> refs = new ArrayList();
-
-        // Get package admin service.
-        PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
-        if (pa == null)
+        boolean separatorNeeded = false;
+        for (Bundle b : bundles)
         {
-            System.out.println("PackageAdmin service is unavailable.");
-        }
-        else
-        {
-            boolean separatorNeeded = false;
-
-            if ((bundles == null) || (bundles.length == 0))
+            // Print out any matching generic capabilities.
+            BundleWiring wiring = b.adapt(BundleWiring.class);
+            if (wiring != null)
             {
-                bundles = bc.getBundles();
-            }
-
-            for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
-            {
-                try
+                if (separatorNeeded)
                 {
-                    if (bundles[bundleIdx] != null)
+                    System.out.println("");
+                }
+                String title = b + " provides:";
+                System.out.println(title);
+                System.out.println(Util.getUnderlineString(title.length()));
+
+                // Print generic capabilities for matching namespaces.
+                boolean matches = printMatchingCapabilities(wiring, namespace);
+
+                // Handle service capabilities separately, since they aren't part
+                // of the generic model in OSGi.
+                if (matchNamespace(namespace, NONSTANDARD_SERVICE_NAMESPACE))
+                {
+                    matches |= printServiceCapabilities(b);
+                }
+
+                // If there were no capabilities for the specified namespace,
+                // then say so.
+                if (!matches)
+                {
+                    System.out.println(Util.unparseSubstring(namespace) + " " + EMPTY_MESSAGE);
+                }
+                separatorNeeded = true;
+            }
+            else
+            {
+                System.out.println("Bundle "
+                    + b.getBundleId()
+                    + " is apparently not resolved.");
+            }
+        }
+    }
+
+    private static boolean printMatchingCapabilities(BundleWiring wiring, List<String> namespace)
+    {
+        List<BundleWire> wires = wiring.getProvidedWires(null);
+        Map<BundleCapability, List<BundleWire>> aggregateCaps =
+            aggregateCapabilities(namespace, wires);
+        List<BundleCapability> allCaps = wiring.getCapabilities(null);
+        boolean matches = false;
+        for (BundleCapability cap : allCaps)
+        {
+            if (matchNamespace(namespace, cap.getNamespace()))
+            {
+                matches = true;
+                List<BundleWire> dependents = aggregateCaps.get(cap);
+                Object keyAttr =
+                    cap.getAttributes().get(cap.getNamespace());
+                if (dependents != null)
+                {
+                    String msg;
+                    if (keyAttr != null)
                     {
-                        ExportedPackage[] exports = pa.getExportedPackages(bundles[bundleIdx]);
-                        if (separatorNeeded)
+                        msg = cap.getNamespace()
+                            + "; "
+                            + keyAttr
+                            + getVersionFromCapability(cap);
+                    }
+                    else
+                    {
+                        msg = cap.toString();
+                    }
+                    msg = msg + " required by:";
+                    System.out.println(msg);
+                    for (BundleWire wire : dependents)
+                    {
+                        System.out.println("   " + wire.getRequirerWiring().getBundle());
+                    }
+                }
+                else if (keyAttr != null)
+                {
+                    System.out.println(cap.getNamespace()
+                        + "; "
+                        + cap.getAttributes().get(cap.getNamespace())
+                        + getVersionFromCapability(cap)
+                        + " "
+                        + UNUSED_MESSAGE);
+                }
+                else
+                {
+                    System.out.println(cap + " " + UNUSED_MESSAGE);
+                }
+            }
+        }
+        return matches;
+    }
+
+    private static Map<BundleCapability, List<BundleWire>> aggregateCapabilities(
+        List<String> namespace, List<BundleWire> wires)
+    {
+        // Aggregate matching capabilities.
+        Map<BundleCapability, List<BundleWire>> map =
+            new HashMap<BundleCapability, List<BundleWire>>();
+        for (BundleWire wire : wires)
+        {
+            if (matchNamespace(namespace, wire.getCapability().getNamespace()))
+            {
+                List<BundleWire> dependents = map.get(wire.getCapability());
+                if (dependents == null)
+                {
+                    dependents = new ArrayList<BundleWire>();
+                    map.put(wire.getCapability(), dependents);
+                }
+                dependents.add(wire);
+            }
+        }
+        return map;
+    }
+
+    private static boolean printServiceCapabilities(Bundle b)
+    {
+        boolean matches = false;
+
+        try
+        {
+            ServiceReference[] refs = b.getRegisteredServices();
+
+            if ((refs != null) && (refs.length > 0))
+            {
+                matches = true;
+                // Print properties for each service.
+                for (ServiceReference ref : refs)
+                {
+                    // Print object class with "namespace".
+                    System.out.println(
+                        NONSTANDARD_SERVICE_NAMESPACE
+                        + "; "
+                        + Util.getValueString(ref.getProperty("objectClass"))
+                        + " with properties:");
+                    // Print service properties.
+                    String[] keys = ref.getPropertyKeys();
+                    for (String key : keys)
+                    {
+                        if (!key.equalsIgnoreCase(Constants.OBJECTCLASS))
                         {
-                            System.out.println("");
+                            Object v = ref.getProperty(key);
+                            System.out.println("   "
+                                + key + " = " + Util.getValueString(v));
                         }
-                        String title = bundles[bundleIdx] + " exports packages:";
-                        System.out.println(title);
-                        System.out.println(Util.getUnderlineString(title.length()));
-                        if ((exports != null) && (exports.length > 0))
+                    }
+                    Bundle[] users = ref.getUsingBundles();
+                    if ((users != null) && (users.length > 0))
+                    {
+                        System.out.println("   Used by:");
+                        for (Bundle user : users)
                         {
-                            for (int expIdx = 0; expIdx < exports.length; expIdx++)
-                            {
-                                System.out.print(exports[expIdx]);
-                                Bundle[] importers = exports[expIdx].getImportingBundles();
-                                if ((importers == null) || (importers.length == 0))
-                                {
-                                    System.out.println(" UNUSED");
-                                }
-                                else
-                                {
-                                    System.out.println(" imported by:");
-                                    for (int impIdx = 0; impIdx < importers.length; impIdx++)
-                                    {
-                                        System.out.println("   " + importers[impIdx]);
-                                    }
-                                }
-                            }
+                            System.out.println("      " + user);
+                        }
+                    }
+                }
+            }
+        }
+        catch (Exception ex)
+        {
+            System.err.println(ex.toString());
+        }
+
+        return matches;
+    }
+
+    public static void printRequirements(
+        BundleContext bc, List<String> namespace, Bundle[] bundles)
+    {
+        boolean separatorNeeded = false;
+        for (Bundle b : bundles)
+        {
+            // Print out any matching generic requirements.
+            BundleWiring wiring = b.adapt(BundleWiring.class);
+            if (wiring != null)
+            {
+                if (separatorNeeded)
+                {
+                    System.out.println("");
+                }
+                String title = b + " requires:";
+                System.out.println(title);
+                System.out.println(Util.getUnderlineString(title.length()));
+                boolean matches = printMatchingRequirements(wiring, namespace);
+
+                // Handle service requirements separately, since they aren't part
+                // of the generic model in OSGi.
+                if (matchNamespace(namespace, NONSTANDARD_SERVICE_NAMESPACE))
+                {
+                    matches |= printServiceRequirements(b);
+                }
+
+                // If there were no requirements for the specified namespace,
+                // then say so.
+                if (!matches)
+                {
+                    System.out.println(Util.unparseSubstring(namespace) + " " + EMPTY_MESSAGE);
+                }
+                separatorNeeded = true;
+            }
+            else
+            {
+                System.out.println("Bundle "
+                    + b.getBundleId()
+                    + " is apparently not resolved.");
+            }
+        }
+    }
+
+    private static boolean printMatchingRequirements(BundleWiring wiring, List<String> namespace)
+    {
+        List<BundleWire> wires = wiring.getRequiredWires(null);
+        Map<BundleRequirement, List<BundleWire>> aggregateReqs =
+            aggregateRequirements(namespace, wires);
+        List<BundleRequirement> allReqs = wiring.getRequirements(null);
+        boolean matches = false;
+        for (BundleRequirement req : allReqs)
+        {
+            if (matchNamespace(namespace, req.getNamespace()))
+            {
+                matches = true;
+                List<BundleWire> providers = aggregateReqs.get(req);
+                if (providers != null)
+                {
+                    System.out.println(
+                        req.getNamespace()
+                        + "; "
+                        + req.getDirectives().get(Constants.FILTER_DIRECTIVE)
+                        + " resolved by:");
+                    for (BundleWire wire : providers)
+                    {
+                        String msg;
+                        Object keyAttr =
+                            wire.getCapability().getAttributes()
+                                .get(wire.getCapability().getNamespace());
+                        if (keyAttr != null)
+                        {
+                            msg = wire.getCapability().getNamespace()
+                                + "; "
+                                + keyAttr
+                                + getVersionFromCapability(wire.getCapability());
                         }
                         else
                         {
-                            System.out.println("Nothing");
+                            msg = wire.getCapability().toString();
                         }
-                        separatorNeeded = true;
+                        msg = "   " + msg + " from "
+                            + wire.getProviderWiring().getBundle();
+                        System.out.println(msg);
                     }
                 }
-                catch (Exception ex)
+                else
                 {
-                    System.err.println(ex.toString());
+                    System.out.println(
+                        req.getNamespace()
+                        + "; "
+                        + req.getDirectives().get(Constants.FILTER_DIRECTIVE)
+                        + " "
+                        + UNRESOLVED_MESSAGE);
                 }
             }
-            Util.ungetServices(bc, refs);
+        }
+        return matches;
+    }
+
+    private static Map<BundleRequirement, List<BundleWire>> aggregateRequirements(
+        List<String> namespace, List<BundleWire> wires)
+    {
+        // Aggregate matching capabilities.
+        Map<BundleRequirement, List<BundleWire>> map =
+            new HashMap<BundleRequirement, List<BundleWire>>();
+        for (BundleWire wire : wires)
+        {
+            if (matchNamespace(namespace, wire.getRequirement().getNamespace()))
+            {
+                List<BundleWire> providers = map.get(wire.getRequirement());
+                if (providers == null)
+                {
+                    providers = new ArrayList<BundleWire>();
+                    map.put(wire.getRequirement(), providers);
+                }
+                providers.add(wire);
+            }
+        }
+        return map;
+    }
+
+    private static boolean printServiceRequirements(Bundle b)
+    {
+        boolean matches = false;
+
+        try
+        {
+            ServiceReference[] refs = b.getServicesInUse();
+
+            if ((refs != null) && (refs.length > 0))
+            {
+                matches = true;
+                // Print properties for each service.
+                for (ServiceReference ref : refs)
+                {
+                    // Print object class with "namespace".
+                    System.out.println(
+                        NONSTANDARD_SERVICE_NAMESPACE
+                        + "; "
+                        + Util.getValueString(ref.getProperty("objectClass"))
+                        + " provided by:");
+                    System.out.println("   " + ref.getBundle());
+                }
+            }
+        }
+        catch (Exception ex)
+        {
+            System.err.println(ex.toString());
+        }
+
+        return matches;
+    }
+
+    public static void inspect(
+        BundleContext bc, String direction, String namespace, Bundle[] bundles)
+    {
+        // Verify arguments.
+        if (isValidDirection(direction))
+        {
+            bundles = ((bundles == null) || (bundles.length == 0))
+                ? bc.getBundles() : bundles;
+
+            if (CAPABILITY.startsWith(direction))
+            {
+                printNonstandardCapabilities(bc, Util.parseSubstring(namespace), bundles);
+            }
+            else
+            {
+                printNonstandardRequirements(bc, Util.parseSubstring(namespace), bundles);
+            }
+        }
+        else
+        {
+            if (!isValidDirection(direction))
+            {
+                System.out.println("Invalid argument: " + direction);
+            }
         }
     }
 
-    public static void printImportedPackages(BundleContext bc, Bundle[] bundles)
+    private static void printNonstandardCapabilities(
+        BundleContext bc, List<String> namespace, Bundle[] bundles)
     {
         boolean separatorNeeded = false;
-
-        if ((bundles == null) || (bundles.length == 0))
+        for (Bundle b : bundles)
         {
-            bundles = bc.getBundles();
-        }
-
-        for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
-        {
-            try
+            if (separatorNeeded)
             {
-                if (bundles[bundleIdx] != null)
-                {
-                    if (separatorNeeded)
-                    {
-                        System.out.println("");
-                    }
-                    _printImportedPackages(bc, bundles[bundleIdx]);
-                    separatorNeeded = true;
-                }
+                System.out.println("");
             }
-            catch (Exception ex)
-            {
-                System.err.println(ex.toString());
-            }
-        }
-    }
-
-    private static void _printImportedPackages(BundleContext bc, Bundle bundle)
-    {
-        // Keep track of service references.
-        List<ServiceReference> refs = new ArrayList();
-
-        // Get package admin service.
-        PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
-        if (pa == null)
-        {
-            System.out.println("PackageAdmin service is unavailable.");
-        }
-        else
-        {
-            ExportedPackage[] exports = pa.getExportedPackages((Bundle) null);
-            String title = bundle + " imports packages:";
+            String title = b + " provides:";
             System.out.println(title);
             System.out.println(Util.getUnderlineString(title.length()));
-            boolean found = false;
-            for (int expIdx = 0; expIdx < exports.length; expIdx++)
+            boolean matches = false;
+
+            if (matchNamespace(namespace, LEGACY_BUNDLE_NAMESPACE))
             {
-                Bundle[] importers = exports[expIdx].getImportingBundles();
-                for (int impIdx = 0; (importers != null) && (impIdx < importers.length); impIdx++)
-                {
-                    if (importers[impIdx] == bundle)
-                    {
-                        System.out.println(exports[expIdx]
-                            + " -> " + exports[expIdx].getExportingBundle());
-                        found = true;
-                    }
-                }
+                matches |= printRequiringBundles(bc, b);
             }
-            if (!found)
+            if (matchNamespace(namespace, LEGACY_HOST_NAMESPACE))
             {
-                System.out.println("Nothing");
+                matches |= printHostedFragments(bc, b);
             }
-            Util.ungetServices(bc, refs);
+            if (matchNamespace(namespace, LEGACY_PACKAGE_NAMESPACE))
+            {
+                matches |= printExportedPackages(bc, b);
+            }
+            if (matchNamespace(namespace, NONSTANDARD_SERVICE_NAMESPACE))
+            {
+                matches |= printServiceCapabilities(b);
+            }
+
+            // If there were no capabilities for the specified namespace,
+            // then say so.
+            if (!matches)
+            {
+                System.out.println(Util.unparseSubstring(namespace) + " " + EMPTY_MESSAGE);
+            }
+            separatorNeeded = true;
         }
     }
 
-    public static void printRequiringBundles(BundleContext bc, Bundle[] bundles)
+    private static void printNonstandardRequirements(
+        BundleContext bc, List<String> namespace, Bundle[] bundles)
     {
+        boolean separatorNeeded = false;
+        for (Bundle b : bundles)
+        {
+            if (separatorNeeded)
+            {
+                System.out.println("");
+            }
+            String title = b + " requires:";
+            System.out.println(title);
+            System.out.println(Util.getUnderlineString(title.length()));
+            boolean matches = false;
+            if (matchNamespace(namespace, LEGACY_BUNDLE_NAMESPACE))
+            {
+                matches |= printRequiredBundles(bc, b);
+            }
+            if (matchNamespace(namespace, LEGACY_HOST_NAMESPACE))
+            {
+                matches |= printFragmentHosts(bc, b);
+            }
+            if (matchNamespace(namespace, LEGACY_PACKAGE_NAMESPACE))
+            {
+                matches |= printImportedPackages(bc, b);
+            }
+            if (matchNamespace(namespace, NONSTANDARD_SERVICE_NAMESPACE))
+            {
+                matches |= printServiceRequirements(b);
+            }
+
+            // If there were no capabilities for the specified namespace,
+            // then say so.
+            if (!matches)
+            {
+                System.out.println(Util.unparseSubstring(namespace) + " " + EMPTY_MESSAGE);
+            }
+            separatorNeeded = true;
+        }
+    }
+
+    public static boolean printExportedPackages(BundleContext bc, Bundle b)
+    {
+        boolean matches = false;
+
         // Keep track of service references.
         List<ServiceReference> refs = new ArrayList();
 
-        // Get package admin service.
-        PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
-        if (pa == null)
+        // Fragments cannot export packages.
+        if (!isFragment(b))
         {
-            System.out.println("PackageAdmin service is unavailable.");
-        }
-        else
-        {
-            boolean separatorNeeded = false;
-
-            if ((bundles == null) || (bundles.length == 0))
+            // Get package admin service.
+            PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
+            if (pa == null)
             {
-                bundles = bc.getBundles();
+                System.out.println("PackageAdmin service is unavailable.");
             }
-
-            for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
+            else
             {
                 try
                 {
-                    if (bundles[bundleIdx] != null)
+                    ExportedPackage[] exports = pa.getExportedPackages(b);
+                    if (exports != null)
                     {
-                        RequiredBundle[] rbs = pa.getRequiredBundles(
-                            bundles[bundleIdx].getSymbolicName());
-                        for (int rbIdx = 0; (rbs != null) && (rbIdx < rbs.length); rbIdx++)
+                        for (ExportedPackage ep : exports)
                         {
-                            if (rbs[rbIdx].getBundle() == bundles[bundleIdx])
+                            matches = true;
+                            Bundle[] importers = ep.getImportingBundles();
+                            if ((importers != null) && (importers.length > 0))
                             {
-                                if (separatorNeeded)
+                                String msg = LEGACY_PACKAGE_NAMESPACE
+                                    + "; "
+                                    + ep.getName()
+                                    + "; "
+                                    + ep.getVersion().toString()
+                                    + " required by:";
+                                System.out.println(msg);
+                                for (Bundle importer : importers)
                                 {
-                                    System.out.println("");
+                                    System.out.println("   " + importer);
                                 }
-                                String title = bundles[bundleIdx] + " is required by:";
-                                System.out.println(title);
-                                System.out.println(Util.getUnderlineString(title.length()));
-                                if ((rbs[rbIdx].getRequiringBundles() != null)
-                                    && (rbs[rbIdx].getRequiringBundles().length > 0))
-                                {
-                                    for (int reqIdx = 0;
-                                        reqIdx < rbs[rbIdx].getRequiringBundles().length;
-                                        reqIdx++)
-                                    {
-                                        System.out.println(rbs[rbIdx].getRequiringBundles()[reqIdx]);
-                                    }
-                                }
-                                else
-                                {
-                                    System.out.println("Nothing");
-                                }
-                                separatorNeeded = true;
+                            }
+                            else
+                            {
+                                System.out.println(
+                                    LEGACY_PACKAGE_NAMESPACE
+                                    + "; "
+                                    + ep.getName()
+                                    + "; "
+                                    + ep.getVersion().toString()
+                                    + " "
+                                    + UNUSED_MESSAGE);
                             }
                         }
                     }
@@ -306,139 +572,196 @@
                     System.err.println(ex.toString());
                 }
             }
-            Util.ungetServices(bc, refs);
         }
+
+        Util.ungetServices(bc, refs);
+
+        return matches;
     }
 
-    public static void printRequiredBundles(BundleContext bc, Bundle[] bundles)
+    private static boolean printImportedPackages(BundleContext bc, Bundle b)
     {
-        boolean separatorNeeded = false;
+        boolean matches = false;
 
-        if ((bundles == null) || (bundles.length == 0))
-        {
-            bundles = bc.getBundles();
-        }
-
-        for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
-        {
-            try
-            {
-                if (bundles[bundleIdx] != null)
-                {
-                    if (separatorNeeded)
-                    {
-                        System.out.println("");
-                    }
-                    _printRequiredBundles(bc, bundles[bundleIdx]);
-                    separatorNeeded = true;
-                }
-            }
-            catch (Exception ex)
-            {
-                System.err.println(ex.toString());
-            }
-        }
-    }
-
-    private static void _printRequiredBundles(BundleContext bc, Bundle bundle)
-    {
         // Keep track of service references.
         List<ServiceReference> refs = new ArrayList();
 
-        // Get package admin service.
-        PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
-        if (pa == null)
+        // Fragments cannot import packages.
+        if (!isFragment(b))
         {
-            System.out.println("PackageAdmin service is unavailable.");
-        }
-        else
-        {
-            RequiredBundle[] rbs = pa.getRequiredBundles(null);
-            String title = bundle + " requires bundles:";
-            System.out.println(title);
-            System.out.println(Util.getUnderlineString(title.length()));
-            boolean found = false;
-            for (int rbIdx = 0; rbIdx < rbs.length; rbIdx++)
+            // Get package admin service.
+            PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
+            if (pa == null)
             {
-                Bundle[] requirers = rbs[rbIdx].getRequiringBundles();
-                for (int reqIdx = 0; (requirers != null) && (reqIdx < requirers.length); reqIdx++)
+                System.out.println("PackageAdmin service is unavailable.");
+            }
+            else
+            {
+                ExportedPackage[] exports = pa.getExportedPackages((Bundle) null);
+                if (exports != null)
                 {
-                    if (requirers[reqIdx] == bundle)
+                    for (ExportedPackage ep : exports)
                     {
-                        System.out.println(rbs[reqIdx]);
-                        found = true;
+                        Bundle[] importers = ep.getImportingBundles();
+                        if (importers != null)
+                        {
+                            for (Bundle importer : importers)
+                            {
+                                if (importer == b)
+                                {
+                                    matches = true;
+                                    System.out.println(
+                                        LEGACY_PACKAGE_NAMESPACE
+                                        + "; "
+                                        + ep.getName()
+                                        + " resolved by:");
+                                    System.out.println(
+                                        "   "
+                                        + ep.getName()
+                                        + "; "
+                                        + ep.getVersion().toString()
+                                        + " from "
+                                        + ep.getExportingBundle());
+                                }
+                            }
+                        }
                     }
                 }
             }
-            if (!found)
-            {
-                System.out.println("Nothing");
-            }
-            Util.ungetServices(bc, refs);
         }
+
+        Util.ungetServices(bc, refs);
+
+        return matches;
     }
 
-    public static void printFragmentHosts(BundleContext bc, Bundle[] bundles)
+    public static boolean printRequiringBundles(BundleContext bc, Bundle b)
     {
+        boolean matches = false;
+
         // Keep track of service references.
         List<ServiceReference> refs = new ArrayList();
 
-        // Get package admin service.
-        PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
-        if (pa == null)
+        // Fragments cannot be required.
+        if (!isFragment(b))
         {
-            System.out.println("PackageAdmin service is unavailable.");
-        }
-        else
-        {
-            if ((bundles == null) || (bundles.length == 0))
+            // Get package admin service.
+            PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
+            if (pa == null)
             {
-                bundles = bc.getBundles();
+                System.out.println("PackageAdmin service is unavailable.");
             }
-
-            for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
+            else
             {
-                // Print a separator for some whitespace.
-                if (bundleIdx > 0)
-                {
-                    System.out.println("");
-                }
-
                 try
                 {
-                    if ((bundles[bundleIdx] != null) && isFragment(bundles[bundleIdx]))
+                    RequiredBundle[] rbs = pa.getRequiredBundles(b.getSymbolicName());
+                    if (rbs != null)
                     {
-                        String title = bundles[bundleIdx] + " is attached to:";
-                        System.out.println(title);
-                        System.out.println(Util.getUnderlineString(title.length()));
-                        Bundle[] hosts = pa.getHosts(bundles[bundleIdx]);
-                        for (int hostIdx = 0;
-                            (hosts != null) && (hostIdx < hosts.length);
-                            hostIdx++)
+                        for (RequiredBundle rb : rbs)
                         {
-                            System.out.println(hosts[hostIdx]);
-                        }
-                        if ((hosts == null) || (hosts.length == 0))
-                        {
-                            System.out.println("Nothing");
+                            if (rb.getBundle() == b)
+                            {
+                                Bundle[] requires = rb.getRequiringBundles();
+                                if ((requires != null) && (requires.length > 0))
+                                {
+                                    matches = true;
+                                    System.out.println(
+                                        LEGACY_BUNDLE_NAMESPACE
+                                        + "; "
+                                        + b.getSymbolicName()
+                                        + "; "
+                                        + b.getVersion().toString()
+                                        + " required by:");
+                                    for (Bundle requirer : requires)
+                                    {
+                                        System.out.println("   " + requirer);
+                                    }
+                                }
+                            }
                         }
                     }
-                    else if ((bundles[bundleIdx] != null) && !isFragment(bundles[bundleIdx]))
+
+                    if (!matches)
                     {
-                        System.out.println("Bundle " + bundles[bundleIdx] + " is not a fragment.");
+                        matches = true;
+                        System.out.println(
+                            LEGACY_BUNDLE_NAMESPACE
+                            + "; "
+                            + b.getSymbolicName()
+                            + "; "
+                            + b.getVersion().toString()
+                            + " "
+                            + UNUSED_MESSAGE);
                     }
+
                 }
                 catch (Exception ex)
                 {
                     System.err.println(ex.toString());
                 }
             }
-            Util.ungetServices(bc, refs);
         }
+
+        Util.ungetServices(bc, refs);
+
+        return matches;
     }
 
-    public static void printHostedFragments(BundleContext bc, Bundle[] bundles)
+    private static boolean printRequiredBundles(BundleContext bc, Bundle b)
     {
+        boolean matches = false;
+
+        // Keep track of service references.
+        List<ServiceReference> refs = new ArrayList();
+
+        // Fragments cannot require bundles.
+        if (!isFragment(b))
+        {
+            // Get package admin service.
+            PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
+            if (pa == null)
+            {
+                System.out.println("PackageAdmin service is unavailable.");
+            }
+            else
+            {
+                RequiredBundle[] rbs = pa.getRequiredBundles(null);
+                if (rbs != null)
+                {
+                    for (RequiredBundle rb : rbs)
+                    {
+                        Bundle[] requirers = rb.getRequiringBundles();
+                        if (requirers != null)
+                        {
+                            for (Bundle requirer : requirers)
+                            {
+                                if (requirer == b)
+                                {
+                                    matches = true;
+                                    System.out.println(
+                                        LEGACY_BUNDLE_NAMESPACE
+                                        + "; "
+                                        + rb.getSymbolicName()
+                                        + " resolved by:");
+                                    System.out.println("   " + rb.getBundle());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        Util.ungetServices(bc, refs);
+
+        return matches;
+    }
+
+    public static boolean printHostedFragments(BundleContext bc, Bundle b)
+    {
+        boolean matches = false;
+
         // Keep track of service references.
         List<ServiceReference> refs = new ArrayList();
 
@@ -450,104 +773,92 @@
         }
         else
         {
-            if ((bundles == null) || (bundles.length == 0))
+            try
             {
-                bundles = bc.getBundles();
-            }
-
-            for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
-            {
-                // Print a separator for some whitespace.
-                if (bundleIdx > 0)
+                if (!isFragment(b))
                 {
-                    System.out.println("");
-                }
-
-                try
-                {
-                    if ((bundles[bundleIdx] != null) && !isFragment(bundles[bundleIdx]))
+                    matches = true;
+                    Bundle[] fragments = pa.getFragments(b);
+                    if ((fragments != null) && (fragments.length > 0))
                     {
-                        String title = bundles[bundleIdx] + " hosts:";
-                        System.out.println(title);
-                        System.out.println(Util.getUnderlineString(title.length()));
-                        Bundle[] fragments = pa.getFragments(bundles[bundleIdx]);
-                        for (int fragIdx = 0;
-                            (fragments != null) && (fragIdx < fragments.length);
-                            fragIdx++)
+                        System.out.println(
+                            LEGACY_HOST_NAMESPACE
+                            + "; "
+                            + b.getSymbolicName()
+                            + "; "
+                            + b.getVersion().toString()
+                            + " required by:");
+                        for (Bundle fragment : fragments)
                         {
-                            System.out.println(fragments[fragIdx]);
-                        }
-                        if ((fragments == null) || (fragments.length == 0))
-                        {
-                            System.out.println("Nothing");
+                            System.out.println("   " + fragment);
                         }
                     }
-                    else if ((bundles[bundleIdx] != null) && isFragment(bundles[bundleIdx]))
+                    else
                     {
-                        System.out.println("Bundle " + bundles[bundleIdx] + " is a fragment.");
+                        System.out.println(
+                            LEGACY_HOST_NAMESPACE
+                            + "; "
+                            + b.getSymbolicName()
+                            + "; "
+                            + b.getVersion().toString()
+                            + " "
+                            + UNUSED_MESSAGE);
                     }
                 }
-                catch (Exception ex)
-                {
-                    System.err.println(ex.toString());
-                }
             }
+            catch (Exception ex)
+            {
+                System.err.println(ex.toString());
+            }
+
             Util.ungetServices(bc, refs);
         }
+
+        return matches;
     }
 
-    public static void printExportedServices(BundleContext bc, Bundle[] bundles)
+    public static boolean printFragmentHosts(BundleContext bc, Bundle b)
     {
-        if ((bundles == null) || (bundles.length == 0))
+        boolean matches = false;
+
+        // Keep track of service references.
+        List<ServiceReference> refs = new ArrayList();
+
+        // Get package admin service.
+        PackageAdmin pa = Util.getService(bc, PackageAdmin.class, refs);
+        if (pa == null)
         {
-            bundles = bc.getBundles();
+            System.out.println("PackageAdmin service is unavailable.");
         }
-
-        for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
+        else
         {
-            // Print a separator for some whitespace.
-            if (bundleIdx > 0)
-            {
-                System.out.println("");
-            }
-
             try
             {
-                if (bundles[bundleIdx] != null)
+                if (isFragment(b))
                 {
-                    ServiceReference[] refs = bundles[bundleIdx].getRegisteredServices();
+                    matches = true;
 
-                    // Print header if we have not already done so.
-                    String title = Util.getBundleName(bundles[bundleIdx]) + " provides services:";
-                    System.out.println(title);
-                    System.out.println(Util.getUnderlineString(title.length()));
-
-                    if ((refs == null) || (refs.length == 0))
+                    Bundle[] hosts = pa.getHosts(b);
+                    if ((hosts != null) && (hosts.length > 0))
                     {
-                        System.out.println("Nothing");
+                        System.out.println(
+                            LEGACY_HOST_NAMESPACE
+                            + "; "
+                            + b.getHeaders().get(Constants.FRAGMENT_HOST)
+                            + " resolved by:");
+                        for (Bundle host : hosts)
+                        {
+                            System.out.println("   " + host);
+                        }
                     }
-
-                    // Print properties for each service.
-                    for (int refIdx = 0;
-                        (refs != null) && (refIdx < refs.length);
-                        refIdx++)
+                    else
                     {
-                        // Print service properties.
-                        String[] keys = refs[refIdx].getPropertyKeys();
-                        for (int keyIdx = 0;
-                            (keys != null) && (keyIdx < keys.length);
-                            keyIdx++)
-                        {
-                            Object v = refs[refIdx].getProperty(keys[keyIdx]);
-                            System.out.println(
-                                keys[keyIdx] + " = " + Util.getValueString(v));
-                        }
-
-                        // Print service separator if necessary.
-                        if ((refIdx + 1) < refs.length)
-                        {
-                            System.out.println("----");
-                        }
+                        System.out.println(
+                            LEGACY_HOST_NAMESPACE
+                            + "; "
+                            + b.getHeaders().get(Constants.FRAGMENT_HOST)
+                            + " "
+                            + UNRESOLVED_MESSAGE);
                     }
                 }
             }
@@ -555,77 +866,26 @@
             {
                 System.err.println(ex.toString());
             }
+
+            Util.ungetServices(bc, refs);
         }
+
+        return matches;
     }
 
-    public static void printImportedServices(BundleContext bc, Bundle[] bundles)
+    private static String getVersionFromCapability(BundleCapability c)
     {
-        if ((bundles == null) || (bundles.length == 0))
+        Object o = c.getAttributes().get(Constants.VERSION_ATTRIBUTE);
+        if (o == null)
         {
-            bundles = bc.getBundles();
+            o = c.getAttributes().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
         }
-
-        for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
-        {
-            // Print a separator for some whitespace.
-            if (bundleIdx > 0)
-            {
-                System.out.println("");
-            }
-
-            try
-            {
-                if (bundles[bundleIdx] != null)
-                {
-                    ServiceReference[] refs = bundles[bundleIdx].getServicesInUse();
-
-                    // Print header if we have not already done so.
-                    String title = Util.getBundleName(bundles[bundleIdx]) + " requires services:";
-                    System.out.println(title);
-                    System.out.println(Util.getUnderlineString(title.length()));
-
-                    if ((refs == null) || (refs.length == 0))
-                    {
-                        System.out.println("Nothing");
-                    }
-
-                    // Print properties for each service.
-                    for (int refIdx = 0;
-                        (refs != null) && (refIdx < refs.length);
-                        refIdx++)
-                    {
-                        // Print the registering bundle.
-                        System.out.println("Registering bundle = " + refs[refIdx].getBundle());
-                        // Print service properties.
-                        String[] keys = refs[refIdx].getPropertyKeys();
-                        for (int keyIdx = 0;
-                            (keys != null) && (keyIdx < keys.length);
-                            keyIdx++)
-                        {
-                            Object v = refs[refIdx].getProperty(keys[keyIdx]);
-                            System.out.println(
-                                keys[keyIdx] + " = " + Util.getValueString(v));
-                        }
-
-                        // Print service separator if necessary.
-                        if ((refIdx + 1) < refs.length)
-                        {
-                            System.out.println("----");
-                        }
-                    }
-                }
-            }
-            catch (Exception ex)
-            {
-                System.err.println(ex.toString());
-            }
-        }
+        return (o == null) ? "" : o.toString();
     }
 
-    private static boolean isValidType(String type)
+    private static boolean matchNamespace(List<String> namespace, String actual)
     {
-        return (PACKAGE_TYPE.startsWith(type) || BUNDLE_TYPE.startsWith(type)
-            || FRAGMENT_TYPE.startsWith(type) || SERVICE_TYPE.startsWith(type));
+        return Util.compareSubstring(namespace, actual);
     }
 
     private static boolean isValidDirection(String direction)
diff --git a/gogo/command/src/main/java/org/apache/felix/gogo/command/Util.java b/gogo/command/src/main/java/org/apache/felix/gogo/command/Util.java
index 931965b..48ac8c5 100644
--- a/gogo/command/src/main/java/org/apache/felix/gogo/command/Util.java
+++ b/gogo/command/src/main/java/org/apache/felix/gogo/command/Util.java
@@ -28,6 +28,7 @@
 import java.io.PrintStream;
 import java.net.URL;
 import java.net.URLConnection;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
@@ -316,4 +317,178 @@
         setProxyAuth(conn);
         return conn.getInputStream();
     }
+
+    public static List<String> parseSubstring(String value)
+    {
+        List<String> pieces = new ArrayList();
+        StringBuffer ss = new StringBuffer();
+        // int kind = SIMPLE; // assume until proven otherwise
+        boolean wasStar = false; // indicates last piece was a star
+        boolean leftstar = false; // track if the initial piece is a star
+        boolean rightstar = false; // track if the final piece is a star
+
+        int idx = 0;
+
+        // We assume (sub)strings can contain leading and trailing blanks
+        boolean escaped = false;
+loop:   for (;;)
+        {
+            if (idx >= value.length())
+            {
+                if (wasStar)
+                {
+                    // insert last piece as "" to handle trailing star
+                    rightstar = true;
+                }
+                else
+                {
+                    pieces.add(ss.toString());
+                    // accumulate the last piece
+                    // note that in the case of
+                    // (cn=); this might be
+                    // the string "" (!=null)
+                }
+                ss.setLength(0);
+                break loop;
+            }
+
+            // Read the next character and account for escapes.
+            char c = value.charAt(idx++);
+            if (!escaped && ((c == '(') || (c == ')')))
+            {
+                throw new IllegalArgumentException(
+                    "Illegal value: " + value);
+            }
+            else if (!escaped && (c == '*'))
+            {
+                if (wasStar)
+                {
+                    // encountered two successive stars;
+                    // I assume this is illegal
+                    throw new IllegalArgumentException("Invalid filter string: " + value);
+                }
+                if (ss.length() > 0)
+                {
+                    pieces.add(ss.toString()); // accumulate the pieces
+                    // between '*' occurrences
+                }
+                ss.setLength(0);
+                // if this is a leading star, then track it
+                if (pieces.isEmpty())
+                {
+                    leftstar = true;
+                }
+                wasStar = true;
+            }
+            else if (!escaped && (c == '\\'))
+            {
+                escaped = true;
+            }
+            else
+            {
+                escaped = false;
+                wasStar = false;
+                ss.append(c);
+            }
+        }
+        if (leftstar || rightstar || pieces.size() > 1)
+        {
+            // insert leading and/or trailing "" to anchor ends
+            if (rightstar)
+            {
+                pieces.add("");
+            }
+            if (leftstar)
+            {
+                pieces.add(0, "");
+            }
+        }
+        return pieces;
+    }
+
+    public static String unparseSubstring(List<String> pieces)
+    {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < pieces.size(); i++)
+        {
+            if (i > 0)
+            {
+                sb.append("*");
+            }
+            sb.append(pieces.get(i));
+        }
+        return sb.toString();
+    }
+
+    public static boolean compareSubstring(List<String> pieces, String s)
+    {
+        // Walk the pieces to match the string
+        // There are implicit stars between each piece,
+        // and the first and last pieces might be "" to anchor the match.
+        // assert (pieces.length > 1)
+        // minimal case is <string>*<string>
+
+        boolean result = true;
+        int len = pieces.size();
+
+        // Special case, if there is only one piece, then
+        // we must perform an equality test.
+        if (len == 1)
+        {
+            return s.equals(pieces.get(0));
+        }
+
+        // Otherwise, check whether the pieces match
+        // the specified string.
+
+        int index = 0;
+
+loop:   for (int i = 0; i < len; i++)
+        {
+            String piece = pieces.get(i);
+
+            // If this is the first piece, then make sure the
+            // string starts with it.
+            if (i == 0)
+            {
+                if (!s.startsWith(piece))
+                {
+                    result = false;
+                    break loop;
+                }
+            }
+
+            // If this is the last piece, then make sure the
+            // string ends with it.
+            if (i == len - 1)
+            {
+                if (s.endsWith(piece))
+                {
+                    result = true;
+                }
+                else
+                {
+                    result = false;
+                }
+                break loop;
+            }
+
+            // If this is neither the first or last piece, then
+            // make sure the string contains it.
+            if ((i > 0) && (i < (len - 1)))
+            {
+                index = s.indexOf(piece, index);
+                if (index < 0)
+                {
+                    result = false;
+                    break loop;
+                }
+            }
+
+            // Move string index beyond the matching piece.
+            index += piece.length();
+        }
+
+        return result;
+    }
 }
\ No newline at end of file