@SuppressWarnings("unchecked") @Override public <T> JavaConfigurationBuilder bindSetEntry( Class<? extends Name<Set<T>>> iface, Class<? extends T> impl) throws BindException { final Node n = getNode(iface); final Node m = getNode(impl); if (!(n instanceof NamedParameterNode)) { throw new BindException( "BindSetEntry got an interface that resolved to " + n + "; expected a NamedParameter"); } final Type setType = ReflectionUtilities.getInterfaceTarget(Name.class, iface); if (!ReflectionUtilities.getRawClass(setType).equals(Set.class)) { throw new BindException( "BindSetEntry got a NamedParameter that takes a " + setType + "; expected Set<...>"); } final Type valType = ReflectionUtilities.getInterfaceTarget(Set.class, setType); if (!ReflectionUtilities.getRawClass(valType).isAssignableFrom(impl)) { throw new BindException( "BindSetEntry got implementation " + impl + " that is incompatible with expected type " + valType); } super.bindSetEntry((NamedParameterNode<Set<T>>) n, m); return this; }
private <T, U> Node buildPathToNode(final Class<U> clazz) throws ClassHierarchyException { final String[] path = clazz.getName().split("\\$"); Node root = namespace; for (int i = 0; i < path.length - 1; i++) { root = root.get(path[i]); } if (root == null) { throw new NullPointerException(); } final Node parent = root; final Type argType = ReflectionUtilities.getNamedParameterTargetOrNull(clazz); if (argType == null) { return JavaNodeFactory.createClassNode(parent, clazz); } else { // checked inside of NamedParameterNode, using reflection. @SuppressWarnings("unchecked") final NamedParameterNode<T> np = JavaNodeFactory.createNamedParameterNode( parent, (Class<? extends Name<T>>) clazz, argType); if (parameterParser.canParse(ReflectionUtilities.getFullName(argType)) && clazz.getAnnotation(NamedParameter.class).default_class() != Void.class) { throw new ClassHierarchyException( "Named parameter " + ReflectionUtilities.getFullName(clazz) + " defines default implementation for parsable type " + ReflectionUtilities.getFullName(argType)); } final String shortName = np.getShortName(); if (shortName != null) { final NamedParameterNode<?> oldNode = shortNames.get(shortName); if (oldNode != null) { if (oldNode.getFullName().equals(np.getFullName())) { throw new IllegalStateException( "Tried to double bind " + oldNode.getFullName() + " to short name " + shortName); } throw new ClassHierarchyException( "Named parameters " + oldNode.getFullName() + " and " + np.getFullName() + " have the same short name: " + shortName); } shortNames.put(shortName, np); } return np; } }
@SuppressWarnings({"unchecked", "rawtypes"}) public Configuration build() throws BindException { ConfigurationModule c = deepCopy(); if (!c.reqSet.containsAll(c.builder.reqDecl)) { Set<Field> missingSet = new MonotonicHashSet<>(); for (Field f : c.builder.reqDecl) { if (!c.reqSet.contains(f)) { missingSet.add(f); } } throw new BindException( "Attempt to build configuration before setting required option(s): " + builder.toString(missingSet)); } for (Class<?> clazz : c.builder.freeImpls.keySet()) { Impl<?> i = c.builder.freeImpls.get(clazz); if (c.setImpls.containsKey(i)) { c.builder.b.bind(clazz, c.setImpls.get(i)); } else if (c.setLateImpls.containsKey(i)) { c.builder.b.bind(ReflectionUtilities.getFullName(clazz), c.setLateImpls.get(i)); } else if (c.setImplSets.containsKey(i) || c.setLateImplSets.containsKey(i)) { for (Class<?> clz : c.setImplSets.getValuesForKey(i)) { c.builder.b.bindSetEntry((Class) clazz, (Class) clz); } for (String s : c.setLateImplSets.getValuesForKey(i)) { c.builder.b.bindSetEntry((Class) clazz, s); } } else if (c.setImplLists.containsKey(i)) { c.builder.b.bindList((Class) clazz, c.setImplLists.get(i)); } } for (Class<? extends Name<?>> clazz : c.builder.freeParams.keySet()) { Param<?> p = c.builder.freeParams.get(clazz); String s = c.setParams.get(p); boolean foundOne = false; if (s != null) { c.builder.b.bindNamedParameter(clazz, s); foundOne = true; } // Find the bound list for the NamedParameter List list = c.setParamLists.get(p); if (list != null) { c.builder.b.bindList((Class) clazz, list); foundOne = true; } for (String paramStr : c.setParamSets.getValuesForKey(p)) { c.builder.b.bindSetEntry((Class) clazz, paramStr); foundOne = true; } if (!foundOne) { if (!(p instanceof OptionalParameter)) { throw new IllegalStateException(); } } } return c.builder.b.build(); }
public List<Entry<String, String>> toStringPairs() { List<Entry<String, String>> ret = new ArrayList<>(); class MyEntry implements Entry<String, String> { final String k; final String v; public MyEntry(String k, String v) { this.k = k; this.v = v; } @Override public String getKey() { return k; } @Override public String getValue() { return v; } @Override public String setValue(String value) { throw new UnsupportedOperationException(); } } for (Class<?> c : this.builder.freeParams.keySet()) { ret.add( new MyEntry( ReflectionUtilities.getFullName(c), this.builder.map.get(this.builder.freeParams.get(c)).getName())); } for (Class<?> c : this.builder.freeImpls.keySet()) { ret.add( new MyEntry( ReflectionUtilities.getFullName(c), this.builder.map.get(this.builder.freeImpls.get(c)).getName())); } for (String s : ConfigurationFile.toConfigurationStringList(builder.b.build())) { String[] tok = s.split("=", 2); ret.add(new MyEntry(tok[0], tok[1])); } return ret; }
@SuppressWarnings("unchecked") @Override public <T> JavaConfigurationBuilder bindSetEntry( Class<? extends Name<Set<T>>> iface, String value) throws BindException { final Node n = getNode(iface); if (!(n instanceof NamedParameterNode)) { throw new BindException( "BindSetEntry got an interface that resolved to " + n + "; expected a NamedParameter"); } final Type setType = ReflectionUtilities.getInterfaceTarget(Name.class, iface); if (!ReflectionUtilities.getRawClass(setType).equals(Set.class)) { throw new BindException( "BindSetEntry got a NamedParameter that takes a " + setType + "; expected Set<...>"); } // Type valType = ReflectionUtilities.getInterfaceTarget(Set.class, setType); super.bindSetEntry((NamedParameterNode<Set<T>>) n, value); return this; }
@Override public Node getNode(final Class<?> clazz) { try { return getNode(ReflectionUtilities.getFullName(clazz)); } catch (final NameResolutionException e) { throw new ClassHierarchyException( "JavaClassHierarchy could not resolve " + clazz + " which is definitely avalable at runtime", e); } }
/** * Binding list method for JavaConfigurationBuilder. It checks the type of a given named * parameter, and whether all the list's elements can be applied to the named parameter. The * elements' type should be either java Class or String. * * <p>It does not check whether the list's String values can be parsed to T, like bindSetEntry. * * @param iface target named parameter to be instantiated * @param implList implementation list used to instantiate the named parameter * @param <T> type of the list * @return bound JavaConfigurationBuilder object * @throws BindException */ @SuppressWarnings("unchecked") @Override public <T> JavaConfigurationBuilder bindList(Class<? extends Name<List<T>>> iface, List implList) throws BindException { final Node n = getNode(iface); List<Object> result = new ArrayList<>(); if (!(n instanceof NamedParameterNode)) { throw new BindException( "BindList got an interface that resolved to " + n + "; expected a NamedParameter"); } final Type listType = ReflectionUtilities.getInterfaceTarget(Name.class, iface); if (!ReflectionUtilities.getRawClass(listType).equals(List.class)) { throw new BindException( "BindList got a NamedParameter that takes a " + listType + "; expected List<...>"); } if (!implList.isEmpty()) { final Type valType = ReflectionUtilities.getInterfaceTarget(List.class, listType); for (Object item : implList) { if (item instanceof Class) { if (!ReflectionUtilities.getRawClass(valType).isAssignableFrom((Class) item)) { throw new BindException( "BindList got a list element which is not assignable to the given Type; " + "expected: " + valType); } result.add(getNode((Class) item)); } else if (item instanceof String) { result.add(item); } else { throw new BindException( "BindList got an list element with unsupported type; expected Class or String " + "object"); } } } super.bindList((NamedParameterNode<List<T>>) n, result); return this; }
private <T> void processSet(Object impl) { Field f = builder.map.get(impl); if (f == null) { /* throw */ throw new ClassHierarchyException( "Unknown Impl/Param when setting " + ReflectionUtilities.getSimpleName(impl.getClass()) + ". Did you pass in a field from some other module?"); } if (!reqSet.contains(f)) { reqSet.add(f); } }
public Set<NamedParameterNode<?>> getBoundNamedParameters() { Configuration c = this.builder.b.build(); Set<NamedParameterNode<?>> nps = new MonotonicSet<>(); nps.addAll(c.getNamedParameters()); for (Class<?> np : this.builder.freeParams.keySet()) { try { nps.add( (NamedParameterNode<?>) builder.b.getClassHierarchy().getNode(ReflectionUtilities.getFullName(np))); } catch (NameResolutionException e) { throw new IllegalStateException(e); } } return nps; }
public final <T> ConfigurationModule set(Param<T> opt, Class<? extends T> val) { return set(opt, ReflectionUtilities.getFullName(val)); }
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; }
private Node getAlreadyBoundNode(final Class<?> clazz) throws NameResolutionException { return getAlreadyBoundNode(ReflectionUtilities.getFullName(clazz)); }
/** Helper method that converts a String to a Class using this ClassHierarchy's classloader. */ @Override public Class<?> classForName(final String name) throws ClassNotFoundException { return ReflectionUtilities.classForName(name, loader); }