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.proxy;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertTrue;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.when;
  
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Method;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.concurrent.Callable;
  
  import org.apache.aries.proxy.FinalModifierException;
  import org.apache.aries.proxy.InvocationListener;
  import org.apache.aries.proxy.UnableToProxyException;
  import org.apache.aries.proxy.impl.SingleInstanceDispatcher;
  import org.apache.aries.proxy.impl.gen.ProxySubclassGenerator;
  import org.apache.aries.proxy.impl.interfaces.InterfaceProxyGenerator;
  import org.apache.aries.proxy.weaving.WovenProxy;
  import org.junit.BeforeClass;
  import org.junit.Test;
  import org.osgi.framework.Bundle;
  import org.osgi.framework.wiring.BundleWiring;
  
  /**
   * This class uses the {@link ProxySubclassGenerator} to test
   */
  @SuppressWarnings("unchecked")
  public class WovenProxyPlusSubclassGeneratorTest extends WovenProxyGeneratorTest
  {
    private static final Class<?> FINAL_METHOD_CLASS = ProxyTestClassFinalMethod.class;
    private static final Class<?> FINAL_CLASS = ProxyTestClassFinal.class;
    private Callable<Object> testCallable = null;
    
    private static Bundle testBundle;
  
    @BeforeClass
    public static void createTestBundle() throws Exception {
      testBundle = mock(Bundle.class);
      BundleWiring wiring = AbstractProxyTest.getWiring(weavingLoader);
      when(testBundle.adapt(BundleWiring.class))
          .thenReturn(wiring);
    }
  
    //Avoid running four weaving tests that don't apply to us
    public void testUnweavableSuperWithNoNoargsAllTheWay() {}
    public void testUnweavableSuperWithFinalMethod() {}
    public void testUnweavableSuperWithDefaultMethodInWrongPackage() {}
    public void testInnerWithNoParentNoArgs() {}
    
    
    @Test(expected=NoSuchFieldException.class)
    public void testGeneratedSVUIDisSynthetic() throws Exception {
      super.testGeneratedSVUIDisSynthetic();
    }
    
    
  //
  //  /**
  //   * Test that the methods found declared on the generated proxy subclass are
  //   * the ones that we expect.
  //   */
  //  @Test
  //  public void testExpectedMethods() throws Exception
  //  {
  //    Class<?> superclass = getTestClass();
  //
  //    do {
  //      Method[] declaredMethods = superclass.getDeclaredMethods();
  //      List<Method> listOfDeclaredMethods = new ArrayList<Method>();
  //      for (Method m : declaredMethods) {
  //    	
  //        if(m.getName().equals("clone") || m.getName().equals("finalize"))
  //        	continue;
  //    	
  //        int i = m.getModifiers();
  //        if (Modifier.isPrivate(i) || Modifier.isFinal(i)) {
  //          // private or final don't get added
  //        } else if (!(Modifier.isPublic(i) || Modifier.isPrivate(i) || Modifier.isProtected(i))) {
  //          // the method is default visibility, check the package
  //          if (m.getDeclaringClass().getPackage().equals(getTestClass().getPackage())) {
  //            // default vis with same package gets added
  //            listOfDeclaredMethods.add(m);
  //          }
  //        } else {
  //          listOfDeclaredMethods.add(m);
  //        }
  //      }
  //
  //      declaredMethods = listOfDeclaredMethods.toArray(new Method[] {});
  //      ProxySubclassMethodHashSet<String> foundMethods = new ProxySubclassMethodHashSet<String>(
  //          declaredMethods.length);
  //      foundMethods.addMethodArray(declaredMethods);
  //      // as we are using a set we shouldn't get duplicates
  //      expectedMethods.addAll(foundMethods);
  //      superclass = superclass.getSuperclass();
  //    } while (superclass != null);
  //
  //    
  //    
  //    Method[] subclassMethods = getProxyClass(getTestClass()).getDeclaredMethods();
  //    List<Method> listOfDeclaredMethods = new ArrayList<Method>();
  //    for (Method m : subclassMethods) {
  //      if(m.getName().startsWith(WovenProxy.class.getName().replace('.', '_')))
  //        continue;
  //      
  //      listOfDeclaredMethods.add(m);
  //    }
  //    subclassMethods = listOfDeclaredMethods.toArray(new Method[] {});
  //    
  //    ProxySubclassMethodHashSet<String> generatedMethods = new ProxySubclassMethodHashSet<String>(
  //    		subclassMethods.length);
  //        generatedMethods.addMethodArray(subclassMethods);
  //        
  //    // check that all the methods we have generated were expected
  //    for (String gen : generatedMethods) {
  //      assertTrue("Unexpected method: " + gen, expectedMethods.contains(gen));
  //    }
  //    // check that all the expected methods were generated
  //    for (String exp : expectedMethods) {
  //      assertTrue("Method was not generated: " + exp, generatedMethods.contains(exp));
  //    }
  //    // check the sets were the same
  //    assertEquals("Sets were not the same", expectedMethods, generatedMethods);
  //
  //  }
  //
    
    
    /**
     * Test a covariant override method
     */
    @Test
    public void testCovariant() throws Exception
    {
      Class<?> proxy = getProxyClass(ProxyTestClassCovariantOverride.class);
      
      Method m = getDeclaredMethod(ProxyTestClassCovariantOverride.class, "getCovariant", new Class[] {});
      Object returned = m.invoke(getProxyInstance(proxy));
      assertTrue("Object was of wrong type: " + returned.getClass().getSimpleName(),
          proxy.getSuperclass().isInstance(returned));
    }
    
    /**
     * Test a method marked final
     */
    @Test
    public void testFinalMethod() throws Exception
    {
      try {
        InterfaceProxyGenerator.getProxyInstance(null, FINAL_METHOD_CLASS, Collections.EMPTY_SET, 
            new Callable<Object>() {
          public Object call() throws Exception {
            return null;
          }} , null).getClass();
      } catch (RuntimeException re) {
        FinalModifierException e = (FinalModifierException) re.getCause();
        assertFalse("Should have found final method not final class", e.isFinalClass());
      }
    }
  
    /**
     * Test a class marked final
     */
    @Test
    public void testFinalClass() throws Exception
    {
      try {
        InterfaceProxyGenerator.getProxyInstance(null, FINAL_CLASS, Collections.EMPTY_SET, 
            new Callable<Object>() {
          public Object call() throws Exception {
            return null;
          }} , null).getClass();
      } catch (FinalModifierException e) {
        assertTrue("Should have found final class", e.isFinalClass());
      }
    }
  
  
    
    @Test
    public void testAddingInterfacesToClass() throws Exception {
      Object proxy = InterfaceProxyGenerator.getProxyInstance(testBundle, super.getProxyClass(getTestClass()), Arrays.asList(Map.class, Iterable.class), new Callable<Object>() {
  
          int calls = 0;
          private Map<String, String> map = new HashMap<String, String>();
          
          {
            map.put("key", "value");
          }
  
          public Object call() throws Exception {
            switch(++calls) {
              case 1 :
                return WovenProxyPlusSubclassGeneratorTest.super.getProxyInstance(weavingLoader.loadClass(getTestClass().getName()));
              case 2 :
                return map;
              default :
                return map.values();
            }
  		}
      	
      }, null);
      
      Method m = weavingLoader.loadClass(ProxyTestClassGeneral.class.getName()).getDeclaredMethod("testReturnInt");
      m.setAccessible(true);
      assertEquals(17, m.invoke(proxy));
      assertEquals("value", ((Map<String, String>)proxy).put("key", "value2"));
      Iterator<?> it = ((Iterable<?>)proxy).iterator();
      assertEquals("value2", it.next());
      assertFalse(it.hasNext());
  	  
    }
    
    
    
    
    @Override
    protected Method getDeclaredMethod(Class<?> testClass, String name,
  		Class<?>... classes) {
  
  	  Class<?> proxy = getProxyClass(testClass);
  	  
  	  while(proxy != null) {
  	    try {
  			return proxy.getDeclaredMethod(name, classes);
  		} catch (Exception e) {
  			proxy = proxy.getSuperclass();
  		}
  	  }
  	  return null;
    }
  
    @Override
    protected Object getProxyInstance(Class<?> proxyClass) {
  	 
      if(proxyClass == ProxyTestClassChildOfAbstract.class) {
      	return super.getProxyInstance(super.getProxyClass(proxyClass));
      }
  	  
      try {
        Constructor<?> con = proxyClass.getDeclaredConstructor(Callable.class, InvocationListener.class);
        con.setAccessible(true);
        return con.newInstance((testCallable == null) ? new SingleInstanceDispatcher(super.getProxyInstance(proxyClass.getSuperclass())) : testCallable, null);
      } catch (Exception e) {
        return null;
      }
    }
  
    @Override
    protected Class<?> getProxyClass(Class<?> clazz) {
      try {
        return InterfaceProxyGenerator.getProxyInstance(testBundle, super.getProxyClass(clazz), Collections.EMPTY_SET, 
            new Callable<Object>() {
          public Object call() throws Exception {
            return null;
          }} , null).getClass();
      } catch (UnableToProxyException e) {
        return null;
      } catch (RuntimeException re) {
        if(re.getCause() instanceof UnableToProxyException)
          return null;
        else
          throw re;
      }
    }
  
    @Override
    protected Object getProxyInstance(Class<?> proxyClass,
        InvocationListener listener) {
      WovenProxy proxy = (WovenProxy) getProxyInstance(proxyClass);
      proxy = proxy.org_apache_aries_proxy_weaving_WovenProxy_createNewProxyInstance(
          new SingleInstanceDispatcher(proxy), listener);
      return proxy;
    }
  }