/** * Explicitly register a concrete class with a provided interface. The * {@link Creator} will be automatically created for all public * constructors. * * @param intf the interface class to register * @param impl the concrete class which should implement the interface * class * @return whether registration was successful */ public <T extends ICDKObject> boolean register(Class<T> intf, Class<? extends T> impl) { return register(intf, impl, null); }
/** *{@inheritDoc} */ @Override public <T extends ICDKObject> T newInstance(Class<T> clazz, Object... params) throws IllegalArgumentException { return factory.ofClass(clazz, params); }
/** * Creates a constructor key for use in accessing constructors. The key * combines the interface and types in a single instance which we can then * use in a map. * * @param intf the interface to build the key for * @param constructor the constructor value which this key will be linked * to * @return a constructor key which can be used to lookup a constructor */ private static ConstructorKey key(Class<?> intf, Constructor<?> constructor) { return key(intf, constructor.getParameterTypes()); }
/** * Access a constructor for a given constructor key. If the key is not found * the {@link #find(ConstructorKey)} is automatically invoked. * * @param key the constructor key of the interface to find * @param <T> the type of the constructor * @return a constructor for the given key * @throws IllegalArgumentException thrown if a key is provided which cannot * be resolved. */ @SuppressWarnings("unchecked") private <T> Creator<T> get(ConstructorKey key) { Creator<T> creator = (Creator<T>) cache.get(key); if (creator != null) { return creator; } else { synchronized (lock) { // if the creator is still null... try and find one and register it if ((creator = (Creator<T>) cache.get(key)) == null) { creator = find(key); creator = register(key, creator); // avoids invoking find again } } } return creator; }
/** * Registers a class with the factory. The interfaces of the class will be * checked to see if they are CDK interfaces before they are registered. * This method is provided for utility but will register the implementation * with all valid interfaces. If a restricted registration is required then * the safer {@link #register(Class, Class)} can be used. * * @param impl a concrete class * @throws IllegalArgumentException thrown if a non-concrete class is * registered * @see #register(Class, Class) * @see #register(Class, java.lang.reflect.Constructor) * @see #register(org.openscience.cdk.DynamicFactory.ConstructorKey, * org.openscience.cdk.DynamicFactory.Creator) */ public <T extends ICDKObject> boolean register(Class<? extends T> impl) { if (!isConcrete(impl)) throw new IllegalArgumentException("non-concrete implementation provided"); boolean registered = Boolean.FALSE; // only check direct interfaces, // IPolymer -> Polymer not IAtomContainer -> Polymer for (Class<?> c : interfaceProvider.getInterfaces(impl)) { // register this implementation with it's direct interfaces if (isCDKInterface(c)) { @SuppressWarnings("unchecked") Class<T> intf = (Class<T>) c; registered = register(intf, impl) || registered; } } return registered; }
@Test(expected = IllegalArgumentException.class) public void testOfConcrete() throws Exception { DynamicFactory factory = new DynamicFactory(5); assertTrue(factory.implementorsOf(ICDKObject.class).isEmpty()); // register the mock class factory.register(ICDKObject.class, DynamicFactoryTestMock.class); // ofClass -> illegal argument, non-interface ICDKObject instance = factory.ofClass(DynamicFactoryTestMock.class); }
@Test public void testOfClass_Instantiator() throws Exception { DynamicFactory factory = new DynamicFactory(5); factory.register(key(IAtom.class), new DynamicFactory.BasicCreator<IAtom>(null) { @Override public IAtom create(Object[] objects) { return mock(IAtom.class); } }); assertNotNull(factory.ofClass(IAtom.class)); }
@SuppressWarnings("unchecked") // mocking generics @Test public void testRegister_Constructor_Modifier() throws Exception { DynamicFactory factory = new DynamicFactory(5); DynamicFactory.CreationModifier modifier = mock(DynamicFactory.CreationModifier.class); assertTrue(factory.register(ICDKObject.class, DynamicFactoryTestMock.class.getConstructor(String.class), modifier)); assertNotNull(factory.ofClass(ICDKObject.class, "empty")); // verify the modifier was invoked once verify(modifier).modify(anyObject()); }
/** * Check we can't register an interface. */ @Test(expected = IllegalArgumentException.class) public void testRegister_Interface() throws Exception { DynamicFactory factory = new DynamicFactory(0); factory.register(IAtom.class); }
@Test public void testRegister_PrivateConstructor() throws Exception { DynamicFactory factory = new DynamicFactory(5); assertTrue(factory.implementorsOf(ICDKObject.class).isEmpty()); // register the mock class factory.register(ICDKObject.class, DynamicFactoryTestMock.class); Iterator<?> it = factory.suggest(ICDKObject.class); List<Object> list = new ArrayList<Object>(5); while (it.hasNext()) { list.add(it.next()); } assertThat("mocked atom should have two public constructors", list.size(), is(2)); }
@Test public void testImplementationsOf() throws Exception { DynamicFactory factory = new DynamicFactory(5); IElement element = mock(MockedElement.class); IElement atom = mock(MockedAtom.class); assertThat(factory.implementorsOf(IElement.class).size(), is(0)); factory.register(IElement.class, element.getClass()); assertThat(factory.implementorsOf(IElement.class).size(), is(1)); factory.register(IElement.class, atom.getClass()); assertThat(factory.implementorsOf(IElement.class).size(), is(2)); assertThat(factory.implementorsOf(IElement.class).size(), is(2)); }
/** * Kind of already tested in other methods. */ @Test public void testSuggest() throws Exception { DynamicFactory factory = new DynamicFactory(5); IAtom atom = mock(MockedAtom.class); assertFalse(factory.suggest(IAtom.class).hasNext()); factory.register(IAtom.class, atom.getClass()); assertTrue(factory.suggest(IAtom.class).hasNext()); }
CreationModifier<T> modifier) { if (!isConcrete(impl)) throw new IllegalArgumentException("attempt to register non-concrete class"); @SuppressWarnings("unchecked") Constructor<T> typed = (Constructor<T>) untyped; registered = register(intf, typed, modifier) || registered;
/** * Provides a message for use with exceptions when no valid constructor is * found. The message is built using the suggestions from {@link * #suggest(Class)}. * * @param key the constructor key to build the message for * @return a message listing possible constructors */ private String getSuggestionMessage(ConstructorKey key) { StringBuilder sb = new StringBuilder(200); sb.append("No constructor found for '").append(key); sb.append("' candidates are: "); Iterator<ConstructorKey> iterator = suggest(key.intf()); while (iterator.hasNext()) { sb.append(iterator.next()); if (iterator.hasNext()) { sb.append(", "); } } return sb.toString(); }
/** * Converts the provided array of classes to a version with 'boxed' * primitives. This will convert {int, int, int} to {Integer, Integer, * Integer}. * * @param classes converted classes * @return converted types */ private static Class<?>[] convert(Class<?>[] classes) { Class<?>[] types = new Class<?>[classes.length]; for (int i = 0; i < types.length; i++) { types[i] = convert(classes[i]); } return types; }
/** * Ensure a negative size throws an exception. */ @Test(expected = IllegalArgumentException.class) public void testConstructor() { new DynamicFactory(-1); }
/** * Check we get an exception when we try to build from a non-interface. * @throws Exception */ @Test(expected = IllegalArgumentException.class) public void testOfConcrete_Params() throws Exception { IAtom mock = mock(MockedAtom.class); DynamicFactory factory = new DynamicFactory(5); assertTrue(factory.implementorsOf(IAtom.class).isEmpty()); // register the mock class factory.register(IAtom.class, mock.getClass()); // ofClass -> illegal argument, non-interface IAtom instance = factory.ofClass(mock.getClass(), this); }
/** * Unit test ensures the factory wraps up varargs into the correct * representation. This is needed as passing a uniform array will be * converted to actual varargs - this test checks both cases. */ @Test public void testOfClass_Wrapping() { DynamicFactory factory = new DynamicFactory(5); // register ICDKObject with an mock instantiator factory.register(key(ICDKObject.class, IAtom[].class), new DynamicFactory.BasicCreator<IAtom>(null) { @Override public IAtom create(Object[] objects) { return mock(IAtom.class); } }); // uniform parameter array assertNotNull(factory.ofClass(ICDKObject.class, new IAtom[]{mock(IAtom.class), mock(IAtom.class), mock(IAtom.class)})); // is equivalent to just using varargs... assertNotNull(factory.ofClass(ICDKObject.class, mock(IAtom.class), mock(IAtom.class), mock(IAtom.class))); // unless we double wrap it (which resolves to the same instantiator) assertNotNull(factory.ofClass(ICDKObject.class, new Object[]{new IAtom[]{mock(IAtom.class), mock(IAtom.class), mock(IAtom.class)}})); }
/** * Test mocks {@link org.openscience.cdk.DynamicFactory.CreationModifier} * and ensures the modify is called once when a registered implementation is * created. */ @Test @SuppressWarnings("unchecked") public void testRegister_WithModifier() { DynamicFactory factory = new DynamicFactory(5); IAtom mock = mock(IAtom.class); DynamicFactory.CreationModifier modifier = mock(DynamicFactory.CreationModifier.class); // ignore compiler warnings here... we're mocking so don't have a set type factory.register(IAtom.class, mock.getClass(), modifier); assertNotNull(factory.ofClass(IAtom.class)); // verify the modify method was called once verify(modifier, times(1)).modify(anyObject()); }