Combined all dependency introspection commands into one "inspect" command.
(FELIX-1151)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@774448 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/Activator.java b/shell/src/main/java/org/apache/felix/shell/impl/Activator.java
index 92ff81a..286d995 100644
--- a/shell/src/main/java/org/apache/felix/shell/impl/Activator.java
+++ b/shell/src/main/java/org/apache/felix/shell/impl/Activator.java
@@ -92,11 +92,6 @@
         context.registerService(
             classes, new CdCommandImpl(m_context), null);
 
-        // Register "exports" command service.
-        context.registerService(
-            org.apache.felix.shell.Command.class.getName(),
-            new ExportsCommandImpl(m_context), null);
-
         // Register "find" command service.
         context.registerService(
             org.apache.felix.shell.Command.class.getName(),
@@ -112,10 +107,10 @@
             org.apache.felix.shell.Command.class.getName(),
             new HelpCommandImpl(m_context), null);
 
-        // Register "imports" command service.
+        // Register "inspect" command service.
         context.registerService(
             org.apache.felix.shell.Command.class.getName(),
-            new ImportsCommandImpl(m_context), null);
+            new InspectCommandImpl(m_context), null);
 
         // Register "install" command service.
         context.registerService(
@@ -137,26 +132,11 @@
             org.apache.felix.shell.Command.class.getName(),
             new RefreshCommandImpl(m_context), null);
 
-        // Register "requires" command service.
-        context.registerService(
-            org.apache.felix.shell.Command.class.getName(),
-            new RequiresCommandImpl(m_context), null);
-
-        // Register "requirers" command service.
-        context.registerService(
-            org.apache.felix.shell.Command.class.getName(),
-            new RequirersCommandImpl(m_context), null);
-
         // Register "resolve" command service.
         context.registerService(
             org.apache.felix.shell.Command.class.getName(),
             new ResolveCommandImpl(m_context), null);
 
