FELIX-4403 complete ServiceReferenceDTO info, with what the spec says about type conversions, however implausible that is, and update SCRCommand appropriately

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1617027 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java b/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java
index ca29a8e..ff5074a 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java
@@ -19,6 +19,7 @@
 package org.apache.felix.scr.impl;
 
 import java.io.PrintWriter;
+import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -392,11 +393,22 @@
                 Object prop = entry.getValue();
                 if ( prop.getClass().isArray() )
                 {
-                    prop = Arrays.asList( ( Object[] ) prop );
+                    out.print("[");
+                    int length = Array.getLength(prop);
+                    for (int i = 0; i< length; i++)
+                    {
+                        out.print(Array.get(prop, i));
+                        if ( i < length - 1)
+                        {
+                            out.print(", ");
+                        }
+                    }
+                    out.println("]");
+                } 
+                else
+                {
+                    out.println( prop );
                 }
-                out.print( prop );
-
-                out.println();
             }
         }
     }
@@ -422,6 +434,7 @@
               {
                   out.print( "        " );
                   out.println( sr.id );
+                  propertyInfo(sr.properties, out, "        ");
               }
           }
           else
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/runtime/ServiceComponentRuntimeImpl.java b/scr/src/main/java/org/apache/felix/scr/impl/runtime/ServiceComponentRuntimeImpl.java
index 2d375bb..ba00533 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/runtime/ServiceComponentRuntimeImpl.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/runtime/ServiceComponentRuntimeImpl.java
@@ -18,11 +18,13 @@
  */
 package org.apache.felix.scr.impl.runtime;
 
+import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.felix.scr.impl.ComponentRegistry;
 import org.apache.felix.scr.impl.config.ComponentHolder;
@@ -30,8 +32,10 @@
 import org.apache.felix.scr.impl.config.ReferenceManager;
 import org.apache.felix.scr.impl.metadata.ComponentMetadata;
 import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
+import org.osgi.dto.DTO;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.dto.BundleDTO;
 import org.osgi.framework.dto.ServiceReferenceDTO;
@@ -43,7 +47,8 @@
 import org.osgi.service.component.runtime.dto.UnsatisfiedReferenceDTO;
 import org.osgi.util.promise.Promise;
 
