You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@reef.apache.org by we...@apache.org on 2015/01/23 00:46:47 UTC

[14/51] [partial] incubator-reef git commit: [REEF-93] Move java sources to lang/java

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/formats/package-info.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/formats/package-info.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/formats/package-info.java
new file mode 100644
index 0000000..0513a03
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/formats/package-info.java
@@ -0,0 +1,29 @@
+/**
+ * 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.
+ */
+/**
+ * Tang format classes encode and decode information that Tang gathers at
+ * runtime.  Such information comes in three forms: ClassHierarchy data
+ * that is derived directly from compiled application code, Configuration
+ * data that has been typechecked, and is derived from various user inputs,
+ * such as configuration files and command line arguments, and finally,
+ * InjectionPlans which encode the constructor invocations that Tang would
+ * make when instantiating a particular class.
+ */
+package org.apache.reef.tang.formats;
+

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationBuilderImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationBuilderImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationBuilderImpl.java
new file mode 100644
index 0000000..a9ead69
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationBuilderImpl.java
@@ -0,0 +1,378 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.*;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.exceptions.NameResolutionException;
+import org.apache.reef.tang.exceptions.ParseException;
+import org.apache.reef.tang.implementation.java.ClassHierarchyImpl;
+import org.apache.reef.tang.types.*;
+import org.apache.reef.tang.util.MonotonicMultiMap;
+import org.apache.reef.tang.util.TracingMonotonicMap;
+import org.apache.reef.tang.util.TracingMonotonicTreeMap;
+
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class ConfigurationBuilderImpl implements ConfigurationBuilder {
+  public final static String IMPORT = "import";
+  public final static String INIT = "<init>";
+  final TracingMonotonicMap<ClassNode<?>, ClassNode<?>> boundImpls = new TracingMonotonicTreeMap<>();
+  final TracingMonotonicMap<ClassNode<?>, ClassNode<? extends ExternalConstructor<?>>> boundConstructors = new TracingMonotonicTreeMap<>();
+  final Map<NamedParameterNode<?>, String> namedParameters = new TracingMonotonicTreeMap<>();
+  final Map<ClassNode<?>, ConstructorDef<?>> legacyConstructors = new TracingMonotonicTreeMap<>();
+  final MonotonicMultiMap<NamedParameterNode<Set<?>>, Object> boundSetEntries = new MonotonicMultiMap<>();
+  final TracingMonotonicMap<NamedParameterNode<List<?>>, List<Object>> boundLists = new TracingMonotonicTreeMap<>();
+  // TODO: None of these should be public! - Move to configurationBuilder. Have
+  // that wrap itself
+  // in a sane Configuration interface...
+  // TODO: Should be final again!
+  public ClassHierarchy namespace;
+
+  protected ConfigurationBuilderImpl() {
+    this.namespace = Tang.Factory.getTang().getDefaultClassHierarchy();
+  }
+
+  protected ConfigurationBuilderImpl(ClassHierarchy namespace) {
+    this.namespace = namespace;
+  }
+
+  protected ConfigurationBuilderImpl(URL[] jars, Configuration[] confs, Class<? extends ExternalConstructor<?>>[] parsers)
+      throws BindException {
+    this.namespace = Tang.Factory.getTang().getDefaultClassHierarchy(jars, parsers);
+    for (Configuration tc : confs) {
+      addConfiguration(((ConfigurationImpl) tc));
+    }
+  }
+
+  protected ConfigurationBuilderImpl(ConfigurationBuilderImpl t) {
+    this.namespace = t.getClassHierarchy();
+    try {
+      addConfiguration(t.getClassHierarchy(), t);
+    } catch (BindException e) {
+      throw new IllegalStateException("Could not copy builder", e);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  protected ConfigurationBuilderImpl(URL... jars) throws BindException {
+    this(jars, new Configuration[0], new Class[0]);
+  }
+
+  @SuppressWarnings("unchecked")
+  protected ConfigurationBuilderImpl(Configuration... confs) throws BindException {
+    this(new URL[0], confs, new Class[0]);
+  }
+
+  @Override
+  public void addConfiguration(Configuration conf) throws BindException {
+    // XXX remove cast!
+    addConfiguration(conf.getClassHierarchy(), ((ConfigurationImpl) conf).builder);
+  }
+
+  @SuppressWarnings("unchecked")
+  private <T> void addConfiguration(ClassHierarchy ns, ConfigurationBuilderImpl builder)
+      throws BindException {
+    namespace = namespace.merge(ns);
+    if ((namespace instanceof ClassHierarchyImpl || builder.namespace instanceof ClassHierarchyImpl)) {
+      if ((namespace instanceof ClassHierarchyImpl && builder.namespace instanceof ClassHierarchyImpl)) {
+        ((ClassHierarchyImpl) namespace).parameterParser
+            .mergeIn(((ClassHierarchyImpl) builder.namespace).parameterParser);
+      } else {
+        throw new IllegalArgumentException("Attempt to merge Java and non-Java class hierarchy!  Not supported.");
+      }
+    }
+
+    for (ClassNode<?> cn : builder.boundImpls.keySet()) {
+      bind(cn.getFullName(), builder.boundImpls.get(cn).getFullName());
+    }
+    for (ClassNode<?> cn : builder.boundConstructors.keySet()) {
+      bind(cn.getFullName(), builder.boundConstructors.get(cn).getFullName());
+    }
+    // The namedParameters set contains the strings that can be used to
+    // instantiate new
+    // named parameter instances. Create new ones where we can.
+    for (NamedParameterNode<?> np : builder.namedParameters.keySet()) {
+      bind(np.getFullName(), builder.namedParameters.get(np));
+    }
+    for (ClassNode<?> cn : builder.legacyConstructors.keySet()) {
+      registerLegacyConstructor(cn, builder.legacyConstructors.get(cn)
+          .getArgs());
+    }
+    for (Entry<NamedParameterNode<Set<?>>, Object> e : builder.boundSetEntries) {
+      String name = ((NamedParameterNode<Set<T>>) (NamedParameterNode<?>) e.getKey()).getFullName();
+      if (e.getValue() instanceof Node) {
+        bindSetEntry(name, (Node) e.getValue());
+      } else if (e.getValue() instanceof String) {
+        bindSetEntry(name, (String) e.getValue());
+      } else {
+        throw new IllegalStateException();
+      }
+    }
+    // The boundLists set contains bound lists with their target NamedParameters
+    for (NamedParameterNode<List<?>> np : builder.boundLists.keySet()) {
+      bindList(np.getFullName(), builder.boundLists.get(np));
+    }
+  }
+
+  @Override
+  public ClassHierarchy getClassHierarchy() {
+    return namespace;
+  }
+
+  @Override
+  public void registerLegacyConstructor(ClassNode<?> c,
+                                        final ConstructorArg... args) throws BindException {
+    String cn[] = new String[args.length];
+    for (int i = 0; i < args.length; i++) {
+      cn[i] = args[i].getType();
+    }
+    registerLegacyConstructor(c.getFullName(), cn);
+  }
+
+  @Override
+  public void registerLegacyConstructor(String s, final String... args)
+      throws BindException {
+    ClassNode<?> cn = (ClassNode<?>) namespace.getNode(s);
+    ClassNode<?>[] cnArgs = new ClassNode[args.length];
+    for (int i = 0; i < args.length; i++) {
+      cnArgs[i] = (ClassNode<?>) namespace.getNode(args[i]);
+    }
+    registerLegacyConstructor(cn, cnArgs);
+  }
+
+  @Override
+  public void registerLegacyConstructor(ClassNode<?> cn,
+                                        final ClassNode<?>... args) throws BindException {
+    legacyConstructors.put(cn, cn.getConstructorDef(args));
+  }
+
+  @Override
+  public <T> void bind(String key, String value) throws BindException {
+    Node n = namespace.getNode(key);
+    if (n instanceof NamedParameterNode) {
+      bindParameter((NamedParameterNode<?>) n, value);
+    } else if (n instanceof ClassNode) {
+      Node m = namespace.getNode(value);
+      bind((ClassNode<?>) n, (ClassNode<?>) m);
+    } else {
+      throw new IllegalStateException("getNode() returned " + n + " which is neither a ClassNode nor a NamedParameterNode");
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public void bind(Node key, Node value) throws BindException {
+    if (key instanceof NamedParameterNode) {
+      bindParameter((NamedParameterNode<?>) key, value.getFullName());
+    } else if (key instanceof ClassNode) {
+      ClassNode<?> k = (ClassNode<?>) key;
+      if (value instanceof ClassNode) {
+        ClassNode<?> val = (ClassNode<?>) value;
+        if (val.isExternalConstructor() && !k.isExternalConstructor()) {
+          bindConstructor(k, (ClassNode) val);
+        } else {
+          bindImplementation(k, (ClassNode) val);
+        }
+      }
+    }
+  }
+
+  public <T> void bindImplementation(ClassNode<T> n, ClassNode<? extends T> m)
+      throws BindException {
+    if (namespace.isImplementation(n, m)) {
+      boundImpls.put(n, m);
+    } else {
+      throw new IllegalArgumentException("Class" + m + " does not extend " + n);
+    }
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public <T> void bindParameter(NamedParameterNode<T> name, String value)
+      throws BindException {
+    /* Parse and discard value; this is just for type checking */
+    if (namespace instanceof JavaClassHierarchy) {
+      ((JavaClassHierarchy) namespace).parse(name, value);
+    }
+    if (name.isSet()) {
+      bindSetEntry((NamedParameterNode) name, value);
+    } else {
+      namedParameters.put(name, value);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public void bindSetEntry(String iface, String impl)
+      throws BindException {
+    boundSetEntries.put((NamedParameterNode<Set<?>>) namespace.getNode(iface), impl);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public void bindSetEntry(String iface, Node impl)
+      throws BindException {
+    boundSetEntries.put((NamedParameterNode<Set<?>>) namespace.getNode(iface), impl);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T> void bindSetEntry(NamedParameterNode<Set<T>> iface, String impl)
+      throws BindException {
+    if (namespace instanceof ClassHierarchyImpl) {
+      JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace;
+      try {
+        javanamespace.parse(iface, impl);
+      } catch (ParseException e) {
+        throw new IllegalStateException("Could not parse " + impl + " which was passed to " + iface);
+      }
+    }
+    boundSetEntries.put((NamedParameterNode<Set<?>>) (NamedParameterNode<?>) iface, impl);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T> void bindSetEntry(NamedParameterNode<Set<T>> iface, Node impl)
+      throws BindException {
+    boundSetEntries.put((NamedParameterNode<Set<?>>) (NamedParameterNode<?>) iface, impl);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T> void bindList(NamedParameterNode<List<T>> iface, List implList) {
+    // Check parsability of list items
+    for (Object item : implList) {
+      if (item instanceof String) {
+        JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace;
+        try {
+          // Just for parsability checking.
+          javanamespace.parse(iface, (String) item);
+        } catch (ParseException e) {
+          throw new IllegalStateException("Could not parse " + item + " which was passed to " + iface);
+        }
+      }
+    }
+    boundLists.put((NamedParameterNode<List<?>>) (NamedParameterNode<?>) iface, implList);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public void bindList(String iface, List implList) {
+    NamedParameterNode<List<?>> ifaceNode = (NamedParameterNode<List<?>>) namespace.getNode(iface);
+    // Check parsability of list items
+    for (Object item : implList) {
+      if (item instanceof String) {
+        JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace;
+        try {
+          // Just for parsability checking.
+          javanamespace.parse(ifaceNode, (String) item);
+        } catch (ParseException e) {
+          throw new IllegalStateException("Could not parse " + item + " which was passed to " + iface);
+        }
+      }
+    }
+    boundLists.put(ifaceNode, implList);
+  }
+
+  @Override
+  public <T> void bindConstructor(ClassNode<T> k,
+                                  ClassNode<? extends ExternalConstructor<? extends T>> v) {
+    boundConstructors.put(k, v);
+  }
+
+  @Override
+  public ConfigurationImpl build() {
+    return new ConfigurationImpl(new ConfigurationBuilderImpl(this));
+  }
+
+  @Override
+  public String classPrettyDefaultString(String longName) throws NameResolutionException {
+    final NamedParameterNode<?> param = (NamedParameterNode<?>) namespace
+        .getNode(longName);
+    return param.getSimpleArgName() + "=" + join(",", param.getDefaultInstanceAsStrings());
+  }
+
+  private String join(String sep, String[] s) {
+    if (s.length == 0) {
+      return null;
+    } else {
+      StringBuilder sb = new StringBuilder(s[0]);
+      for (int i = 1; i < s.length; i++) {
+        sb.append(sep);
+        sb.append(s[i]);
+      }
+      return sb.toString();
+    }
+  }
+
+  @Override
+  public String classPrettyDescriptionString(String fullName)
+      throws NameResolutionException {
+    final NamedParameterNode<?> param = (NamedParameterNode<?>) namespace
+        .getNode(fullName);
+    return param.getDocumentation() + "\n" + param.getFullName();
+  }
+
+  @Override
+  public boolean equals(final Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    ConfigurationBuilderImpl that = (ConfigurationBuilderImpl) o;
+
+    if (boundConstructors != null ? !boundConstructors.equals(that.boundConstructors) : that.boundConstructors != null) {
+      return false;
+    }
+    if (boundImpls != null ? !boundImpls.equals(that.boundImpls) : that.boundImpls != null) {
+      return false;
+    }
+    if (boundSetEntries != null ? !boundSetEntries.equals(that.boundSetEntries) : that.boundSetEntries != null) {
+      return false;
+    }
+    if (boundLists != null ? !boundLists.equals(that.boundLists) : that.boundLists != null) {
+      return false;
+    }
+    if (legacyConstructors != null ? !legacyConstructors.equals(that.legacyConstructors) : that.legacyConstructors != null) {
+      return false;
+    }
+    if (namedParameters != null ? !namedParameters.equals(that.namedParameters) : that.namedParameters != null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = boundImpls != null ? boundImpls.hashCode() : 0;
+    result = 31 * result + (boundConstructors != null ? boundConstructors.hashCode() : 0);
+    result = 31 * result + (namedParameters != null ? namedParameters.hashCode() : 0);
+    result = 31 * result + (legacyConstructors != null ? legacyConstructors.hashCode() : 0);
+    result = 31 * result + (boundSetEntries != null ? boundSetEntries.hashCode() : 0);
+    result = 31 * result + (boundLists != null ? boundLists.hashCode() : 0);
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationImpl.java
new file mode 100644
index 0000000..076435f
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationImpl.java
@@ -0,0 +1,143 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.ClassHierarchy;
+import org.apache.reef.tang.Configuration;
+import org.apache.reef.tang.ConfigurationBuilder;
+import org.apache.reef.tang.ExternalConstructor;
+import org.apache.reef.tang.types.ClassNode;
+import org.apache.reef.tang.types.ConstructorDef;
+import org.apache.reef.tang.types.NamedParameterNode;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class ConfigurationImpl implements Configuration {
+  final ConfigurationBuilderImpl builder;
+
+  protected ConfigurationImpl(final ConfigurationBuilderImpl builder) {
+    this.builder = builder;
+  }
+
+  @Override
+  public String getNamedParameter(final NamedParameterNode<?> np) {
+    return builder.namedParameters.get(np);
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public <T> ClassNode<ExternalConstructor<T>> getBoundConstructor(
+      final ClassNode<T> cn) {
+    return (ClassNode<ExternalConstructor<T>>) builder.boundConstructors.get(cn);
+  }
+
+  @Override
+  public Set<ClassNode<?>> getBoundImplementations() {
+    return builder.boundImpls.keySet();
+  }
+
+  @Override
+  public Set<ClassNode<?>> getBoundConstructors() {
+    return builder.boundConstructors.keySet();
+  }
+
+  @Override
+  public Set<NamedParameterNode<?>> getNamedParameters() {
+    return builder.namedParameters.keySet();
+  }
+
+  @Override
+  public Set<ClassNode<?>> getLegacyConstructors() {
+    return builder.legacyConstructors.keySet();
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public <T> ClassNode<T> getBoundImplementation(final ClassNode<T> cn) {
+    return (ClassNode<T>) builder.boundImpls.get(cn);
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public <T> ConstructorDef<T> getLegacyConstructor(final ClassNode<T> cn) {
+    return (ConstructorDef<T>) builder.legacyConstructors.get(cn);
+  }
+
+  @Override
+  public ConfigurationBuilder newBuilder() {
+    return builder.build().builder;
+  }
+
+  @Override
+  public ClassHierarchy getClassHierarchy() {
+    return builder.namespace;
+  }
+
+  @Override
+  public Set<Object> getBoundSet(NamedParameterNode<Set<?>> np) {
+    return this.builder.boundSetEntries.getValuesForKey(np);
+  }
+
+  @Override
+  public List<Object> getBoundList(NamedParameterNode<List<?>> np) {
+    return this.builder.boundLists.get(np);
+  }
+
+  @Override
+  public Iterable<Entry<NamedParameterNode<Set<?>>, Object>> getBoundSets() {
+    return new Iterable<Entry<NamedParameterNode<Set<?>>, Object>>() {
+
+      @Override
+      public Iterator<Entry<NamedParameterNode<Set<?>>, Object>> iterator() {
+        return builder.boundSetEntries.iterator();
+      }
+    };
+  }
+
+  @Override
+  public Set<NamedParameterNode<List<?>>> getBoundLists() {
+    return builder.boundLists.keySet();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    final ConfigurationImpl that = (ConfigurationImpl) o;
+
+    if (builder != null ? !builder.equals(that.builder) : that.builder != null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return builder != null ? builder.hashCode() : 0;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Constructor.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Constructor.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Constructor.java
new file mode 100644
index 0000000..b41682d
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Constructor.java
@@ -0,0 +1,186 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.types.ClassNode;
+import org.apache.reef.tang.types.ConstructorDef;
+
+import java.util.*;
+
+final public class Constructor<T> extends InjectionPlan<T> {
+
+  final ConstructorDef<T> constructor;
+  final InjectionPlan<?>[] args;
+  final int numAlternatives;
+  final boolean isAmbiguous;
+  final boolean isInjectable;
+
+  public Constructor(final ClassNode<T> classNode,
+                     final ConstructorDef<T> constructor, final InjectionPlan<?>[] args) {
+    super(classNode);
+    this.constructor = constructor;
+    this.args = args;
+    int curAlternatives = 1;
+    boolean curAmbiguous = false;
+    boolean curInjectable = true;
+    for (final InjectionPlan<?> plan : args) {
+      curAlternatives *= plan.getNumAlternatives();
+      curAmbiguous |= plan.isAmbiguous();
+      curInjectable &= plan.isInjectable();
+    }
+    this.numAlternatives = curAlternatives;
+    this.isAmbiguous = curAmbiguous;
+    this.isInjectable = curInjectable;
+  }
+
+  public InjectionPlan<?>[] getArgs() {
+    return args;
+  }
+
+  /**
+   * Get child elements of the injection plan tree.
+   * This method is inherited from the Traversable interface.
+   * TODO: use ArrayList internally (and maybe for input, too).
+   *
+   * @return A list of injection plans for the constructor's arguments.
+   */
+  @Override
+  public Collection<InjectionPlan<?>> getChildren() {
+    return Collections.unmodifiableList(Arrays.asList(this.args));
+  }
+
+  public ConstructorDef<T> getConstructorDef() {
+    return constructor;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public ClassNode<T> getNode() {
+    return (ClassNode<T>) node;
+  }
+
+  @Override
+  public int getNumAlternatives() {
+    return numAlternatives;
+  }
+
+  @Override
+  public boolean isAmbiguous() {
+    return isAmbiguous;
+  }
+
+  @Override
+  public boolean isInjectable() {
+    return isInjectable;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder("new " + getNode().getName() + '(');
+    if (args.length > 0) {
+      sb.append(args[0]);
+      for (int i = 1; i < args.length; i++) {
+        sb.append(", " + args[i]);
+      }
+    }
+    sb.append(')');
+    return sb.toString();
+  }
+
+  private String shallowArgString(final InjectionPlan<?> arg) {
+    if (arg instanceof Constructor || arg instanceof Subplan) {
+      return arg.getClass().getName() + ": " + arg.getNode().getName();
+    } else {
+      return arg.toShallowString();
+    }
+  }
+
+  @Override
+  public String toShallowString() {
+    final StringBuilder sb = new StringBuilder("new " + getNode().getName() + '(');
+    if (args.length > 0) {
+      sb.append(shallowArgString(args[0]));
+      for (int i = 1; i < args.length; i++) {
+        sb.append(", " + shallowArgString(args[i]));
+      }
+    }
+    sb.append(')');
+    return sb.toString();
+  }
+
+  /**
+   * @return A string describing ambiguous constructor arguments.
+   * @throws IllegalArgumentException if constructor is not ambiguous.
+   */
+  @Override
+  protected String toAmbiguousInjectString() {
+
+    if (!isAmbiguous) {
+      throw new IllegalArgumentException(getNode().getFullName() + " is NOT ambiguous.");
+    }
+
+    final StringBuilder sb = new StringBuilder(
+        getNode().getFullName() + " has ambiguous arguments: [ ");
+
+    for (final InjectionPlan<?> plan : args) {
+      if (plan.isAmbiguous()) {
+        sb.append(plan.toAmbiguousInjectString());
+      }
+    }
+
+    sb.append(']');
+    return sb.toString();
+  }
+
+  @Override
+  protected String toInfeasibleInjectString() {
+
+    final List<InjectionPlan<?>> leaves = new ArrayList<>();
+
+    for (final InjectionPlan<?> ip : args) {
+      if (!ip.isFeasible()) {
+        if (ip.isInfeasibleLeaf()) {
+          leaves.add(ip);
+        } else {
+          return ip.toInfeasibleInjectString();
+        }
+      }
+    }
+
+    if (leaves.isEmpty()) {
+      throw new IllegalArgumentException(getNode().getFullName() + " has NO infeasible leaves.");
+    }
+
+    if (leaves.size() == 1) {
+      return getNode().getFullName() + " missing argument " + leaves.get(0).getNode().getFullName();
+    } else {
+      final StringBuffer sb = new StringBuffer(getNode().getFullName() + " missing arguments: [\n\t");
+      for (final InjectionPlan<?> leaf : leaves) {
+        sb.append(leaf.getNode().getFullName() + "\n\t");
+      }
+      sb.append(']');
+      return sb.toString();
+    }
+  }
+
+  @Override
+  protected boolean isInfeasibleLeaf() {
+    return false;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionFuturePlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionFuturePlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionFuturePlan.java
new file mode 100644
index 0000000..b57eeec
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionFuturePlan.java
@@ -0,0 +1,64 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+
+public class InjectionFuturePlan<T> extends InjectionPlan<T> {
+
+  public InjectionFuturePlan(Node name) {
+    super(name);
+  }
+
+  @Override
+  public int getNumAlternatives() {
+    return 1;
+  }
+
+  @Override
+  public boolean isAmbiguous() {
+    return false;
+  }
+
+  @Override
+  public boolean isInjectable() {
+    return true;
+  }
+
+  @Override
+  protected String toAmbiguousInjectString() {
+    throw new UnsupportedOperationException("InjectionFuturePlan cannot be ambiguous!");
+  }
+
+  @Override
+  protected String toInfeasibleInjectString() {
+    throw new UnsupportedOperationException("InjectionFuturePlan is always feasible!");
+  }
+
+  @Override
+  protected boolean isInfeasibleLeaf() {
+    return false;
+  }
+
+  @Override
+  public String toShallowString() {
+    return "InjectionFuture<" + getNode().getFullName() + ">";
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionPlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionPlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionPlan.java
new file mode 100644
index 0000000..ee113b1
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionPlan.java
@@ -0,0 +1,161 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+import org.apache.reef.tang.types.Traversable;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public abstract class InjectionPlan<T> implements Traversable<InjectionPlan<?>> {
+
+  protected final Node node;
+
+  public InjectionPlan(final Node node) {
+    this.node = node;
+  }
+
+  private static void newline(StringBuffer pretty, int indent) {
+    pretty.append('\n');
+    for (int j = 0; j < indent * 2; j++) {
+      pretty.append(' ');
+    }
+  }
+
+  public Node getNode() {
+    return node;
+  }
+
+  /**
+   * Get child elements of the injection plan tree.
+   * By default, returns an empty list.
+   *
+   * @return An empty list.
+   */
+  @SuppressWarnings("unchecked")
+  @Override
+  public Collection<InjectionPlan<?>> getChildren() {
+    return Collections.EMPTY_LIST;
+  }
+
+  public abstract int getNumAlternatives();
+
+  public boolean isFeasible() {
+    return getNumAlternatives() > 0;
+  }
+
+  abstract public boolean isAmbiguous();
+
+  abstract public boolean isInjectable();
+
+  protected void pad(StringBuffer sb, int n) {
+    for (int i = 0; i < n; i++) {
+      sb.append("  ");
+    }
+  }
+
+  public String toPrettyString() {
+    String ugly = node.getFullName() + ":\n" + toString();
+    StringBuffer pretty = new StringBuffer();
+    int currentIndent = 1;
+    for (int i = 0; i < ugly.length(); i++) {
+      char c = ugly.charAt(i);
+      if (c == '(') {
+        if (ugly.charAt(i + 1) == ')') {
+          pretty.append("()");
+          i++;
+        } else {
+          newline(pretty, currentIndent);
+          currentIndent++;
+          pretty.append(c);
+          pretty.append(' ');
+        }
+      } else if (c == '[') {
+        if (ugly.charAt(i + 1) == ']') {
+          pretty.append("[]");
+          i++;
+        } else {
+          newline(pretty, currentIndent);
+          currentIndent++;
+          pretty.append(c);
+          pretty.append(' ');
+        }
+      } else if (c == ')' || c == ']') {
+        currentIndent--;
+        newline(pretty, currentIndent);
+        pretty.append(c);
+      } else if (c == '|') {
+        newline(pretty, currentIndent);
+        pretty.append(c);
+      } else if (c == ',') {
+        currentIndent--;
+        newline(pretty, currentIndent);
+        pretty.append(c);
+        currentIndent++;
+      } else {
+        pretty.append(c);
+      }
+    }
+    return pretty.toString();
+  }
+
+  /**
+   * Algorithm for generating cant inject string:
+   * <p/>
+   * For infeasible plans:
+   * <p/>
+   * Some node types are "leaves":
+   * <ul>
+   * <li>NamedParameterNode</li>
+   * <li>ClassNode with no @Inject constructors</li>
+   * </ul>
+   * We do a depth first search of the injection plan, starting with the left
+   * most constructor arguments. When we encounter a constructor whose arguments
+   * are all either injectable or non-injectable leaf nodes, we return the name
+   * of its parent, and the name of the non-injectable leaves.
+   * <p/>
+   * For ambiguous plans:
+   * <p/>
+   * We perform a depth first search of the ambiguous constructors, as above. We
+   * return the name of the first class that has multiple constructors that are
+   * feasible or ambiguous (as opposed to having a single constructor with an
+   * ambiguous argument, or a constructor with an infeasible argument and an
+   * ambiguous argument).
+   */
+  public final String toCantInjectString() {
+    if (!isFeasible()) {
+      return toInfeasibleInjectString();
+    } else if (isAmbiguous()) {
+      return toAmbiguousInjectString();
+    } else {
+      throw new IllegalArgumentException(
+          "toCantInjectString() called on injectable constructor:"
+              + this.toPrettyString());
+    }
+  }
+
+  protected abstract String toAmbiguousInjectString();
+
+  protected abstract String toInfeasibleInjectString();
+
+  protected abstract boolean isInfeasibleLeaf();
+
+  public abstract String toShallowString();
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ListInjectionPlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ListInjectionPlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ListInjectionPlan.java
new file mode 100644
index 0000000..236820c
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ListInjectionPlan.java
@@ -0,0 +1,106 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ListInjectionPlan<T> extends InjectionPlan<T> {
+  private final List<InjectionPlan<T>> entries = new ArrayList<>();
+  private final int numAlternatives;
+  private final boolean isAmbiguous;
+  private final boolean isInjectable;
+
+  public ListInjectionPlan(Node name, List<InjectionPlan<T>> entries) {
+    super(name);
+    this.entries.addAll(entries);
+    int numAlternatives = 1;
+    boolean isAmbiguous = false;
+    boolean isInjectable = true;
+    for (InjectionPlan<T> ip : entries) {
+      numAlternatives *= ip.getNumAlternatives();
+      isAmbiguous |= ip.isAmbiguous();
+      isInjectable &= ip.isInjectable();
+    }
+    this.numAlternatives = numAlternatives;
+    this.isAmbiguous = isAmbiguous;
+    this.isInjectable = isInjectable;
+  }
+
+  @Override
+  public int getNumAlternatives() {
+    return numAlternatives;
+  }
+
+  @Override
+  public boolean isAmbiguous() {
+    return isAmbiguous;
+  }
+
+  @Override
+  public boolean isInjectable() {
+    return isInjectable;
+  }
+
+  public List<InjectionPlan<T>> getEntryPlans() {
+    return new ArrayList<>(this.entries);
+  }
+
+  @Override
+  protected String toAmbiguousInjectString() {
+    StringBuilder sb = new StringBuilder(getNode().getFullName() + "(list) includes ambiguous plans [");
+    for (InjectionPlan<T> ip : entries) {
+      if (ip.isAmbiguous()) {
+        sb.append("\n" + ip.toAmbiguousInjectString());
+      }
+    }
+    sb.append("]");
+    return sb.toString();
+  }
+
+  @Override
+  protected String toInfeasibleInjectString() {
+    StringBuilder sb = new StringBuilder(getNode().getFullName() + "(list) includes infeasible plans [");
+    for (InjectionPlan<T> ip : entries) {
+      if (!ip.isFeasible()) {
+        sb.append("\n" + ip.toInfeasibleInjectString());
+      }
+    }
+    sb.append("\n]");
+    return sb.toString();
+  }
+
+  @Override
+  protected boolean isInfeasibleLeaf() {
+    return false;
+  }
+
+  @Override
+  public String toShallowString() {
+    StringBuilder sb = new StringBuilder("list { ");
+    for (InjectionPlan<T> ip : entries) {
+      sb.append("\n" + ip.toShallowString());
+    }
+    sb.append("\n } ");
+    return null;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/SetInjectionPlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/SetInjectionPlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/SetInjectionPlan.java
new file mode 100644
index 0000000..e39a17f
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/SetInjectionPlan.java
@@ -0,0 +1,106 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+import org.apache.reef.tang.util.MonotonicHashSet;
+
+import java.util.Set;
+
+public class SetInjectionPlan<T> extends InjectionPlan<T> {
+  private final Set<InjectionPlan<T>> entries = new MonotonicHashSet<>();
+  private final int numAlternatives;
+  private final boolean isAmbiguous;
+  private final boolean isInjectable;
+
+  public SetInjectionPlan(Node name, Set<InjectionPlan<T>> entries) {
+    super(name);
+    this.entries.addAll(entries);
+    int numAlternatives = 1;
+    boolean isAmbiguous = false;
+    boolean isInjectable = true;
+    for (InjectionPlan<T> ip : entries) {
+      numAlternatives *= ip.getNumAlternatives();
+      isAmbiguous |= ip.isAmbiguous();
+      isInjectable &= ip.isInjectable();
+    }
+    this.numAlternatives = numAlternatives;
+    this.isAmbiguous = isAmbiguous;
+    this.isInjectable = isInjectable;
+  }
+
+  @Override
+  public int getNumAlternatives() {
+    return numAlternatives;
+  }
+
+  @Override
+  public boolean isAmbiguous() {
+    return isAmbiguous;
+  }
+
+  @Override
+  public boolean isInjectable() {
+    return isInjectable;
+  }
+
+  public Set<InjectionPlan<T>> getEntryPlans() {
+    return new MonotonicHashSet<>(this.entries);
+  }
+
+  @Override
+  protected String toAmbiguousInjectString() {
+    StringBuilder sb = new StringBuilder(getNode().getFullName() + "(set) includes ambiguous plans [");
+    for (InjectionPlan<T> ip : entries) {
+      if (ip.isAmbiguous()) {
+        sb.append("\n" + ip.toAmbiguousInjectString());
+      }
+    }
+    sb.append("]");
+    return sb.toString();
+  }
+
+  @Override
+  protected String toInfeasibleInjectString() {
+    StringBuilder sb = new StringBuilder(getNode().getFullName() + "(set) includes infeasible plans [");
+    for (InjectionPlan<T> ip : entries) {
+      if (!ip.isFeasible()) {
+        sb.append("\n" + ip.toInfeasibleInjectString());
+      }
+    }
+    sb.append("\n]");
+    return sb.toString();
+  }
+
+  @Override
+  protected boolean isInfeasibleLeaf() {
+    return false;
+  }
+
+  @Override
+  public String toShallowString() {
+    StringBuilder sb = new StringBuilder("set { ");
+    for (InjectionPlan<T> ip : entries) {
+      sb.append("\n" + ip.toShallowString());
+    }
+    sb.append("\n } ");
+    return null;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/StackBindLocation.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/StackBindLocation.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/StackBindLocation.java
new file mode 100644
index 0000000..1349001
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/StackBindLocation.java
@@ -0,0 +1,46 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.BindLocation;
+
+import java.util.Arrays;
+
+public class StackBindLocation implements BindLocation {
+  final StackTraceElement[] stack;
+
+  public StackBindLocation() {
+    StackTraceElement[] stack = new Throwable().getStackTrace();
+    if (stack.length != 0) {
+      this.stack = Arrays.copyOfRange(stack, 1, stack.length);
+    } else {
+      this.stack = new StackTraceElement[0];
+    }
+  }
+
+  @Override
+  public String toString() {
+    StringBuffer sb = new StringBuffer("[\n");
+    for (StackTraceElement e : stack) {
+      sb.append(e.toString() + "\n");
+    }
+    sb.append("]\n");
+    return sb.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Subplan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Subplan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Subplan.java
new file mode 100644
index 0000000..5866703
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Subplan.java
@@ -0,0 +1,188 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+
+import java.util.*;
+
+final public class Subplan<T> extends InjectionPlan<T> {
+  final InjectionPlan<? extends T>[] alternatives;
+  final int numAlternatives;
+  final int selectedIndex;
+
+  @SafeVarargs
+  public Subplan(Node node, int selectedIndex, InjectionPlan<T>... alternatives) {
+    super(node);
+    this.alternatives = alternatives;
+    if (selectedIndex < -1 || selectedIndex >= alternatives.length) {
+      throw new ArrayIndexOutOfBoundsException();
+    }
+    this.selectedIndex = selectedIndex;
+    if (selectedIndex != -1) {
+      this.numAlternatives = alternatives[selectedIndex].getNumAlternatives();
+    } else {
+      int numAlternatives = 0;
+      for (InjectionPlan<? extends T> a : alternatives) {
+        numAlternatives += a.getNumAlternatives();
+      }
+      this.numAlternatives = numAlternatives;
+    }
+  }
+
+  @SafeVarargs
+  public Subplan(Node node, InjectionPlan<T>... alternatives) {
+    this(node, -1, alternatives);
+  }
+
+  /**
+   * Get child elements of the injection plan tree.
+   * TODO: use ArrayList internally (and maybe for input, too).
+   *
+   * @return A list of injection sub-plans.
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  @Override
+  public Collection<InjectionPlan<?>> getChildren() {
+    return (Collection) Collections.unmodifiableCollection(Arrays.asList(this.alternatives));
+  }
+
+  @Override
+  public int getNumAlternatives() {
+    return this.numAlternatives;
+  }
+
+  /**
+   * Even if there is only one sub-plan, it was registered as a default plan,
+   * and is therefore ambiguous.
+   */
+  @Override
+  public boolean isAmbiguous() {
+    if (selectedIndex == -1) {
+      return true;
+    }
+    return alternatives[selectedIndex].isAmbiguous();
+  }
+
+  @Override
+  public boolean isInjectable() {
+    if (selectedIndex == -1) {
+      return false;
+    } else {
+      return alternatives[selectedIndex].isInjectable();
+    }
+  }
+
+  @Override
+  public String toString() {
+    if (alternatives.length == 1) {
+      return getNode().getName() + " = " + alternatives[0];
+    } else if (alternatives.length == 0) {
+      return getNode().getName() + ": no injectable constructors";
+    }
+    StringBuilder sb = new StringBuilder("[");
+    sb.append(getNode().getName() + " = " + alternatives[0]);
+    for (int i = 1; i < alternatives.length; i++) {
+      sb.append(" | " + alternatives[i]);
+    }
+    sb.append("]");
+    return sb.toString();
+  }
+
+  @Override
+  public String toShallowString() {
+    if (alternatives.length == 1) {
+      return getNode().getName() + " = " + alternatives[0].toShallowString();
+    } else if (alternatives.length == 0) {
+      return getNode().getName() + ": no injectable constructors";
+    }
+    StringBuilder sb = new StringBuilder("[");
+    sb.append(getNode().getName() + " = " + alternatives[0].toShallowString());
+    for (int i = 1; i < alternatives.length; i++) {
+      sb.append(" | " + alternatives[i].toShallowString());
+    }
+    sb.append("]");
+    return sb.toString();
+  }
+
+  public int getSelectedIndex() {
+    return selectedIndex;
+  }
+
+  public InjectionPlan<? extends T> getDelegatedPlan() {
+    if (selectedIndex == -1) {
+      throw new IllegalStateException();
+    } else {
+      return alternatives[selectedIndex];
+    }
+  }
+
+  @Override
+  protected String toAmbiguousInjectString() {
+    if (alternatives.length == 1) {
+      return alternatives[0].toAmbiguousInjectString();
+    } else if (selectedIndex != -1) {
+      return alternatives[selectedIndex].toAmbiguousInjectString();
+    } else {
+      List<InjectionPlan<?>> alts = new ArrayList<>();
+      List<InjectionPlan<?>> ambig = new ArrayList<>();
+      for (InjectionPlan<?> alt : alternatives) {
+        if (alt.isFeasible()) {
+          alts.add(alt);
+        }
+        if (alt.isAmbiguous()) {
+          ambig.add(alt);
+        }
+      }
+      StringBuffer sb = new StringBuffer("Ambigous subplan " + getNode().getFullName());
+      for (InjectionPlan<?> alt : alts) {
+        sb.append("\n  " + alt.toShallowString() + " ");
+      }
+      for (InjectionPlan<?> alt : ambig) {
+        sb.append("\n  " + alt.toShallowString() + " ");
+      }
+      sb.append("\n]");
+      return sb.toString();
+    }
+  }
+
+  @Override
+  protected String toInfeasibleInjectString() {
+    if (alternatives.length == 1) {
+      return alternatives[0].toInfeasibleInjectString();
+    } else if (alternatives.length == 0) {
+      return "No known implementations / injectable constructors for "
+          + this.getNode().getFullName();
+    } else if (selectedIndex != -1) {
+      return alternatives[selectedIndex].toInfeasibleInjectString();
+    } else {
+      return "Multiple infeasible plans: " + toPrettyString();
+    }
+  }
+
+  @Override
+  protected boolean isInfeasibleLeaf() {
+    return false;
+  }
+
+  public InjectionPlan<?>[] getPlans() {
+    return Arrays.copyOf(alternatives, alternatives.length);
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/TangImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/TangImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/TangImpl.java
new file mode 100644
index 0000000..52e0569
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/TangImpl.java
@@ -0,0 +1,161 @@
+/**
+ * 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.reef.tang.implementation;
+
+import org.apache.reef.tang.*;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.implementation.java.ClassHierarchyImpl;
+import org.apache.reef.tang.implementation.java.InjectorImpl;
+import org.apache.reef.tang.implementation.java.JavaConfigurationBuilderImpl;
+
+import java.net.URL;
+import java.util.*;
+
+public class TangImpl implements Tang {
+
+  private static Map<SetValuedKey, JavaClassHierarchy> defaultClassHierarchy = new HashMap<>();
+
+  /**
+   * Only for testing. Deletes Tang's current database of known classes, forcing
+   * it to rebuild them over time.
+   */
+  public static void reset() {
+    defaultClassHierarchy = new HashMap<>(); //new ClassHierarchyImpl();
+  }
+
+  @Override
+  public Injector newInjector(Configuration... confs) throws BindException {
+    return new InjectorImpl(new JavaConfigurationBuilderImpl(confs).build());
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public JavaConfigurationBuilder newConfigurationBuilder() {
+    try {
+      return newConfigurationBuilder(new URL[0], new Configuration[0], new Class[0]);
+    } catch (BindException e) {
+      throw new IllegalStateException(
+          "Caught unexpeceted bind exception!  Implementation bug.", e);
+    }
+  }
+
+  @Override
+  public ConfigurationBuilder newConfigurationBuilder(ClassHierarchy ch) {
+    return new ConfigurationBuilderImpl(ch);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public JavaConfigurationBuilder newConfigurationBuilder(URL... jars) {
+    try {
+      return newConfigurationBuilder(jars, new Configuration[0], new Class[0]);
+    } catch (BindException e) {
+      throw new IllegalStateException(
+          "Caught unexpeceted bind exception!  Implementation bug.", e);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public JavaConfigurationBuilder newConfigurationBuilder(
+      Configuration... confs) throws BindException {
+    return newConfigurationBuilder(new URL[0], confs, new Class[0]);
+  }
+
+  @Override
+  public final JavaConfigurationBuilder newConfigurationBuilder(
+      @SuppressWarnings("unchecked") Class<? extends ExternalConstructor<?>>... parsers) throws BindException {
+    return newConfigurationBuilder(new URL[0], new Configuration[0], parsers);
+  }
+
+  @Override
+  public JavaConfigurationBuilder newConfigurationBuilder(URL[] jars,
+                                                          Configuration[] confs, Class<? extends ExternalConstructor<?>>[] parameterParsers) throws BindException {
+    JavaConfigurationBuilder cb = new JavaConfigurationBuilderImpl(jars, confs, parameterParsers);
+//    for (Configuration c : confs) {
+//      cb.addConfiguration(c);
+//    }
+    return cb;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public JavaClassHierarchy getDefaultClassHierarchy() {
+    return getDefaultClassHierarchy(new URL[0], new Class[0]);
+  }
+
+  @Override
+  public JavaClassHierarchy getDefaultClassHierarchy(URL[] jars, Class<? extends ExternalConstructor<?>>[] parameterParsers) {
+    SetValuedKey key = new SetValuedKey(jars, parameterParsers);
+
+    JavaClassHierarchy ret = defaultClassHierarchy.get(key);
+    if (ret == null) {
+      ret = new ClassHierarchyImpl(jars, parameterParsers);
+      defaultClassHierarchy.put(key, ret);
+    }
+    return ret;
+  }
+
+  @Override
+  public Injector newInjector(Configuration confs) {
+    try {
+      return newInjector(new Configuration[]{confs});
+    } catch (BindException e) {
+      throw new IllegalStateException("Unexpected error cloning configuration", e);
+    }
+  }
+
+  @Override
+  public Injector newInjector() {
+    try {
+      return newInjector(new Configuration[]{});
+    } catch (BindException e) {
+      throw new IllegalStateException("Unexpected error from empty configuration", e);
+    }
+  }
+
+  private class SetValuedKey {
+    public final Set<Object> key;
+
+    public SetValuedKey(Object[] ts, Object[] us) {
+      key = new HashSet<Object>(Arrays.asList(ts));
+      key.addAll(Arrays.asList(us));
+    }
+
+    @Override
+    public int hashCode() {
+      int i = 0;
+      for (Object t : key) {
+        i += t.hashCode();
+      }
+      return i;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      SetValuedKey other = (SetValuedKey) o;
+      if (other.key.size() != this.key.size()) {
+        return false;
+      }
+      return key.containsAll(other.key);
+    }
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/ClassHierarchyImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/ClassHierarchyImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/ClassHierarchyImpl.java
new file mode 100644
index 0000000..4368aa8
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/ClassHierarchyImpl.java
@@ -0,0 +1,448 @@
+/**
+ * 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.reef.tang.implementation.java;
+
+import org.apache.reef.tang.ClassHierarchy;
+import org.apache.reef.tang.ExternalConstructor;
+import org.apache.reef.tang.JavaClassHierarchy;
+import org.apache.reef.tang.annotations.Name;
+import org.apache.reef.tang.annotations.NamedParameter;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.exceptions.ClassHierarchyException;
+import org.apache.reef.tang.exceptions.NameResolutionException;
+import org.apache.reef.tang.exceptions.ParseException;
+import org.apache.reef.tang.formats.ParameterParser;
+import org.apache.reef.tang.types.*;
+import org.apache.reef.tang.util.MonotonicTreeMap;
+import org.apache.reef.tang.util.ReflectionUtilities;
+
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.*;
+
+public class ClassHierarchyImpl implements JavaClassHierarchy {
+  // TODO Want to add a "register namespace" method, but Java is not designed
+  // to support such things.
+  // There are third party libraries that would help, but they can fail if the
+  // relevant jar has not yet been loaded.  Tint works around this using such
+  // a library.
+
+  /**
+   * The ParameterParser that this ClassHierarchy uses to parse default values.
+   * Custom parameter parsers allow applications to extend the set of classes
+   * that Tang can parse.
+   */
+  public final ParameterParser parameterParser = new ParameterParser();
+  /**
+   * The classloader that was used to populate this class hierarchy.
+   */
+  private final URLClassLoader loader;
+  /**
+   * The jars that are reflected by that loader.  These are in addition to
+   * whatever jars are available by the default classloader (i.e., the one
+   * that loaded Tang.  We need this list so that we can merge class hierarchies
+   * that are backed by different classpaths.
+   */
+  private final List<URL> jars;
+  /**
+   * A reference to the root package which is a root of a tree of nodes.
+   * The children of each node in the tree are accessible by name, and the
+   * structure of the tree mirrors Java's package namespace.
+   */
+  private final PackageNode namespace;
+  /**
+   * A map from short name to named parameter node.  This is only used to
+   * sanity check short names so that name clashes get resolved.
+   */
+  private final Map<String, NamedParameterNode<?>> shortNames = new MonotonicTreeMap<>();
+
+  @SuppressWarnings("unchecked")
+  public ClassHierarchyImpl() {
+    this(new URL[0], new Class[0]);
+  }
+
+  @SuppressWarnings("unchecked")
+  public ClassHierarchyImpl(URL... jars) {
+    this(jars, new Class[0]);
+  }
+
+  public ClassHierarchyImpl(URL[] jars, Class<? extends ExternalConstructor<?>>[] parameterParsers) {
+    this.namespace = JavaNodeFactory.createRootPackageNode();
+    this.jars = new ArrayList<>(Arrays.asList(jars));
+    this.loader = new URLClassLoader(jars, this.getClass().getClassLoader());
+    for (Class<? extends ExternalConstructor<?>> p : parameterParsers) {
+      try {
+        parameterParser.addParser(p);
+      } catch (BindException e) {
+        throw new IllegalArgumentException("Could not register parameter parsers", e);
+      }
+    }
+  }
+
+  /**
+   * A helper method that returns the parsed default value of a given
+   * NamedParameter.
+   *
+   * @return null or an empty set if there is no default value, the default value (or set of values) otherwise.
+   * @throws ClassHierarchyException if a default value was specified, but could not be parsed, or if a set of
+   *                                 values were specified for a non-set parameter.
+   */
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T> T parseDefaultValue(NamedParameterNode<T> name) {
+    String[] vals = name.getDefaultInstanceAsStrings();
+    T[] ret = (T[]) new Object[vals.length];
+    for (int i = 0; i < vals.length; i++) {
+      String val = vals[i];
+      try {
+        ret[i] = parse(name, val);
+      } catch (ParseException e) {
+        throw new ClassHierarchyException("Could not parse default value", e);
+      }
+    }
+    if (name.isSet()) {
+      return (T) new HashSet<T>(Arrays.asList(ret));
+    } else if (name.isList()) {
+      return (T) new ArrayList<T>(Arrays.asList(ret));
+    } else {
+      if (ret.length == 0) {
+        return null;
+      } else if (ret.length == 1) {
+        return ret[0];
+      } else {
+        throw new IllegalStateException("Multiple defaults for non-set named parameter! " + name.getFullName());
+      }
+    }
+  }
+
+  /**
+   * Parse a string, assuming that it is of the type expected by a given NamedParameter.
+   * <p/>
+   * This method does not deal with sets; if the NamedParameter is set valued, then the provided
+   * string should correspond to a single member of the set.  It is up to the caller to call parse
+   * once for each value that should be parsed as a member of the set.
+   *
+   * @return a non-null reference to the parsed value.
+   */
+  @Override
+  @SuppressWarnings("unchecked")
+  public <T> T parse(NamedParameterNode<T> np, String value) throws ParseException {
+    final ClassNode<T> iface;
+    try {
+      iface = (ClassNode<T>) getNode(np.getFullArgName());
+    } catch (NameResolutionException e) {
+      throw new IllegalStateException("Could not parse validated named parameter argument type.  NamedParameter is " + np.getFullName() + " argument type is " + np.getFullArgName());
+    }
+    Class<?> clazz;
+    String fullName;
+    try {
+      clazz = (Class<?>) classForName(iface.getFullName());
+      fullName = null;
+    } catch (ClassNotFoundException e) {
+      clazz = null;
+      fullName = iface.getFullName();
+    }
+    try {
+      if (clazz != null) {
+        return (T) parameterParser.parse(clazz, value);
+      } else {
+        return parameterParser.parse(fullName, value);
+      }
+    } catch (UnsupportedOperationException e) {
+      try {
+        final Node impl = getNode(value);
+        if (impl instanceof ClassNode) {
+          if (isImplementation(iface, (ClassNode<?>) impl)) {
+            return (T) impl;
+          }
+        }
+        throw new ParseException("Name<" + iface.getFullName() + "> " + np.getFullName() + " cannot take non-subclass " + impl.getFullName(), e);
+      } catch (NameResolutionException e2) {
+        throw new ParseException("Name<" + iface.getFullName() + "> " + np.getFullName() + " cannot take non-class " + value, e);
+      }
+    }
+  }
+
+  /**
+   * Helper method that converts a String to a Class using this
+   * ClassHierarchy's classloader.
+   */
+  @Override
+  public Class<?> classForName(String name) throws ClassNotFoundException {
+    return ReflectionUtilities.classForName(name, loader);
+  }
+
+  private <T, U> Node buildPathToNode(Class<U> clazz)
+      throws ClassHierarchyException {
+    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();
+    }
+    Node parent = root;
+
+    Type argType = ReflectionUtilities.getNamedParameterTargetOrNull(clazz);
+
+    if (argType == null) {
+      return JavaNodeFactory.createClassNode(parent, clazz);
+    } else {
+
+      @SuppressWarnings("unchecked")
+      // checked inside of NamedParameterNode, using reflection.
+          NamedParameterNode<T> np = JavaNodeFactory.createNamedParameterNode(
+          parent, (Class<? extends Name<T>>) clazz, argType);
+
+      if (parameterParser.canParse(ReflectionUtilities.getFullName(argType))) {
+        if (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));
+        }
+      }
+
+      String shortName = np.getShortName();
+      if (shortName != null) {
+        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;
+    }
+  }
+
+  private Node getAlreadyBoundNode(Class<?> clazz) throws NameResolutionException {
+    return getAlreadyBoundNode(ReflectionUtilities.getFullName(clazz));
+  }
+
+  @Override
+  public Node getNode(Class<?> clazz) {
+    try {
+      return getNode(ReflectionUtilities.getFullName(clazz));
+    } catch (NameResolutionException e) {
+      throw new ClassHierarchyException("JavaClassHierarchy could not resolve " + clazz
+          + " which is definitely avalable at runtime", e);
+    }
+  }
+
+  @Override
+  public synchronized Node getNode(String name) throws NameResolutionException {
+    Node n = register(name);
+    if (n == null) {
+      // This will never succeed; it just generates a nice exception.
+      getAlreadyBoundNode(name);
+      throw new IllegalStateException("IMPLEMENTATION BUG: Register failed, "
+          + "but getAlreadyBoundNode succeeded!");
+    }
+    return n;
+  }
+
+  private Node getAlreadyBoundNode(String name) throws NameResolutionException {
+    Node root = namespace;
+    String[] toks = name.split("\\$");
+    String outerClassName = toks[0];
+    root = root.get(outerClassName);
+    if (root == null) {
+      throw new NameResolutionException(name, outerClassName);
+    }
+    for (int i = 1; i < toks.length; i++) {
+      root = root.get(toks[i]);
+      if (root == null) {
+        StringBuilder sb = new StringBuilder(outerClassName);
+        for (int j = 0; j < i; j++) {
+          sb.append(toks[j]);
+          if (j != i - 1) {
+            sb.append(".");
+          }
+        }
+        throw new NameResolutionException(name, sb.toString());
+      }
+    }
+    return root;
+  }
+
+  private Node register(String s) {
+    final Class<?> c;
+    try {
+      c = classForName(s);
+    } catch (ClassNotFoundException e1) {
+      return null;
+    }
+    try {
+      Node n = getAlreadyBoundNode(c);
+      return n;
+    } catch (NameResolutionException e) {
+    }
+    // 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 (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.
+    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.
+    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 (Class<?> inner_class : c.getDeclaredClasses()) {
+      register(ReflectionUtilities.getFullName(inner_class));
+    }
+    if (n instanceof ClassNode) {
+      ClassNode<?> cls = (ClassNode<?>) n;
+      for (ConstructorDef<?> def : cls.getInjectableConstructors()) {
+        for (ConstructorArg arg : def.getArgs()) {
+          register(arg.getType());
+          if (arg.getNamedParameterName() != null) {
+            NamedParameterNode<?> np = (NamedParameterNode<?>) register(arg
+                .getNamedParameterName());
+            try {
+              if (np.isSet()) {
+                /// XXX When handling sets, need to track target of generic parameter, and check the type here!
+              } else if (np.isList()) {
+
+              } else {
+                if (!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 (ClassNotFoundException e) {
+              throw new ClassHierarchyException("Constructor refers to unknown class "
+                  + arg.getType(), e);
+            }
+          }
+        }
+      }
+    } else if (n instanceof NamedParameterNode) {
+      NamedParameterNode<?> np = (NamedParameterNode<?>) n;
+      register(np.getFullArgName());
+    }
+    return n;
+  }
+
+  /**
+   * 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 (NameResolutionException e) {
+    }
+
+    final Node n = buildPathToNode(c);
+
+    if (n instanceof ClassNode) {
+      ClassNode<T> cn = (ClassNode<T>) n;
+      Class<T> superclass = (Class<T>) c.getSuperclass();
+      if (superclass != null) {
+        try {
+          ((ClassNode<T>) getAlreadyBoundNode(superclass)).putImpl(cn);
+        } catch (NameResolutionException e) {
+          throw new IllegalStateException(e);
+        }
+      }
+      for (Class<?> interf : c.getInterfaces()) {
+        try {
+          ((ClassNode<T>) getAlreadyBoundNode(interf)).putImpl(cn);
+        } catch (NameResolutionException e) {
+          throw new IllegalStateException(e);
+        }
+      }
+    }
+    return n;
+  }
+
+  @Override
+  public PackageNode getNamespace() {
+    return namespace;
+  }
+
+  @Override
+  public synchronized boolean isImplementation(ClassNode<?> inter, ClassNode<?> impl) {
+    return impl.isImplementationOf(inter);
+  }
+
+  @Override
+  public synchronized ClassHierarchy merge(ClassHierarchy ch) {
+    if (this == ch) {
+      return this;
+    }
+    if (!(ch instanceof ClassHierarchyImpl)) {
+      throw new UnsupportedOperationException("Can't merge java and non-java class hierarchies yet!");
+    }
+    if (this.jars.size() == 0) {
+      return ch;
+    }
+    ClassHierarchyImpl chi = (ClassHierarchyImpl) ch;
+    HashSet<URL> otherJars = new HashSet<>();
+    otherJars.addAll(chi.jars);
+    HashSet<URL> myJars = new HashSet<>();
+    myJars.addAll(this.jars);
+    if (myJars.containsAll(otherJars)) {
+      return this;
+    } else if (otherJars.containsAll(myJars)) {
+      return ch;
+    } else {
+      myJars.addAll(otherJars);
+      return new ClassHierarchyImpl(myJars.toArray(new URL[0]));
+    }
+  }
+}