-        // Register "services" command service.
-        context.registerService(
-            org.apache.felix.shell.Command.class.getName(),
-            new ServicesCommandImpl(m_context), null);
-
         // Register "startlevel" command service.
         context.registerService(
             org.apache.felix.shell.Command.class.getName(),
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/ExportsCommandImpl.java b/shell/src/main/java/org/apache/felix/shell/impl/ExportsCommandImpl.java
deleted file mode 100644
index 9637c6e..0000000
--- a/shell/src/main/java/org/apache/felix/shell/impl/ExportsCommandImpl.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/* 
- * 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.shell.impl;
-
-import java.io.PrintStream;
-import java.util.StringTokenizer;
-
-import org.apache.felix.shell.Command;
-import org.osgi.framework.*;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-
-public class ExportsCommandImpl implements Command
-{
-    private BundleContext m_context = null;
-
-    public ExportsCommandImpl(BundleContext context)
-    {
-        m_context = context;
-    }
-
-    public String getName()
-    {
-        return "exports";
-    }
-
-    public String getUsage()
-    {
-        return "exports <id> ...";
-    }
-
-    public String getShortDescription()
-    {
-        return "list exported packages.";
-    }
-
-    public void execute(String s, PrintStream out, PrintStream err)
-    {
-        // Get package admin service.
-        ServiceReference ref = m_context.getServiceReference(
-            org.osgi.service.packageadmin.PackageAdmin.class.getName());
-        PackageAdmin pa = (ref == null) ? null : (PackageAdmin) m_context.getService(ref);
-        if (pa == null)
-        {
-            out.println("PackageAdmin service is unavailable.");
-            return;
-        }
-
-        // Parse command line.
-        StringTokenizer st = new StringTokenizer(s, " ");
-
-        // Ignore the command name.
-        st.nextToken();
-
-        if (st.hasMoreTokens())
-        {
-            boolean separatorNeeded = false;
-            while (st.hasMoreTokens())
-            {
-                String id = st.nextToken();
-                try
-                {
-                    long l = Long.parseLong(id);
-                    Bundle bundle = m_context.getBundle(l);
-                    if (bundle != null)
-                    {
-                        ExportedPackage[] exports = pa.getExportedPackages(bundle);
-                        if (separatorNeeded)
-                        {
-                            out.println("");
-                        }
-                        printExports(out, bundle, exports);
-                        separatorNeeded = true;
-                    }
-                    else
-                    {
-                        err.println("Bundle ID " + id + " is invalid.");
-                    }
-                }
-                catch (NumberFormatException ex)
-                {
-                    err.println("Unable to parse id '" + id + "'.");
-                }
-                catch (Exception ex)
-                {
-                    err.println(ex.toString());
-                }
-            }
-        }
-    }
-
-    private void printExports(PrintStream out, Bundle target, ExportedPackage[] exports)
-    {
-        String title = target + " exports:";
-        out.println(title);
-        out.println(Util.getUnderlineString(title));
-        if ((exports != null) && (exports.length > 0))
-        {
-            for (int i = 0; i < exports.length; i++)
-            {
-                out.println(exports[i]);
-            }
-        }
-        else
-        {
-            out.println("Nothing");
-        }
-    }
-}
\ No newline at end of file
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/ImportsCommandImpl.java b/shell/src/main/java/org/apache/felix/shell/impl/ImportsCommandImpl.java
deleted file mode 100644
index 47e4b38..0000000
--- a/shell/src/main/java/org/apache/felix/shell/impl/ImportsCommandImpl.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/* 
- * 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.shell.impl;
-
-import java.io.PrintStream;
-import java.util.StringTokenizer;
-
-import org.apache.felix.shell.Command;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-
-public class ImportsCommandImpl implements Command
-{
-    private final BundleContext m_context;
-    private ServiceReference m_ref = null;
-
-    public ImportsCommandImpl(BundleContext context)
-    {
-        m_context = context;
-    }
-
-    public String getName()
-    {
-        return "imports";
-    }
-
-    public String getUsage()
-    {
-        return "imports <id> ...";
-    }
-
-    public String getShortDescription()
-    {
-        return "list imported packages.";
-    }
-
-    public void execute(String s, PrintStream out, PrintStream err)
-    {
-        StringTokenizer st = new StringTokenizer(s, " ");
-
-        // Ignore the command name.
-        st.nextToken();
-
-        if (st.hasMoreTokens())
-        {
-            boolean separatorNeeded = false;
-            while (st.hasMoreTokens())
-            {
-                String id = st.nextToken().trim();
-
-                try
-                {
-                    long l = Long.parseLong(id);
-                    Bundle bundle = m_context.getBundle(l);
-                    if (bundle != null)
-                    {
-                        if (separatorNeeded)
-                        {
-                            out.println("");
-                        }
-                        getImportedPackages(bundle, out, err);
-                        separatorNeeded = true;
-                    }
-                    else
-                    {
-                        err.println("Bundle ID " + id + " is invalid.");
-                    }
-                }
-                catch (NumberFormatException ex)
-                {
-                    err.println("Unable to parse id '" + id + "'.");
-                }
-                catch (Exception ex)
-                {
-                    err.println(ex.toString());
-                }
-            }
-        }
-    }
-
-    private void getImportedPackages(Bundle bundle, PrintStream out, PrintStream err)
-    {
-        // Get package admin service.
-        PackageAdmin pa = getPackageAdmin();
-        if (pa == null)
-        {
-            out.println("PackageAdmin service is unavailable.");
-        }
-        else
-        {
-            ExportedPackage[] exports = pa.getExportedPackages((Bundle) null);
-            String title = bundle + " imports:";
-            out.println(title);
-            out.println(Util.getUnderlineString(title));
-            boolean found = false;
-            for (int expIdx = 0; expIdx < exports.length; expIdx++)
-            {
-                Bundle[] importers = exports[expIdx].getImportingBundles();
-                for (int impIdx = 0; (importers != null) && (impIdx < importers.length); impIdx++)
-                {
-                    if (importers[impIdx] == bundle)
-                    {
-                        out.println(exports[expIdx]
-                            + " -> " + exports[expIdx].getExportingBundle());
-                        found = true;
-                    }
-                }
-            }
-            if (!found)
-            {
-                out.println("Nothing");
-            }
-            ungetPackageAdmin();
-        }
-    }
-
-    private PackageAdmin getPackageAdmin()
-    {
-        PackageAdmin pa = null;
-        m_ref = m_context.getServiceReference(
-            org.osgi.service.packageadmin.PackageAdmin.class.getName());
-        if (m_ref != null)
-        {
-            pa = (PackageAdmin) m_context.getService(m_ref);
-        }
-        return pa;
-    }
-
-    private void ungetPackageAdmin()
-    {
-        m_context.ungetService(m_ref);
-    }
-}
\ No newline at end of file
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/InspectCommandImpl.java b/shell/src/main/java/org/apache/felix/shell/impl/InspectCommandImpl.java
new file mode 100644
index 0000000..43d1c0b
--- /dev/null
+++ b/shell/src/main/java/org/apache/felix/shell/impl/InspectCommandImpl.java
@@ -0,0 +1,553 @@
+/* 
+ * 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.shell.impl;
+
+import java.io.PrintStream;
+import java.util.StringTokenizer;
+
+import org.apache.felix.shell.Command;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.packageadmin.RequiredBundle;
+
+public class InspectCommandImpl implements Command
+{
+    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 PROVIDE_DIRECTION = "provide";
+    public static final String REQUIRE_DIRECTION = "require";
+
+    private final BundleContext m_context;
+    private ServiceReference m_ref = null;
+
+    public InspectCommandImpl(BundleContext context)
+    {
+        m_context = context;
+    }
+
+    public String getName()
+    {
+        return "inspect";
+    }
+
+    public String getUsage()
+    {
+        return "inspect (package|bundle|fragment|service) (require|provide) <id> ...";
+    }
+
+    public String getShortDescription()
+    {
+        return "inspects dependency information.";
+    }
+
+    public void execute(String s, PrintStream out, PrintStream err)
+    {
+        StringTokenizer st = new StringTokenizer(s, " ");
+
+        // Ignore the command name.
+        st.nextToken();
+
+        if (st.countTokens() < 3)
+        {
+            out.println("Too few arguments.");
+            out.println(getUsage());
+        }
+        else
+        {
+            // Get dependency type.
+            String type = st.nextToken();
+            // Get dependency direction.
+            String direction = st.nextToken();
+            // Get target bundle identifiers.
+            String[] ids = new String[st.countTokens()];
+            for (int i = 0; st.hasMoreTokens(); i++)
+            {
+                ids[i] = st.nextToken().trim();
+            }
+            // Verify arguments.
+            if (validTypeAndDirection(type, direction))
+            {
+                // Now determine what needs to be printed.
+                if (PACKAGE_TYPE.startsWith(type))
+                {
+                    if (PROVIDE_DIRECTION.startsWith(direction))
+                    {
+                        printExportedPackages(ids, out, err);
+                    }
+                    else
+                    {
+                        printImportedPackages(ids, out, err);
+                    }
+                }
+                else if (BUNDLE_TYPE.startsWith(type))
+                {
+                    if (PROVIDE_DIRECTION.startsWith(direction))
+                    {
+                        printRequiringBundles(ids, out, err);
+                    }
+                    else
+                    {
+                        printRequiredBundles(ids, out, err);
+                    }
+                }
+                else if (FRAGMENT_TYPE.startsWith(type))
+                {
+                    if (PROVIDE_DIRECTION.startsWith(direction))
+                    {
+                        out.println("Not yet implemented.");
+                    }
+                    else
+                    {
+                        out.println("Not yet implemented.");
+                    }
+                }
+                else
+                {
+                    if (PROVIDE_DIRECTION.startsWith(direction))
+                    {
+                        printExportedServices(ids, out, err);
+                    }
+                    else
+                    {
+                        printImportedServices(ids, out, err);
+                    }
+                }
+            }
+            else
+            {
+                out.println("Invalid arguments.");
+            }
+        }
+    }
+
+    private void printExportedPackages(String[] ids, PrintStream out, PrintStream err)
+    {
+        PackageAdmin pa = getPackageAdmin();
+        if (pa == null)
+        {
+            out.println("PackageAdmin service is unavailable.");
+        }
+        else
+        {
+            boolean separatorNeeded = false;
+            for (int idIdx = 0; idIdx < ids.length; idIdx++)
+            {
+                try
+                {
+                    long l = Long.parseLong(ids[idIdx]);
+                    Bundle bundle = m_context.getBundle(l);
+                    if (bundle != null)
+                    {
+                        ExportedPackage[] exports = pa.getExportedPackages(bundle);
+                        if (separatorNeeded)
+                        {
+                            out.println("");
+                        }
+                        String title = bundle + " provides packages:";
+                        out.println(title);
+                        out.println(Util.getUnderlineString(title));
+                        if ((exports != null) && (exports.length > 0))
+                        {
+                            for (int expIdx = 0; expIdx < exports.length; expIdx++)
+                            {
+                                out.println(exports[expIdx]);
+                            }
+                        }
+                        else
+                        {
+                            out.println("Nothing");
+                        }
+                        separatorNeeded = true;
+                    }
+                    else
+                    {
+                        err.println("Bundle ID " + ids[idIdx] + " is invalid.");
+                    }
+                }
+                catch (NumberFormatException ex)
+                {
+                    err.println("Unable to parse id '" + ids[idIdx] + "'.");
+                }
+                catch (Exception ex)
+                {
+                    err.println(ex.toString());
+                }
+            }
+        }
+        ungetPackageAdmin();
+    }
+
+    private void printImportedPackages(String[] ids, PrintStream out, PrintStream err)
+    {
+        boolean separatorNeeded = false;
+        for (int idIdx = 0; idIdx < ids.length; idIdx++)
+        {
+            try
+            {
+                long l = Long.parseLong(ids[idIdx]);
+                Bundle bundle = m_context.getBundle(l);
+                if (bundle != null)
+                {
+                    if (separatorNeeded)
+                    {
+                        out.println("");
+                    }
+                    _printImportedPackages(bundle, out, err);
+                    separatorNeeded = true;
+                }
+                else
+                {
+                    err.println("Bundle ID " + ids[idIdx] + " is invalid.");
+                }
+            }
+            catch (NumberFormatException ex)
+            {
+                err.println("Unable to parse id '" + ids[idIdx] + "'.");
+            }
+            catch (Exception ex)
+            {
+                err.println(ex.toString());
+            }
+        }
+    }
+
+    private void _printImportedPackages(Bundle bundle, PrintStream out, PrintStream err)
+    {
+        // Get package admin service.
+        PackageAdmin pa = getPackageAdmin();
+        if (pa == null)
+        {
+            out.println("PackageAdmin service is unavailable.");
+        }
+        else
+        {
+            ExportedPackage[] exports = pa.getExportedPackages((Bundle) null);
+            String title = bundle + " requires packages:";
+            out.println(title);
+            out.println(Util.getUnderlineString(title));
+            boolean found = false;
+            for (int expIdx = 0; expIdx < exports.length; expIdx++)
+            {
+                Bundle[] importers = exports[expIdx].getImportingBundles();
+                for (int impIdx = 0; (importers != null) && (impIdx < importers.length); impIdx++)
+                {
+                    if (importers[impIdx] == bundle)
+                    {
+                        out.println(exports[expIdx]
+                            + " -> " + exports[expIdx].getExportingBundle());
+                        found = true;
+                    }
+                }
+            }
+            if (!found)
+            {
+                out.println("Nothing");
+            }
+            ungetPackageAdmin();
+        }
+    }
+
+    private void printRequiringBundles(String[] ids, PrintStream out, PrintStream err)
+    {
+        PackageAdmin pa = getPackageAdmin();
+        if (pa == null)
+        {
+            out.println("PackageAdmin service is unavailable.");
+        }
+        else
+        {
+            boolean separatorNeeded = false;
+            for (int idIdx = 0; idIdx < ids.length; idIdx++)
+            {
+                try
+                {
+                    long l = Long.parseLong(ids[idIdx]);
+                    Bundle bundle = m_context.getBundle(l);
+                    if (bundle != null)
+                    {
+                        RequiredBundle[] rbs = pa.getRequiredBundles(bundle.getSymbolicName());
+                        for (int rbIdx = 0; (rbs != null) && (rbIdx < rbs.length); rbIdx++)
+                        {
+                            if (rbs[rbIdx].getBundle() == bundle)
+                            {
+                                if (separatorNeeded)
+                                {
+                                    out.println("");
+                                }
+                                String title = bundle + " required by bundles:";
+                                out.println(title);
+                                out.println(Util.getUnderlineString(title));
+                                if ((rbs[rbIdx].getRequiringBundles() != null)
+                                    && (rbs[rbIdx].getRequiringBundles().length > 0))
+                                {
+                                    for (int reqIdx = 0;
+                                        reqIdx < rbs[rbIdx].getRequiringBundles().length;
+                                        reqIdx++)
+                                    {
+                                        out.println(rbs[rbIdx].getRequiringBundles()[reqIdx]);
+                                    }
+                                }
+                                else
+                                {
+                                    out.println("Nothing");
+                                }
+                                separatorNeeded = true;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        err.println("Bundle ID " + ids[idIdx] + " is invalid.");
+                    }
+                }
+                catch (NumberFormatException ex)
+                {
+                    err.println("Unable to parse id '" + ids[idIdx] + "'.");
+                }
+                catch (Exception ex)
+                {
+                    err.println(ex.toString());
+                }
+            }
+        }
+    }
+
+    private void printRequiredBundles(String[] ids, PrintStream out, PrintStream err)
+    {
+        boolean separatorNeeded = false;
+        for (int idIdx = 0; idIdx < ids.length; idIdx++)
+        {
+            try
+            {
+                long l = Long.parseLong(ids[idIdx]);
+                Bundle bundle = m_context.getBundle(l);
+                if (bundle != null)
+                {
+                    if (separatorNeeded)
+                    {
+                        out.println("");
+                    }
+                    _printRequiredBundles(bundle, out, err);
+                    separatorNeeded = true;
+                }
+                else
+                {
+                    err.println("Bundle ID " + ids[idIdx] + " is invalid.");
+                }
+            }
+            catch (NumberFormatException ex)
+            {
+                err.println("Unable to parse id '" + ids[idIdx] + "'.");
+            }
+            catch (Exception ex)
+            {
+                err.println(ex.toString());
+            }
+        }
+    }
+
+    private void _printRequiredBundles(Bundle bundle, PrintStream out, PrintStream err)
+    {
+        // Get package admin service.
+        PackageAdmin pa = getPackageAdmin();
+        if (pa == null)
+        {
+            out.println("PackageAdmin service is unavailable.");
+        }
+        else
+        {
+            RequiredBundle[] rbs = pa.getRequiredBundles(null);
+            String title = bundle + " requires bundles:";
+            out.println(title);
+            out.println(Util.getUnderlineString(title));
+            boolean found = false;
+            for (int rbIdx = 0; rbIdx < rbs.length; rbIdx++)
+            {
+                Bundle[] requirers = rbs[rbIdx].getRequiringBundles();
+                for (int reqIdx = 0; (requirers != null) && (reqIdx < requirers.length); reqIdx++)
+                {
+                    if (requirers[reqIdx] == bundle)
+                    {
+                        out.println(rbs[reqIdx]);
+                        found = true;
+                    }
+                }
+            }
+            if (!found)
+            {
+                out.println("Nothing");
+            }
+            ungetPackageAdmin();
+        }
+    }
+
+    public void printExportedServices(String[] ids, PrintStream out, PrintStream err)
+    {
+        for (int i = 0; i < ids.length; i++)
+        {
+            try
+            {
+                long l = Long.parseLong(ids[i]);
+                Bundle bundle = m_context.getBundle(l);
+                if (bundle != null)
+                {
+                    ServiceReference[] refs = bundle.getRegisteredServices();
+
+                    // Print header if we have not already done so.
+                    String title = Util.getBundleName(bundle) + " provides services:";
+                    out.println(title);
+                    out.println(Util.getUnderlineString(title));
+
+                    if ((refs == null) || (refs.length == 0))
+                    {
+                        out.println("Nothing");
+                    }
+
+                    // Print properties for each service.
+                    for (int refIdx = 0;
+                        (refs != null) && (refIdx < refs.length);
+                        refIdx++)
+                    {
+                        // 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]);
+                            out.println(
+                                keys[keyIdx] + " = " + Util.getValueString(v));
+                        }
+
+                        // Print service separator if necessary.
+                        if ((refIdx + 1) < refs.length)
+                        {
+                            out.println("----");
+                        }
+                    }
+                }
+                else
+                {
+                    err.println("Bundle ID " + ids[i] + " is invalid.");
+                }
+            }
+            catch (NumberFormatException ex)
+            {
+                err.println("Unable to parse id '" + ids[i] + "'.");
+            }
+            catch (Exception ex)
+            {
+                err.println(ex.toString());
+            }
+        }
+    }
+
+    public void printImportedServices(String[] ids, PrintStream out, PrintStream err)
+    {
+        for (int i = 0; i < ids.length; i++)
+        {
+            try
+            {
+                long l = Long.parseLong(ids[i]);
+                Bundle bundle = m_context.getBundle(l);
+                if (bundle != null)
+                {
+                    ServiceReference[] refs = bundle.getServicesInUse();
+
+                    // Print header if we have not already done so.
+                    String title = Util.getBundleName(bundle) + " requires services:";
+                    out.println(title);
+                    out.println(Util.getUnderlineString(title));
+
+                    if ((refs == null) || (refs.length == 0))
+                    {
+                        out.println("Nothing");
+                    }
+
+                    // Print properties for each service.
+                    for (int refIdx = 0;
+                        (refs != null) && (refIdx < refs.length);
+                        refIdx++)
+                    {
+                        // 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]);
+                            out.println(
+                                keys[keyIdx] + " = " + Util.getValueString(v));
+                        }
+
+                        // Print service separator if necessary.
+                        if ((refIdx + 1) < refs.length)
+                        {
+                            out.println("----");
+                        }
+                    }
+                }
+                else
+                {
+                    err.println("Bundle ID " + ids[i] + " is invalid.");
+                }
+            }
+            catch (NumberFormatException ex)
+            {
+                err.println("Unable to parse id '" + ids[i] + "'.");
+            }
+            catch (Exception ex)
+            {
+                err.println(ex.toString());
+            }
+        }
+    }
+
+    private PackageAdmin getPackageAdmin()
+    {
+        PackageAdmin pa = null;
+        m_ref = m_context.getServiceReference(
+            org.osgi.service.packageadmin.PackageAdmin.class.getName());
+        if (m_ref != null)
+        {
+            pa = (PackageAdmin) m_context.getService(m_ref);
+        }
+        return pa;
+    }
+
+    private void ungetPackageAdmin()
+    {
+        m_context.ungetService(m_ref);
+    }
+
+    private boolean validTypeAndDirection(String type, String direction)
+    {
+        return
+            (((PACKAGE_TYPE.startsWith(type) || BUNDLE_TYPE.startsWith(type)
+                || FRAGMENT_TYPE.startsWith(type) || SERVICE_TYPE.startsWith(type)))
+            && (PROVIDE_DIRECTION.startsWith(direction)
+                || REQUIRE_DIRECTION.startsWith(direction)));
+    }
+}
\ No newline at end of file
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/RequirersCommandImpl.java b/shell/src/main/java/org/apache/felix/shell/impl/RequirersCommandImpl.java
deleted file mode 100644
index d21a404..0000000
--- a/shell/src/main/java/org/apache/felix/shell/impl/RequirersCommandImpl.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/* 
- * 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.shell.impl;
-
-import java.io.PrintStream;
-import java.util.StringTokenizer;
-
-import org.apache.felix.shell.Command;
-import org.osgi.framework.*;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.service.packageadmin.RequiredBundle;
-
-public class RequirersCommandImpl implements Command
-{
-    private BundleContext m_context = null;
-
-    public RequirersCommandImpl(BundleContext context)
-    {
-        m_context = context;
-    }
-
-    public String getName()
-    {
-        return "requirers";
-    }
-
-    public String getUsage()
-    {
-        return "requirers <id> ...";
-    }
-
-    public String getShortDescription()
-    {
-        return "list requiring bundles.";
-    }
-
-    public void execute(String s, PrintStream out, PrintStream err)
-    {
-        // Get package admin service.
-        ServiceReference ref = m_context.getServiceReference(
-            org.osgi.service.packageadmin.PackageAdmin.class.getName());
-        PackageAdmin pa = (ref == null) ? null : (PackageAdmin) m_context.getService(ref);
-        if (pa == null)
-        {
-            out.println("PackageAdmin service is unavailable.");
-            return;
-        }
-
-        // Parse command line.
-        StringTokenizer st = new StringTokenizer(s, " ");
-
-        // Ignore the command name.
-        st.nextToken();
-
-        if (st.hasMoreTokens())
-        {
-            boolean separatorNeeded = false;
-            while (st.hasMoreTokens())
-            {
-                String id = st.nextToken();
-                try
-                {
-                    long l = Long.parseLong(id);
-                    Bundle bundle = m_context.getBundle(l);
-                    if (bundle != null)
-                    {
-                        RequiredBundle[] rbs = pa.getRequiredBundles(bundle.getSymbolicName());
-                        for (int i = 0; (rbs != null) && (i < rbs.length); i++)
-                        {
-                            if (rbs[i].getBundle() == bundle)
-                            {
-                                if (separatorNeeded)
-                                {
-                                    out.println("");
-                                }
-                                printRequiredBundles(out, bundle, rbs[i].getRequiringBundles());
-                                separatorNeeded = true;
-                            }
-                        }
-                    }
-                    else
-                    {
-                        err.println("Bundle ID " + id + " is invalid.");
-                    }
-                }
-                catch (NumberFormatException ex)
-                {
-                    err.println("Unable to parse id '" + id + "'.");
-                }
-                catch (Exception ex)
-                {
-                    err.println(ex.toString());
-                }
-            }
-        }
-    }
-
-    private void printRequiredBundles(PrintStream out, Bundle target, Bundle[] requirers)
-    {
-        String title = target + " required by:";
-        out.println(title);
-        out.println(Util.getUnderlineString(title));
-        if ((requirers != null) && (requirers.length > 0))
-        {
-            for (int i = 0; i < requirers.length; i++)
-            {
-                out.println(requirers[i]);
-            }
-        }
-        else
-        {
-            out.println("Nothing");
-        }
-    }
-}
\ No newline at end of file
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/RequiresCommandImpl.java b/shell/src/main/java/org/apache/felix/shell/impl/RequiresCommandImpl.java
deleted file mode 100644
index c5b3c57..0000000
--- a/shell/src/main/java/org/apache/felix/shell/impl/RequiresCommandImpl.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/* 
- * 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.shell.impl;
-
-import java.io.PrintStream;
-import java.util.StringTokenizer;
-
-import org.apache.felix.shell.Command;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.service.packageadmin.RequiredBundle;
-
-public class RequiresCommandImpl implements Command
-{
-    private final BundleContext m_context;
-    private ServiceReference m_ref = null;
-
-    public RequiresCommandImpl(BundleContext context)
-    {
-        m_context = context;
-    }
-
-    public String getName()
-    {
-        return "requires";
-    }
-
-    public String getUsage()
-    {
-        return "requires <id> ...";
-    }
-
-    public String getShortDescription()
-    {
-        return "list required bundles.";
-    }
-
-    public void execute(String s, PrintStream out, PrintStream err)
-    {
-        StringTokenizer st = new StringTokenizer(s, " ");
-
-        // Ignore the command name.
-        st.nextToken();
-
-        if (st.hasMoreTokens())
-        {
-            boolean separatorNeeded = false;
-            while (st.hasMoreTokens())
-            {
-                String id = st.nextToken().trim();
-
-                try
-                {
-                    long l = Long.parseLong(id);
-                    Bundle bundle = m_context.getBundle(l);
-                    if (bundle != null)
-                    {
-                        if (separatorNeeded)
-                        {
-                            out.println("");
-                        }
-                        getImportedPackages(bundle, out, err);
-                        separatorNeeded = true;
-                    }
-                    else
-                    {
-                        err.println("Bundle ID " + id + " is invalid.");
-                    }
-                }
-                catch (NumberFormatException ex)
-                {
-                    err.println("Unable to parse id '" + id + "'.");
-                }
-                catch (Exception ex)
-                {
-                    err.println(ex.toString());
-                }
-            }
-        }
-    }
-
-    private void getImportedPackages(Bundle bundle, PrintStream out, PrintStream err)
-    {
-        // Get package admin service.
-        PackageAdmin pa = getPackageAdmin();
-        if (pa == null)
-        {
-            out.println("PackageAdmin service is unavailable.");
-        }
-        else
-        {
-            RequiredBundle[] rbs = pa.getRequiredBundles(null);
-            String title = bundle + " requires:";
-            out.println(title);
-            out.println(Util.getUnderlineString(title));
-            boolean found = false;
-            for (int rbIdx = 0; rbIdx < rbs.length; rbIdx++)
-            {
-                Bundle[] requirers = rbs[rbIdx].getRequiringBundles();
-                for (int reqIdx = 0; (requirers != null) && (reqIdx < requirers.length); reqIdx++)
-                {
-                    if (requirers[reqIdx] == bundle)
-                    {
-                        out.println(rbs[reqIdx]);
-                        found = true;
-                    }
-                }
-            }
-            if (!found)
-            {
-                out.println("Nothing");
-            }
-            ungetPackageAdmin();
-        }
-    }
-
-    private PackageAdmin getPackageAdmin()
-    {
-        PackageAdmin pa = null;
-        m_ref = m_context.getServiceReference(
-            org.osgi.service.packageadmin.PackageAdmin.class.getName());
-        if (m_ref != null)
-        {
-            pa = (PackageAdmin) m_context.getService(m_ref);
-        }
-        return pa;
-    }
-
-    private void ungetPackageAdmin()
-    {
-        m_context.ungetService(m_ref);
-    }
-}
\ No newline at end of file
diff --git a/shell/src/main/java/org/apache/felix/shell/impl/ServicesCommandImpl.java b/shell/src/main/java/org/apache/felix/shell/impl/ServicesCommandImpl.java
deleted file mode 100644
index 47ffe72..0000000
--- a/shell/src/main/java/org/apache/felix/shell/impl/ServicesCommandImpl.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/* 
- * 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.shell.impl;
-
-import java.io.PrintStream;
-import java.util.*;
-
-import org.apache.felix.shell.Command;
-import org.osgi.framework.*;
-
-public class ServicesCommandImpl implements Command
-{
-    private static final String IN_USE_SWITCH = "-u";
-    private static final String SHOW_ALL_SWITCH = "-a";
-
-    private BundleContext m_context = null;
-
-    public ServicesCommandImpl(BundleContext context)
-    {
-        m_context = context;
-    }
-
-    public String getName()
-    {
-        return "services";
-    }
-
-    public String getUsage()
-    {
-        return "services [-u] [-a] [<id> ...]";
-    }
-
-    public String getShortDescription()
-    {
-        return "list registered or used services.";
-    }
-
-    public void execute(String s, PrintStream out, PrintStream err)
-    {
-        StringTokenizer st = new StringTokenizer(s, " ");
-
-        // Ignore the command name.
-        st.nextToken();
-
-        // Put the remaining tokens into a list.
-        List tokens = new ArrayList();
-        for (int i = 0; st.hasMoreTokens(); i++)
-        {
-            tokens.add(st.nextToken());
-        }
-
-        // Default switch values.
-        boolean inUse = false;
-        boolean showAll = false;
-
-        // Check for "in use" switch.
-        if (tokens.contains(IN_USE_SWITCH))
-        {
-            // Remove the switch and set boolean flag.
-            tokens.remove(IN_USE_SWITCH);
-            inUse = true;
-        }
-
-        // Check for "show all" switch.
-        if (tokens.contains(SHOW_ALL_SWITCH))
-        {
-            // Remove the switch and set boolean flag.
-            tokens.remove(SHOW_ALL_SWITCH);
-            showAll = true;
-        }
-
-        // If there are bundle IDs specified then print their
-        // services and associated service properties, otherwise
-        // list all bundles and services.
-        if (tokens.size() >= 1)
-        {
-            while (tokens.size() > 0)
-            {
-                String id = (String) tokens.remove(0);
-
-                boolean headerPrinted = false;
-                boolean needSeparator = false;
-
-                try
-                {
-                    long l = Long.parseLong(id);
-                    Bundle bundle = m_context.getBundle(l);
-                    if (bundle != null)
-                    {
-                        ServiceReference[] refs = null;
-                        
-                        // Get registered or in-use services.
-                        if (inUse)
-                        {
-                            refs = bundle.getServicesInUse();
-                        }
-                        else
-                        {
-                            refs = bundle.getRegisteredServices();
-                        }
-
-                        // Print properties for each service.
-                        for (int refIdx = 0;
-                            (refs != null) && (refIdx < refs.length);
-                            refIdx++)
-                        {
-                            String[] objectClass = (String[])
-                                refs[refIdx].getProperty("objectClass");
-
-                            // Determine if we need to print the service, depending
-                            // on whether it is a command service or not.
-                            boolean print = true;
-                            for (int ocIdx = 0;
-                                !showAll && (ocIdx < objectClass.length);
-                                ocIdx++)
-                            {
-                                if (objectClass[ocIdx].equals(
-                                    org.apache.felix.shell.Command.class.getName()))
-                                {
-                                    print = false;
-                                }
-                            }
-
-                            // Print header if we have not already done so.
-                            if (!headerPrinted)
-                            {
-                                headerPrinted = true;
-                                String title = Util.getBundleName(bundle);
-                                title = (inUse)
-                                    ? title + " uses:"
-                                    : title + " provides:";
-                                out.println("");
-                                out.println(title);
-                                out.println(Util.getUnderlineString(title));
-                            }
-
-                            if (showAll || print)
-                            {
-                                // Print service separator if necessary.
-                                if (needSeparator)
-                                {
-                                    out.println("----");
-                                }
-
-                                // 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]);
-                                    out.println(
-                                        keys[keyIdx] + " = " + Util.getValueString(v));
-                                }
-                                
-                                needSeparator = true;
-                            }
-                        }
-                    }
-                    else
-                    {
-                        err.println("Bundle ID " + id + " is invalid.");
-                    }
-                }
-                catch (NumberFormatException ex)
-                {
-                    err.println("Unable to parse id '" + id + "'.");
-                }
-                catch (Exception ex)
-                {
-                    err.println(ex.toString());
-                }
-            }
-        }
-        else
-        {
-            Bundle[] bundles = m_context.getBundles();
-            if (bundles != null)
-            {
-                // TODO: Sort list.
-                for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
-                {
-                    boolean headerPrinted = false;
-                    ServiceReference[] refs = null;
-
-                    // Get registered or in-use services.
-                    if (inUse)
-                    {
-                        refs = bundles[bundleIdx].getServicesInUse();
-                    }
-                    else
-                    {
-                        refs = bundles[bundleIdx].getRegisteredServices();
-                    }
-
-                    for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
-                    { 
-                        String[] objectClass = (String[])
-                            refs[refIdx].getProperty("objectClass");
-
-                        // Determine if we need to print the service, depending
-                        // on whether it is a command service or not.
-                        boolean print = true;
-                        for (int ocIdx = 0;
-                            !showAll && (ocIdx < objectClass.length);
-                            ocIdx++)
-                        {
-                            if (objectClass[ocIdx].equals(
-                                org.apache.felix.shell.Command.class.getName()))
-                            {
-                                print = false;
-                            }
-                        }
-
-                        // Print the service if necessary.
-                        if (showAll || print)
-                        {
-                            if (!headerPrinted)
-                            {
-                                headerPrinted = true;
-                                String title = Util.getBundleName(bundles[bundleIdx]);
-                                title = (inUse)
-                                    ? title + " uses:"
-                                    : title + " provides:";
-                                out.println("\n" + title);
-                                out.println(Util.getUnderlineString(title));
-                            }
-                            out.println(Util.getValueString(objectClass));
-                        }
-                    }
-                }
-            }
-            else
-            {
-                out.println("There are no registered services.");
-            }
-        }
-    }
-}