/** * Assumes that all of the parents of c have been registered already. * * @param c */ @SuppressWarnings("unchecked") private <T> Node registerClass(final Class<T> c) throws ClassHierarchyException { if (c.isArray()) { throw new UnsupportedOperationException("Can't register array types"); } try { return getAlreadyBoundNode(c); } catch (final NameResolutionException e) { // node not bound yet } final Node n = buildPathToNode(c); if (n instanceof ClassNode) { final ClassNode<T> cn = (ClassNode<T>) n; final Class<T> superclass = (Class<T>) c.getSuperclass(); if (superclass != null) { try { ((ClassNode<T>) getAlreadyBoundNode(superclass)).putImpl(cn); } catch (final NameResolutionException e) { throw new IllegalStateException(e); } } for (final Class<?> interf : c.getInterfaces()) { try { ((ClassNode<T>) getAlreadyBoundNode(interf)).putImpl(cn); } catch (final NameResolutionException e) { throw new IllegalStateException(e); } } } return n; }
private Node register(final String s) { final Class<?> c; try { c = classForName(s); } catch (final ClassNotFoundException e1) { return null; } try { final Node n = getAlreadyBoundNode(c); return n; } catch (final NameResolutionException e) { // node not bound yet } // First, walk up the class hierarchy, registering all out parents. This // can't be loopy. if (c.getSuperclass() != null) { register(ReflectionUtilities.getFullName(c.getSuperclass())); } for (final Class<?> i : c.getInterfaces()) { register(ReflectionUtilities.getFullName(i)); } // Now, we'd like to register our enclosing classes. This turns out to be // safe. // Thankfully, Java doesn't allow: // class A implements A.B { class B { } } // It also doesn't allow cycles such as: // class A implements B.BB { interface AA { } } // class B implements A.AA { interface BB { } } // So, even though grafting arbitrary DAGs together can give us cycles, Java // seems // to have our back on this one. final Class<?> enclosing = c.getEnclosingClass(); if (enclosing != null) { register(ReflectionUtilities.getFullName(enclosing)); } // Now register the class. This has to be after the above so we know our // parents (superclasses and enclosing packages) are already registered. final Node n = registerClass(c); // Finally, do things that might introduce cycles that invlove c. // This has to be below registerClass, which ensures that any cycles // this stuff introduces are broken. for (final Class<?> innerClass : c.getDeclaredClasses()) { register(ReflectionUtilities.getFullName(innerClass)); } if (n instanceof ClassNode) { final ClassNode<?> cls = (ClassNode<?>) n; for (final ConstructorDef<?> def : cls.getInjectableConstructors()) { for (final ConstructorArg arg : def.getArgs()) { register(arg.getType()); if (arg.getNamedParameterName() != null) { final NamedParameterNode<?> np = (NamedParameterNode<?>) register(arg.getNamedParameterName()); try { // TODO: When handling sets, need to track target of generic parameter, and check the // type here! if (!np.isSet() && !np.isList() && !ReflectionUtilities.isCoercable( classForName(arg.getType()), classForName(np.getFullArgName()))) { throw new ClassHierarchyException( "Named parameter type mismatch in " + cls.getFullName() + ". Constructor expects a " + arg.getType() + " but " + np.getName() + " is a " + np.getFullArgName()); } } catch (final ClassNotFoundException e) { throw new ClassHierarchyException( "Constructor refers to unknown class " + arg.getType(), e); } } } } } else if (n instanceof NamedParameterNode) { final NamedParameterNode<?> np = (NamedParameterNode<?>) n; register(np.getFullArgName()); } return n; }