-public class ServiceComponentRuntimeImpl implements ServiceComponentRuntime {
+public class ServiceComponentRuntimeImpl implements ServiceComponentRuntime
+{
 	
 	private static final String[] EMPTY = {};
 	
@@ -51,14 +56,14 @@
 	private final ComponentRegistry componentRegistry;
 
 
-	public ServiceComponentRuntimeImpl(BundleContext context,
-			ComponentRegistry componentRegistry) {
+	public ServiceComponentRuntimeImpl(BundleContext context,ComponentRegistry componentRegistry) 
+	{
 		this.context = context;
 		this.componentRegistry = componentRegistry;
 	}
 
-	public Collection<ComponentDescriptionDTO> getComponentDescriptionDTOs(
-			Bundle... bundles) {
+	public Collection<ComponentDescriptionDTO> getComponentDescriptionDTOs(Bundle... bundles) 
+	{
 		List<ComponentHolder<?>> holders;
 		if (bundles == null || bundles.length == 0)
 		{
@@ -77,7 +82,8 @@
 		return result;
 	}
 
-	public ComponentDescriptionDTO getComponentDescriptionDTO(Bundle bundle, String name) {
+	public ComponentDescriptionDTO getComponentDescriptionDTO(Bundle bundle, String name)
+	{
 	    ComponentHolder<?> holder = componentRegistry.getComponentHolder(bundle, name);
 		if ( holder != null )
 		{
@@ -89,8 +95,8 @@
 		}
 	}
 
-	public Collection<ComponentConfigurationDTO> getComponentConfigurationDTOs(
-			ComponentDescriptionDTO description) {
+	public Collection<ComponentConfigurationDTO> getComponentConfigurationDTOs(ComponentDescriptionDTO description) 
+	{
 		if ( description == null)
 		{
 			return Collections.emptyList();
@@ -107,23 +113,26 @@
 		return result;
 	}
 
-	public boolean isComponentEnabled(ComponentDescriptionDTO description) {
+	public boolean isComponentEnabled(ComponentDescriptionDTO description) 
+	{
 		ComponentHolder<?> holder = getHolderFromDescription( description);
 		return holder.isEnabled();
 	}
 
-	public Promise<Void> enableComponent(ComponentDescriptionDTO description) {
+	public Promise<Void> enableComponent(ComponentDescriptionDTO description) 
+	{
 		ComponentHolder<?> holder = getHolderFromDescription( description);
 		return holder.enableComponents(true);
 	}
 
-	public Promise<Void> disableComponent(ComponentDescriptionDTO description) {
+	public Promise<Void> disableComponent(ComponentDescriptionDTO description)
+	{
 		ComponentHolder<?> holder = getHolderFromDescription( description);
 		return holder.disableComponents(true); //synchronous
 	}
 	
-	private ComponentConfigurationDTO managerToConfiguration(
-			ComponentManager<?> manager, ComponentDescriptionDTO description) {
+	private ComponentConfigurationDTO managerToConfiguration(ComponentManager<?> manager, ComponentDescriptionDTO description) 
+	{
 		ComponentConfigurationDTO dto = new ComponentConfigurationDTO();
         dto.satisfiedReferences = satisfiedRefManagersToDTO(manager.getReferenceManagers());
         dto.unsatisfiedReferences = unsatisfiedRefManagersToDTO(manager.getReferenceManagers());
@@ -134,7 +143,8 @@
 		return dto;
 	}
 
-    private SatisfiedReferenceDTO[] satisfiedRefManagersToDTO(List<? extends ReferenceManager<?, ?>> referenceManagers) {
+    private SatisfiedReferenceDTO[] satisfiedRefManagersToDTO(List<? extends ReferenceManager<?, ?>> referenceManagers)
+    {
         List<SatisfiedReferenceDTO> dtos = new ArrayList<SatisfiedReferenceDTO>();
         for (ReferenceManager<?, ?> ref: referenceManagers)
         {
@@ -157,7 +167,8 @@
         return dtos.toArray( new SatisfiedReferenceDTO[dtos.size()] );
     }
 
-    private UnsatisfiedReferenceDTO[] unsatisfiedRefManagersToDTO(List<? extends ReferenceManager<?, ?>> referenceManagers) {
+    private UnsatisfiedReferenceDTO[] unsatisfiedRefManagersToDTO(List<? extends ReferenceManager<?, ?>> referenceManagers) 
+    {
         List<UnsatisfiedReferenceDTO> dtos = new ArrayList<UnsatisfiedReferenceDTO>();
         for (ReferenceManager<?, ?> ref: referenceManagers)
         {
@@ -180,17 +191,27 @@
         return dtos.toArray( new UnsatisfiedReferenceDTO[dtos.size()] );
     }
 
-	private ServiceReferenceDTO serviceReferenceToDTO(
-			ServiceReference<?> serviceRef) {
+	private ServiceReferenceDTO serviceReferenceToDTO( ServiceReference<?> serviceRef)
+	{
 		ServiceReferenceDTO dto = new ServiceReferenceDTO();
 		dto.bundle = serviceRef.getBundle().getBundleId();
-		dto.id = (Long) serviceRef.getProperty("service.id"); //TODO use proper constant
-		//TODO service properties, using bundles
-		return dto;
+		dto.id = (Long) serviceRef.getProperty(Constants.SERVICE_ID);
+		dto.properties = deepCopy( serviceRef );
+		Bundle[] usingBundles = serviceRef.getUsingBundles();
+		if (usingBundles != null)
+        {
+            long[] usingBundleIds = new long[usingBundles.length];
+            for (int i = 0; i < usingBundles.length; i++)
+            {
+                usingBundleIds[i] = usingBundles[i].getBundleId();
+            }
+            dto.usingBundles = usingBundleIds;
+        }
+        return dto;
 	}
 
-	private ComponentHolder<?> getHolderFromDescription(
-			ComponentDescriptionDTO description) {
+	private ComponentHolder<?> getHolderFromDescription(ComponentDescriptionDTO description)
+	{
 		if (description.bundle == null)
 		{
 			throw new IllegalArgumentException("No bundle supplied in ComponentDescriptionDTO named " + description.name);
@@ -216,14 +237,72 @@
 		dto.implementationClass = m.getImplementationClassName();
 		dto.modified = m.getModified();
 		dto.name = m.getName();
-		dto.properties = new HashMap<String, Object>(m.getProperties());// TODO deep copy of arrays
+		dto.properties = deepCopy(m.getProperties());
 		dto.references = refsToDTO(m.getDependencies());
 		dto.scope = m.getServiceMetadata() == null? null: m.getServiceMetadata().getScope().name();
 		dto.serviceInterfaces = m.getServiceMetadata() == null? EMPTY: m.getServiceMetadata().getProvides();
 		return dto;
 	}
+	
+    private Map<String, Object> deepCopy(Map<String, Object> source)
+    {
+        HashMap<String, Object> result = new HashMap<String, Object>(source.size());
+        for (Map.Entry<String, Object> entry: source.entrySet())
+        {
+            result.put(entry.getKey(), convert(entry.getValue()));
+        }
+        return result;
+    }
 
-	private ReferenceDTO[] refsToDTO(List<ReferenceMetadata> dependencies) {
+    private Map<String, Object> deepCopy(ServiceReference<?> source)
+    {
+        String[] keys = source.getPropertyKeys();
+        HashMap<String, Object> result = new HashMap<String, Object>(keys.length);
+        for (int i = 0; i< keys.length; i++)
+        {
+            result.put(keys[i], convert(source.getProperty(keys[i])));
+        }
+        return result;
+    }
+
+    Object convert(Object source)
+	{
+	    if (source.getClass().isArray())
+	    {
+	        Class<?> type = source.getClass().getComponentType();
+	        if (checkType(type))
+	        {
+	            return source;
+	        }
+	        return String.valueOf(source);
+	        /* array copy code in case it turns out to be needed
+	        int length = Array.getLength(source);
+            Object copy = Array.newInstance(type, length);
+	        for (int i = 0; i<length; i++)
+	        {
+	            Array.set(copy, i, Array.get(source, i));
+	        }
+	        return copy;
+	        */
+	    }
+	    if (checkType(source.getClass()))
+	    {
+	        return source;
+	    }
+	    return String.valueOf(source);
+	}
+    
+    boolean checkType(Class<?> type)
+    {
+        if (type == String.class) return true;
+        if (type == Boolean.class) return true;
+        if (Number.class.isAssignableFrom(type)) return true;
+        if (DTO.class.isAssignableFrom(type)) return true;
+        return false;            
+    }
+	
+	private ReferenceDTO[] refsToDTO(List<ReferenceMetadata> dependencies) 
+	{
 		ReferenceDTO[] dtos = new ReferenceDTO[dependencies.size()];
 		int i = 0;
 		for (ReferenceMetadata r: dependencies)
@@ -244,7 +323,8 @@
 		return dtos;
 	}
 
-	private BundleDTO bundleToDTO(BundleContext bundleContext) {
+	private BundleDTO bundleToDTO(BundleContext bundleContext) 
+	{
 		if (bundleContext == null)
 		{
 			return null;
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/SCRCommandlTest.java b/scr/src/test/java/org/apache/felix/scr/impl/SCRCommandlTest.java
new file mode 100644
index 0000000..b93c7a9
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/SCRCommandlTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scr.impl;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.osgi.dto.DTO;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.framework.dto.ServiceReferenceDTO;
+
+import junit.framework.TestCase;
+
+public class SCRCommandlTest extends TestCase
+{
+ 
+    
+    public void testPropertyInfo()
+    {
+        ScrCommand scr = new ScrCommand(null, null, null);
+         check(scr, "  Properties:\n    key = [1, 2]\n", new int[] {1, 2});
+         check(scr, "  Properties:\n    key = [1, 2]\n", new String[] {"1", "2"});
+         check(scr, "  Properties:\n    key = [true, false]\n", new Boolean[] {true, false});
+         check(scr, "  Properties:\n    key = foo\n", "foo");
+         check(scr, "  Properties:\n    key = true\n", true);
+    }
+
+    private PrintWriter check(ScrCommand scr, String expected, Object o)
+    {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        scr.propertyInfo(Collections.<String, Object>singletonMap("key", o), pw, "");
+        assertEquals(expected, sw.toString());
+        return pw;
+    }
+    
+}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/runtime/ServiceComponentRuntimeImplTest.java b/scr/src/test/java/org/apache/felix/scr/impl/runtime/ServiceComponentRuntimeImplTest.java
new file mode 100644
index 0000000..c52521f
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/runtime/ServiceComponentRuntimeImplTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.scr.impl.runtime;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+
+import org.osgi.dto.DTO;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.framework.dto.ServiceReferenceDTO;
+
+import junit.framework.TestCase;
+
+public class ServiceComponentRuntimeImplTest extends TestCase
+{
+ 
+    /* in case behavior is supposed to actually involve copying
+    public void testCopy()
+    {
+        ServiceComponentRuntimeImpl scr = new ServiceComponentRuntimeImpl(null, null);
+        assertEquals(1, scr.convert(1));
+        assertEquals("foo", scr.convert("foo"));
+        equalCopy(new int[] {1, 2, 3}, scr);
+        equalCopy(new byte[] {1, 2, 3}, scr);
+        equalCopy(new String[] {"1", "2", "3"}, scr);
+        equalCopy(new Long[] {1l, 2l, 3l}, scr);
+    }
+    
+    private void equalCopy(Object o1, ServiceComponentRuntimeImpl scr)
+    {
+        Object o2 = scr.convert(o1);
+        assertEquals("expected same length", Array.getLength(o1), Array.getLength(o2));
+        assertEquals("expceted same component type", o1.getClass().getComponentType(), o2.getClass().getComponentType());
+        for (int i = 0; i < Array.getLength(o1); i++)
+        {
+            assertEquals("expected same value at " + i, Array.get(o1, i), Array.get(o2, i));
+        }
+        
+    }
+    */
+    
+    public void testConvert()
+    {
+        ServiceComponentRuntimeImpl scr = new ServiceComponentRuntimeImpl(null, null);
+        same("foo", scr);
+        same(Boolean.TRUE, scr);
+        same(1, scr);
+        same(1l, scr);
+        same(new ServiceReferenceDTO(), scr);
+        same( new String[] {"foo", "bar"}, scr);
+        same( new Boolean[] {true, false}, scr);
+        same( new Long[] {1l, 2l}, scr);
+        same( new DTO[] {new ServiceReferenceDTO(), new BundleDTO()}, scr);
+        equalsToString(new int[] {1, 2}, scr);
+        equalsToString(Arrays.asList(new int[] {1, 2}), scr);
+        equalsToString(Arrays.asList(new String[] {"foo", "bar"}), scr);
+    }
+    
+    private void equalsToString(Object o, ServiceComponentRuntimeImpl scr)
+    {
+        assertEquals(String.valueOf(o), scr.convert(o));        
+    }
+
+    private void same(Object o, ServiceComponentRuntimeImpl scr)
+    {
+        assertSame(o, scr.convert(o));
+    }
+
+}