content: /*
   * 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.aries.blueprint.container;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertNotSame;
  import static org.junit.Assert.assertNull;
  import static org.junit.Assert.assertSame;
  import static org.junit.Assert.assertThrows;
  import static org.junit.Assert.assertTrue;
  
  import java.io.ByteArrayOutputStream;
  import java.lang.reflect.Constructor;
  import java.math.BigInteger;
  import java.net.URI;
  import java.net.URL;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.Hashtable;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Locale;
  import java.util.Properties;
  
  import org.apache.aries.blueprint.TestBlueprintContainer;
  import org.apache.aries.blueprint.pojos.PojoGenerics2.MyClass;
  import org.apache.aries.blueprint.pojos.PojoGenerics2.MyObject;
  import org.apache.aries.blueprint.pojos.PojoGenerics2.Tata;
  import org.apache.aries.blueprint.pojos.PojoGenerics2.Toto;
  import org.junit.Before;
  import org.junit.Test;
  import org.osgi.service.blueprint.container.Converter;
  import org.osgi.service.blueprint.container.ReifiedType;
  
  public class AggregateConverterTest {
  
      private AggregateConverter service;
  
      @Before
      public void setUp() throws Exception {
          service = new AggregateConverter(new TestBlueprintContainer(null));
      }
  
      @Test
      public void testConvertNumbers() throws Exception {
          assertEquals(1, service.convert(1.46f, int.class));
          assertEquals(1.0d, service.convert(1, double.class));
      }
  
      @Test
      public void testConvertSimpleTypes() throws Exception {
          assertEquals(123, service.convert("123", int.class));
          assertEquals(123, service.convert("123", Integer.class));
          assertEquals(123l, service.convert("123", long.class));
          assertEquals(123l, service.convert("123", Long.class));
          assertEquals((short) 123, service.convert("123", short.class));
          assertEquals((short) 123, service.convert("123", Short.class));
          assertEquals(1.5f, service.convert("1.5", float.class));
          assertEquals(1.5f, service.convert("1.5", Float.class));
          assertEquals(1.5, service.convert("1.5", double.class));
          assertEquals(1.5, service.convert("1.5", Double.class));
      }
  
      @Test
      public void testConvertCharacter() throws Exception {
          assertEquals('c', service.convert("c", char.class));
          assertEquals('c', service.convert("c", Character.class));
          assertEquals('\u00F6', service.convert("\\u00F6", char.class));
          assertEquals('\u00F6', service.convert("\\u00F6", Character.class));
      }
  
      @Test
      public void testConvertBoolean() throws Exception {
          assertEquals(Boolean.TRUE, service.convert("true", Boolean.class));
          assertEquals(Boolean.TRUE, service.convert("yes", Boolean.class));
          assertEquals(Boolean.TRUE, service.convert("on", Boolean.class));
          assertEquals(Boolean.TRUE, service.convert("TRUE", Boolean.class));
          assertEquals(Boolean.TRUE, service.convert("YES", Boolean.class));
          assertEquals(Boolean.TRUE, service.convert("ON", Boolean.class));
          assertEquals(Boolean.TRUE, service.convert("true", boolean.class));
          assertEquals(Boolean.TRUE, service.convert("yes", boolean.class));
          assertEquals(Boolean.TRUE, service.convert("on", boolean.class));
          assertEquals(Boolean.TRUE, service.convert("TRUE", boolean.class));
          assertEquals(Boolean.TRUE, service.convert("YES", boolean.class));
          assertEquals(Boolean.TRUE, service.convert("ON", boolean.class));
          assertEquals(Boolean.FALSE, service.convert("false", Boolean.class));
          assertEquals(Boolean.FALSE, service.convert("no", Boolean.class));
          assertEquals(Boolean.FALSE, service.convert("off", Boolean.class));
          assertEquals(Boolean.FALSE, service.convert("FALSE", Boolean.class));
          assertEquals(Boolean.FALSE, service.convert("NO", Boolean.class));
          assertEquals(Boolean.FALSE, service.convert("OFF", Boolean.class));
          assertEquals(Boolean.FALSE, service.convert("false", boolean.class));
          assertEquals(Boolean.FALSE, service.convert("no", boolean.class));
          assertEquals(Boolean.FALSE, service.convert("off", boolean.class));
          assertEquals(Boolean.FALSE, service.convert("FALSE", boolean.class));
          assertEquals(Boolean.FALSE, service.convert("NO", boolean.class));
          assertEquals(Boolean.FALSE, service.convert("OFF", boolean.class));
          
          assertEquals(Boolean.FALSE, service.convert(false, boolean.class));
          assertEquals(Boolean.TRUE, service.convert(true, boolean.class));        
          assertEquals(Boolean.FALSE, service.convert(false, Boolean.class));
          assertEquals(Boolean.TRUE, service.convert(true, Boolean.class));
      }
  
      @Test
      public void testConvertOther() throws Exception {
          assertEquals(URI.create("urn:test"), service.convert("urn:test", URI.class));
          assertEquals(new URL("file:/test"), service.convert("file:/test", URL.class));
          assertEquals(new BigInteger("12345"), service.convert("12345", BigInteger.class));
      }
  
      @Test
      public void testConvertProperties() throws Exception {
          Properties props = new Properties();
          props.setProperty("key", "value");
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          props.store(baos, null);
          props = (Properties) service.convert(baos.toString(), Properties.class);
          assertEquals(1, props.size());
          assertEquals("value", props.getProperty("key"));
      }
  
      @Test
      public void testConvertLocale() throws Exception {
          Object result;
          result = service.convert("en", Locale.class);
          assertTrue(result instanceof Locale);
          assertEquals(new Locale("en"), result);
          
          result = service.convert("de_DE", Locale.class);
          assertTrue(result instanceof Locale);
          assertEquals(new Locale("de", "DE"), result);
          
          result = service.convert("_GB", Locale.class);
          assertTrue(result instanceof Locale);
          assertEquals(new Locale("", "GB"), result);
          
          result = service.convert("en_US_WIN", Locale.class);
          assertTrue(result instanceof Locale);
          assertEquals(new Locale("en", "US", "WIN"), result);
          
          result = service.convert("de__POSIX", Locale.class);
          assertTrue(result instanceof Locale);
          assertEquals(new Locale("de", "", "POSIX"), result);
      }
  
      @Test
      public void testConvertClass() throws Exception {
          assertEquals(this, service.convert(this, AggregateConverterTest.class));
          assertEquals(AggregateConverterTest.class, service.convert(this.getClass().getName(), Class.class));
          assertEquals(int[].class, service.convert("int[]", Class.class));
          assertEquals(RegionIterable.class, service.convert(RegionIterable.class.getName(), new GenericType(Class.class, new GenericType(RegionIterable.class))));
          assertTrue(AggregateConverter.isAssignable(RegionIterable.class, new GenericType(Class.class, new GenericType(RegionIterable.class))));
      }
  
      @Test
      public void testConvertArray() throws Exception {
          Object obj = service.convert(Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4)),
                                       GenericType.parse("java.util.List<java.lang.Integer>[]", getClass().getClassLoader()));
          assertNotNull(obj);
          assertTrue(obj.getClass().isArray());
          Object[] o = (Object[]) obj;
          assertEquals(2, o.length);
          assertNotNull(o[0]);
          assertTrue(o[0] instanceof List);
          assertEquals(2, ((List) o[0]).size());
          assertEquals(1, ((List) o[0]).get(0));
          assertEquals(2, ((List) o[0]).get(1));
          assertNotNull(o[0]);
          assertTrue(o[1] instanceof List);
          assertEquals(2, ((List) o[1]).size());
          assertEquals(3, ((List) o[1]).get(0));
          assertEquals(4, ((List) o[1]).get(1));
          //assertEquals((Object) new int[] { 1, 2 }, (Object) service.convert(Arrays.asList(1, 2), int[].class));
      }
  
      @Test
      public void testCustom() throws Exception {
          AggregateConverter s = new AggregateConverter(new TestBlueprintContainer(null));
          s.registerConverter(new RegionConverter());
          s.registerConverter(new EuRegionConverter());
          
          // lookup on a specific registered converter type
          Object result;
          result = s.convert(new Object(), Region.class);
          assertTrue(result instanceof Region);
          assertFalse(result instanceof EuRegion);
                  
          result = s.convert(new Object(), EuRegion.class);
          assertTrue(result instanceof EuRegion);
          
          // find first converter that matches the type
          s = new AggregateConverter(new TestBlueprintContainer(null));
          s.registerConverter(new AsianRegionConverter());
          s.registerConverter(new EuRegionConverter());
          s.registerConverter(new NullMarkerConverter());
          
          result = s.convert(new Object(), Region.class);
          // TODO: check with the spec about the result
          //assertTrue(result instanceof AsianRegion || result instanceof EuRegion);
          result = s.convert(new Object(), NullMarker.class);
          assertNull(result);
      }
  
      @Test
      public void testGenericWilcard() throws Exception {
          Constructor cns = MyClass.class.getConstructor(MyObject.class);
          assertTrue(AggregateConverter.isAssignable(new Toto(), new GenericType(cns.getGenericParameterTypes()[0])));
  
          cns = Tata.class.getConstructor(MyObject.class);
          assertTrue(AggregateConverter.isAssignable(new Toto(), new GenericType(cns.getGenericParameterTypes()[0])));
      }
  
      @Test
      public void testGenericAssignable() throws Exception {
          AggregateConverter s = new AggregateConverter(new TestBlueprintContainer(null));
  
          assertNotNull(s.convert(new RegionIterable(), new GenericType(Iterable.class, new GenericType(Region.class))));
  
          assertThrows(Exception.class, () ->
                  s.convert(new ArrayList<Region>(), new GenericType(Iterable.class, new GenericType(Region.class))));
  
          assertTrue(Iterable.class.isAssignableFrom(RegionIterable.class));
          // note that method signature is fromType, toType - reverse than above
          assertTrue("Type should be assignable.", AggregateConverter.isTypeAssignable(new GenericType(RegionIterable.class), new GenericType(Iterable.class)));
      }
  
      @Test
      public void testGenericCollection() throws Exception {
          AggregateConverter s = new AggregateConverter(new TestBlueprintContainer(null));
  
          assertThrows(Exception.class,
                  () -> s.convert(new ArrayList<Region>(), new GenericType(Iterable.class, new GenericType(Region.class))));
  
          assertThrows(Exception.class,
                  () -> s.convert(Arrays.asList(0l), new GenericType(Iterable.class, new GenericType(Region.class))));
  
          assertNotNull(s.convert(Arrays.asList(new EuRegion() {}), new GenericType(List.class, new GenericType(Region.class))));
      }
  
      @Test
      public void testConvertCompatibleCollections() throws Exception {
          Object org = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));
          Object obj = service.convert(org,
                  GenericType.parse("java.util.List<java.util.List<java.lang.Integer>>", getClass().getClassLoader()));
          assertSame(org, obj);
  
          org = Collections.singletonMap("foo", 1);
          obj = service.convert(org,
                  GenericType.parse("java.util.Map<java.lang.String,java.lang.Integer>", getClass().getClassLoader()));
          assertSame(org, obj);
  
          org = new int[] { 1, 2 };
          obj = service.convert(org,
                  GenericType.parse("int[]", getClass().getClassLoader()));
          assertSame(org, obj);
  
          org = new Object[] { 1, 2 };
          obj = service.convert(org,
                  GenericType.parse("int[]", getClass().getClassLoader()));
          assertNotSame(org, obj);
  
          Hashtable<String, Integer> ht = new Hashtable<String, Integer>();
          ht.put("foo", 1);
          org = ht;
          obj = service.convert(org, GenericType.parse("java.util.Dictionary<java.lang.String,java.lang.Integer>", getClass().getClassLoader()));
          assertSame(org, obj);;
      }
      
      private interface Region {} 
      
      private interface EuRegion extends Region {}
      
      private interface AsianRegion extends Region {}
  
      private interface NullMarker {}
      
      private static class RegionConverter implements Converter {
          public boolean canConvert(Object fromValue, ReifiedType toType) {
              return Region.class == toType.getRawClass();
          }
          public Object convert(Object source, ReifiedType toType) throws Exception {
              return new Region() {} ;
          }
      }
      
      private static class EuRegionConverter implements Converter {
          public boolean canConvert(Object fromValue, ReifiedType toType) {
              return toType.getRawClass().isAssignableFrom(EuRegion.class);
          }
          public Object convert(Object source, ReifiedType toType) throws Exception {
              return new EuRegion() {} ;
          }
      }
      
      private static class AsianRegionConverter implements Converter {
          public boolean canConvert(Object fromValue, ReifiedType toType) {
              return toType.getRawClass().isAssignableFrom(AsianRegion.class);
          }
          public Object convert(Object source, ReifiedType toType) throws Exception {
              return new AsianRegion() {} ;
          }
      }
  
      private static class NullMarkerConverter implements Converter {
          public boolean canConvert(Object fromValue, ReifiedType toType) {
              return toType.getRawClass().isAssignableFrom(NullMarker.class);
          }
          public Object convert(Object source, ReifiedType toType) throws Exception {
              return null;
          }
      }
  
      private static class RegionIterable implements Iterable<Region> {
          public Iterator<Region> iterator() {
              return null;
          }
      }
  
  }
  