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:44 UTC

[11/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/util/Tint.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/Tint.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/Tint.java
new file mode 100644
index 0000000..3db6afc
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/Tint.java
@@ -0,0 +1,735 @@
+/**
+ * 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.util;
+
+import org.apache.reef.tang.ExternalConstructor;
+import org.apache.reef.tang.JavaClassHierarchy;
+import org.apache.reef.tang.Tang;
+import org.apache.reef.tang.annotations.*;
+import org.apache.reef.tang.exceptions.ClassHierarchyException;
+import org.apache.reef.tang.exceptions.NameResolutionException;
+import org.apache.reef.tang.formats.ConfigurationModule;
+import org.apache.reef.tang.formats.ConfigurationModuleBuilder;
+import org.apache.reef.tang.types.*;
+import org.apache.reef.tang.util.walk.AbstractClassHierarchyNodeVisitor;
+import org.apache.reef.tang.util.walk.NodeVisitor;
+import org.apache.reef.tang.util.walk.Walk;
+import org.reflections.Reflections;
+import org.reflections.scanners.MethodAnnotationsScanner;
+import org.reflections.scanners.MethodParameterScanner;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.scanners.TypeAnnotationsScanner;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class Tint {
+  final private static String SETTERS = "setters";
+  final private static String USES = "uses";
+  final private static String FULLNAME = "fullName";
+  final JavaClassHierarchy ch;
+  final Map<Field, ConfigurationModule> modules = new MonotonicHashMap<>();
+  final MonotonicMultiMap<String, String> setters = new MonotonicMultiMap<>();
+  // map from thing that was used to user of the thing.
+  final MonotonicMultiMap<String, String> usages = new MonotonicMultiMap<>();
+  final Set<ClassNode<?>> knownClasses = new MonotonicSet<>();
+  final Set<String> divs = new MonotonicSet<>();
+
+  {
+    divs.add("doc");
+    divs.add(USES);
+    divs.add(SETTERS);
+
+  }
+
+  public Tint() {
+    this(new URL[0]);
+  }
+
+  public Tint(URL[] jars) {
+    this(jars, false);
+  }
+
+  @SuppressWarnings("unchecked")
+  public Tint(URL[] jars, boolean checkTang) {
+    Object[] args = new Object[jars.length + 6];
+    for (int i = 0; i < jars.length; i++) {
+      args[i] = jars[i];
+    }
+    args[args.length - 1] = new TypeAnnotationsScanner();
+    args[args.length - 2] = new SubTypesScanner();
+    args[args.length - 3] = new MethodAnnotationsScanner();
+    args[args.length - 4] = new MethodParameterScanner();
+    args[args.length - 5] = "com.microsoft";
+    args[args.length - 6] = "org.apache";
+    Reflections r = new Reflections(args);
+//    Set<Class<?>> classes = new MonotonicSet<>();
+    Set<String> strings = new TreeSet<>();
+    Set<String> moduleBuilders = new MonotonicSet<>();
+
+    // Workaround bug in Reflections by keeping things stringly typed, and using Tang to parse them.
+//  Set<Constructor<?>> injectConstructors = (Set<Constructor<?>>)(Set)r.getMethodsAnnotatedWith(Inject.class);
+//  for(Constructor<?> c : injectConstructors) {
+//    classes.add(c.getDeclaringClass());
+//  }
+    Set<String> injectConstructors = r.getStore().getConstructorsAnnotatedWith(ReflectionUtilities.getFullName(Inject.class));
+    for (String s : injectConstructors) {
+      strings.add(s.replaceAll("\\.<.+$", ""));
+    }
+    Set<String> parameterConstructors = r.getStore().get(MethodParameterScanner.class, ReflectionUtilities.getFullName(Parameter.class));
+    for (String s : parameterConstructors) {
+      strings.add(s.replaceAll("\\.<.+$", ""));
+    }
+//    Set<Class> r.getConstructorsWithAnyParamAnnotated(Parameter.class);
+//    for(Constructor<?> c : parameterConstructors) {
+//      classes.add(c.getDeclaringClass());
+//    }
+    Set<String> defaultStrings = r.getStore().get(TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(DefaultImplementation.class));
+    strings.addAll(defaultStrings);
+    strings.addAll(r.getStore().get(TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(NamedParameter.class)));
+    strings.addAll(r.getStore().get(TypeAnnotationsScanner.class, ReflectionUtilities.getFullName(Unit.class)));
+//    classes.addAll(r.getTypesAnnotatedWith(DefaultImplementation.class));
+//    classes.addAll(r.getTypesAnnotatedWith(NamedParameter.class));
+//    classes.addAll(r.getTypesAnnotatedWith(Unit.class));
+
+    strings.addAll(r.getStore().get(SubTypesScanner.class, ReflectionUtilities.getFullName(Name.class)));
+
+    moduleBuilders.addAll(r.getStore().get(SubTypesScanner.class, ReflectionUtilities.getFullName(ConfigurationModuleBuilder.class)));
+//    classes.addAll(r.getSubTypesOf(Name.class));
+
+    ch = Tang.Factory.getTang().getDefaultClassHierarchy(jars, (Class<? extends ExternalConstructor<?>>[]) new Class[0]);
+//    for(String s : defaultStrings) {
+//      if(classFilter(checkTang, s)) {
+//        try {
+//          ch.getNode(s);
+//        } catch(ClassHierarchyException | NameResolutionException | ClassNotFoundException e) {
+//          System.err.println(e.getMessage());
+//        }
+//      }
+//    }
+
+    for (String s : strings) {
+      if (classFilter(checkTang, s)) {
+        try {
+          ch.getNode(s);
+        } catch (ClassHierarchyException | NameResolutionException e) {
+          System.err.println(e.getMessage());
+        }
+      }
+    }
+    for (String s : moduleBuilders) {
+      if (classFilter(checkTang, s)) {
+        try {
+          ch.getNode(s);
+        } catch (ClassHierarchyException | NameResolutionException e) {
+          e.printStackTrace();
+        }
+      }
+    }
+
+    NodeVisitor<Node> v = new AbstractClassHierarchyNodeVisitor() {
+
+      @Override
+      public boolean visit(NamedParameterNode<?> node) {
+        String node_s = node.getFullName();
+        for (String s : node.getDefaultInstanceAsStrings()) {
+          if (!usages.contains(s, node_s)) {
+            usages.put(s, node_s);
+          }
+        }
+        return true;
+      }
+
+      @Override
+      public boolean visit(PackageNode node) {
+        return true;
+      }
+
+      @Override
+      public boolean visit(ClassNode<?> node) {
+        String node_s = node.getFullName();
+        for (ConstructorDef<?> d : node.getInjectableConstructors()) {
+          for (ConstructorArg a : d.getArgs()) {
+            if (a.getNamedParameterName() != null &&
+                !usages.contains(a.getNamedParameterName(), node_s)) {
+              usages.put(a.getNamedParameterName(), node_s);
+            }
+          }
+        }
+        if (!knownClasses.contains(node)) {
+          knownClasses.add(node);
+        }
+        return true;
+      }
+    };
+    int numClasses;
+    do {
+      numClasses = knownClasses.size();
+
+      Walk.preorder(v, null, ch.getNamespace());
+
+      for (ClassNode<?> cn : knownClasses) {
+        try {
+          String s = cn.getFullName();
+          if (classFilter(checkTang, s)) {
+            Class<?> c = ch.classForName(s);
+            processDefaultAnnotation(c);
+            processConfigurationModules(c);
+          }
+        } catch (ClassNotFoundException e) {
+          e.printStackTrace();
+        }
+      }
+
+      for (Field f : modules.keySet()) {
+        ConfigurationModule m = modules.get(f);
+        String f_s = ReflectionUtilities.getFullName(f);
+        Set<NamedParameterNode<?>> nps = m.getBoundNamedParameters();
+        for (NamedParameterNode<?> np : nps) {
+          String np_s = np.getFullName();
+          if (!setters.contains(np_s, f_s)) {
+            setters.put(np_s, f_s);
+          }
+        }
+      }
+    } while (numClasses != knownClasses.size()); // Note naive fixed point evaluation here.  Semi-naive would be faster.
+
+  }
+
+  public static String stripCommonPrefixes(String s) {
+    return
+        stripPrefixHelper2(
+            stripPrefixHelper2(s, "java.lang"),
+            "java.util");
+  }
+
+  public static String stripPrefixHelper2(String s, String prefix) {
+    String[] pretok = prefix.split("\\.");
+    String[] stok = s.split("\\.");
+    StringBuffer sb = new StringBuffer();
+    int i;
+    for (i = 0; i < pretok.length && i < stok.length; i++) {
+      if (pretok[i].equals(stok[i])) {
+        sb.append(pretok[i].charAt(0) + ".");
+      } else {
+        break;
+      }
+    }
+    for (; i < stok.length - 1; i++) {
+      sb.append(stok[i] + ".");
+    }
+    sb.append(stok[stok.length - 1]);
+    return sb.toString();
+  }
+
+  /*  public static String stripPrefixHelper(String s, String prefix) {
+      if(!"".equals(prefix) && s.startsWith(prefix+".")) {
+        try {
+          String suffix = s.substring(prefix.length()+1);
+          if(suffix.matches("^[A-Z].+")){
+            return suffix;
+          } else {
+            String shorten = prefix.replaceAll("([^.])[^.]+", "$1");
+            return shorten + "." + suffix;
+          }
+        } catch(StringIndexOutOfBoundsException e) {
+          throw new RuntimeException("Couldn't strip " + prefix + " from " + s, e);
+        }
+      } else {
+        return s;
+      }
+    }*/
+  public static String stripPrefix(String s, String prefix) {
+    return stripPrefixHelper2(stripPrefixHelper2(stripPrefixHelper2(
+        stripCommonPrefixes(stripPrefixHelper2(s, prefix)),
+        "org.apache.reef"), "org.apache.reef"), "org.apache.reef.wake");
+  }
+
+  /**
+   * @param args
+   * @throws FileNotFoundException
+   * @throws MalformedURLException
+   */
+  public static void main(String[] args) throws FileNotFoundException, MalformedURLException {
+    int i = 0;
+    String doc = null;
+    String jar = null;
+    boolean tangTests = false;
+    while (i < args.length) {
+      if (args[i].equals("--doc")) {
+        i++;
+        doc = args[i];
+      } else if (args[i].equals("--jar")) {
+        i++;
+        jar = args[i];
+      } else if (args[i].equals("--tang-tests")) {
+        tangTests = true;
+      }
+
+      i++;
+    }
+
+    final Tint t;
+    if (jar != null) {
+      final File f = new File(jar);
+      if (!f.exists()) {
+        throw new FileNotFoundException(jar);
+      }
+      t = new Tint(new URL[]{f.toURI().toURL()}, tangTests);
+    } else {
+      t = new Tint(new URL[0], tangTests);
+    }
+
+    if (doc != null) {
+      try (final PrintStream out = new PrintStream(new FileOutputStream(new File(doc)))) {
+        out.println("<html><head><title>TangDoc</title>");
+
+        out.println("<style>");
+        out.println("body { font-family: 'Segoe UI', 'Comic Sans MS'; font-size:12pt; font-weight: 200; margin: 1em; column-count: 2; }");
+        out.println(".package { font-size:18pt; font-weight: 500; column-span: all; }");
+//      out.println(".class { break-after: never; }");
+//      out.println(".doc { break-before: never; }");
+        out.println(".decl-margin { padding: 8pt; break-inside: avoid; }");
+        out.println(".module-margin { padding: 8pt; column-span: all; break-inside: avoid; }");
+        out.println(".decl { background-color: aliceblue; padding: 6pt;}");
+        out.println(".fullName { font-size: 11pt; font-weight: 400; }");
+        out.println(".simpleName { font-size: 11pt; font-weight: 400; }");
+        out.println(".constructorArg { padding-left: 16pt; }");
+        out.println("." + SETTERS + " { padding-top: 6pt; font-size: 10pt; }");
+        out.println("." + USES + " { padding-top: 6pt; font-size: 10pt; }");
+        out.println("pre { font-size: 10pt; }");
+        out.println("</style>");
+
+        out.println("</head><body>");
+//      out.println("<table border='1'><tr><th>Type</th><th>Name</th><th>Default value</th><th>Documentation</th><th>Used by</th><th>Set by</th></tr>");
+
+        String currentPackage = "";
+//      int numcols = 0;
+        for (final Node n : t.getNamesUsedAndSet()) {
+          String fullName = n.getFullName();
+          String tok[] = fullName.split("\\.");
+          StringBuffer sb = new StringBuffer(tok[0]);
+          for (int j = 1; j < tok.length; j++) {
+            if (tok[j].matches("^[A-Z].*") || j > 4) {
+              break;
+            } else
+              sb.append("." + tok[j]);
+          }
+          String pack = sb.toString();
+          if (!currentPackage.equals(pack)) {
+            currentPackage = pack;
+            out.println(t.endPackage());
+            out.println(t.startPackage(currentPackage));
+//          numcols = 0;
+//          out.println("<div class='row'>");
+          }
+//        numcols++;
+//        if(numcols == NUMCOLS) {
+//          out.println("</div><div class='row'>");
+//        }
+          if (n instanceof NamedParameterNode<?>) {
+            out.println(t.toHtmlString((NamedParameterNode<?>) n, currentPackage));
+          } else if (n instanceof ClassNode<?>) {
+            out.println(t.toHtmlString((ClassNode<?>) n, currentPackage));
+          } else {
+            throw new IllegalStateException();
+          }
+        }
+        out.println("</div>");
+        out.println(t.endPackage());
+//      out.println("</table>");
+        out.println("<div class='package'>Module definitions</div>");
+        for (final Field f : t.modules.keySet()) {
+          String moduleName = ReflectionUtilities.getFullName(f);
+//        String declaringClassName = ReflectionUtilities.getFullName(f.getDeclaringClass());
+          out.println("<div class='module-margin' id='" + moduleName + "'><div class='decl'><span class='fullName'>" + moduleName + "</span>");
+          out.println("<pre>");
+          String conf = t.modules.get(f).toPrettyString();
+          String[] tok = conf.split("\n");
+          for (final String line : tok) {
+            out.println(stripPrefix(line, "no.such.prefix"));//t.modules.get(f).toPrettyString());
+          }
+//        List<Entry<String,String>> lines = t.modules.get(f).toStringPairs();
+//        for(Entry<String,String> line : lines) {
+//          String k = t.stripPrefix(line.getKey(), declaringClassName);
+//          String v = t.stripPrefix(line.getValue(), declaringClassName);
+//          out.println(k+"="+v);
+//        }
+          out.println("</pre>");
+          out.println("</div></div>");
+        }
+
+        out.println("<div class='package'>Interfaces and injectable classes</div>");
+        for (final ClassNode<?> c : t.knownClasses) {
+          if (t.classFilter(tangTests, c.getFullName())) {
+            Class<?> clz = null;
+            try {
+              clz = t.ch.classForName(c.getFullName());
+            } catch (ClassNotFoundException e) {
+              // TODO Auto-generated catch block
+              e.printStackTrace();
+            }
+            String typ = clz.isInterface() ? "interface" : "class";
+            out.println("<div class='module-margin' id='" + c.getFullName() + "'><div class='decl'><span class='fullName'>" + typ + " " + c.getFullName() + "</span>");
+            for (ConstructorDef<?> d : c.getInjectableConstructors()) {
+              out.println("<div class='uses'>" + c.getFullName() + "(");
+              for (ConstructorArg a : d.getArgs()) {
+                if (a.getNamedParameterName() != null) {
+                  out.print("<div class='constructorArg'><a href='#" + a.getType() + "'>" + stripPrefix(a.getType(), "xxx") + "</a> <a href='#" + a.getNamedParameterName() + "'>" + a.getNamedParameterName() + "</a></div>");
+                } else {
+                  out.print("<div class='constructorArg'><a href='#" + a.getType() + "'>" + stripPrefix(a.getType(), "xxx") + "</a></div>");
+                }
+              }
+              out.println(")</div>");
+            }
+            out.println("</div></div>");
+          }
+/*
+      out.println("<h1>Default usage of classes and constants</h1>");
+      for(String s : t.usages.keySet()) {
+        out.println("<h2>" + s + "</h2>");
+        for(Node n : t.usages.getValuesForKey(s)) {
+          out.println("<p>" + n.getFullName() + "</p>");
+        }
+      } */
+        }
+        out.println("</body></html>");
+      }
+    }
+  }
+
+  private final boolean classFilter(boolean checkTang, String s) {
+    return (checkTang || /*s.startsWith("org.apache.reef.tang.examples.timer") ||*/ !s.startsWith("org.apache.reef.tang"));
+  }
+
+  private void processDefaultAnnotation(Class<?> cmb) {
+    DefaultImplementation di = cmb.getAnnotation(DefaultImplementation.class);
+    // XXX hack: move to helper method + unify with rest of Tang!
+    if (di != null) {
+      String diName = di.value() == Void.class ? di.name() : ReflectionUtilities.getFullName(di.value());
+      ClassNode<?> cn = (ClassNode<?>) ch.getNode(cmb);
+      String cn_s = cn.getFullName();
+      if (!usages.contains(diName, cn_s)) {
+        usages.put(diName, cn_s);
+        if (!knownClasses.contains(cn)) {
+          knownClasses.add(cn);
+        }
+      }
+    }
+  }
+
+  private void processConfigurationModules(Class<?> cmb) {
+    for (Field f : cmb.getFields()) {
+      if (ReflectionUtilities.isCoercable(ConfigurationModule.class, f.getType())) {
+        int mod = f.getModifiers();
+        boolean ok = true;
+        if (Modifier.isPrivate(mod)) {
+          System.err.println("Found private ConfigurationModule " + f);
+          ok = false;
+        }
+        if (!Modifier.isFinal(mod)) {
+          System.err.println("Found non-final ConfigurationModule " + f);
+          ok = false;
+        }
+        if (!Modifier.isStatic(f.getModifiers())) {
+          System.err.println("Found non-static ConfigurationModule " + f);
+          ok = false;
+        }
+        if (ok) {
+//          System.err.println("OK: " + f);
+          try {
+            f.setAccessible(true);
+            String f_s = ReflectionUtilities.getFullName(f);
+            if (!modules.containsKey(f)) {
+              modules.put(f, (ConfigurationModule) (f.get(null)));
+              try {
+                modules.get(f).assertStaticClean();
+              } catch (ClassHierarchyException e) {
+                System.err.println(f_s + ": " + e.getMessage());
+              }
+              for (Entry<String, String> e : modules.get(f).toStringPairs()) {
+                //System.err.println("e: " + e.getKey() + "=" + e.getValue());
+                try {
+                  Node n = ch.getNode(e.getKey());
+                  if (!setters.contains(e.getKey(), f_s)) {
+                    setters.put(e.getKey(), f_s);
+                  }
+                  if (n instanceof ClassNode) {
+                    ClassNode<?> cn = (ClassNode<?>) n;
+                    if (!knownClasses.contains(cn)) {
+                      knownClasses.add(cn);
+                    }
+                  }
+                } catch (NameResolutionException ex) {
+
+                }
+                try {
+                  String s = e.getValue();
+                  Node n = ch.getNode(s);
+                  if (!usages.contains(ReflectionUtilities.getFullName(f), s)) {
+                    //  System.err.println("Added usage: " + ReflectionUtilities.getFullName(f) + "=" + s);
+                    usages.put(s, ReflectionUtilities.getFullName(f));
+                  }
+                  if (n instanceof ClassNode) {
+                    ClassNode<?> cn = (ClassNode<?>) n;
+                    if (!knownClasses.contains(cn)) {
+                      System.err.println("Added " + cn + " to known classes");
+                      knownClasses.add(cn);
+                    }
+                  }
+                } catch (NameResolutionException ex) {
+
+                }
+              }
+            }
+          } catch (ExceptionInInitializerError e) {
+            System.err.println("Field " + ReflectionUtilities.getFullName(f) + ": " + e.getCause().getMessage());
+          } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+          }
+        }
+      }
+    }
+  }
+
+  public Set<NamedParameterNode<?>> getNames() {
+    final Set<NamedParameterNode<?>> names = new MonotonicSet<>();
+    NodeVisitor<Node> v = new AbstractClassHierarchyNodeVisitor() {
+
+      @Override
+      public boolean visit(NamedParameterNode<?> node) {
+        names.add(node);
+        return true;
+      }
+
+      @Override
+      public boolean visit(PackageNode node) {
+        return true;
+      }
+
+      @Override
+      public boolean visit(ClassNode<?> node) {
+        return true;
+      }
+    };
+    Walk.preorder(v, null, ch.getNamespace());
+    return names;
+  }
+
+  public Set<Node> getNamesUsedAndSet() {
+    final Set<Node> names = new MonotonicSet<>();
+    final Set<String> userKeys = usages.keySet();
+    final Set<String> usedKeys = usages.values();
+    final Set<String> setterKeys = setters.keySet();
+    NodeVisitor<Node> v = new AbstractClassHierarchyNodeVisitor() {
+
+      @Override
+      public boolean visit(NamedParameterNode<?> node) {
+        names.add(node);
+        return true;
+      }
+
+      @Override
+      public boolean visit(PackageNode node) {
+        return true;
+      }
+
+      @Override
+      public boolean visit(ClassNode<?> node) {
+        String node_s = node.getFullName();
+        if (userKeys.contains(node_s)) {
+          names.add(node);
+        }
+        if (setterKeys.contains(node_s)) {
+          names.add(node);
+        }
+        if (usedKeys.contains(node_s)) {
+          if (!names.contains(node)) {
+            names.add(node);
+          }
+        }
+
+        return true;
+      }
+    };
+    Walk.preorder(v, null, ch.getNamespace());
+    return names;
+  }
+
+  public Set<String> getUsesOf(final Node name) {
+
+    return usages.getValuesForKey(name.getFullName());
+  }
+
+  public Set<String> getSettersOf(final Node name) {
+    return setters.getValuesForKey(name.getFullName());
+  }
+
+  public String toString(NamedParameterNode<?> n) {
+    StringBuilder sb = new StringBuilder("Name: " + n.getSimpleArgName() + " " + n.getFullName());
+    String[] instances = n.getDefaultInstanceAsStrings();
+    if (instances.length != 0) {
+      sb.append(" = ");
+      sb.append(join(",", instances));
+    }
+    if (!n.getDocumentation().equals("")) {
+      sb.append(" //" + n.getDocumentation());
+    }
+    return sb.toString();
+  }
+
+  private String join(String sep, String[] s) {
+    if (s.length > 0) {
+      StringBuffer sb = new StringBuffer(s[0]);
+      for (int i = 1; i < s.length; i++) {
+        sb.append(sep);
+        sb.append(s[i]);
+      }
+      return sb.toString();
+    } else {
+      return null;
+    }
+  }
+
+  public String cell(String s, String clazz) {
+    if (clazz.equals(USES) && s.length() > 0) {
+      s = "<em>Used by:</em><br>" + s;
+    }
+    if (clazz.equals(SETTERS) && s.length() > 0) {
+      s = "<em>Set by:</em><br>" + s;
+    }
+    if (clazz.equals(FULLNAME)) {
+      s = "&nbsp;" + s;
+    }
+    if (divs.contains(clazz)) {
+      return "<div class=\"" + clazz + "\">" + s + "</div>";
+    } else {
+      return "<span class=\"" + clazz + "\">" + s + "</span>";
+    }
+  }
+
+  public String cell(StringBuffer sb, String clazz) {
+    return cell(sb.toString(), clazz);
+  }
+
+  public String row(StringBuffer sb) {
+    return sb.toString();
+  }
+
+  public String toHtmlString(NamedParameterNode<?> n, String pack) {
+    String fullName = stripPrefix(n.getFullName(), pack);
+    StringBuffer sb = new StringBuffer();
+
+    sb.append("<div id='" + n.getFullName() + "' class='decl-margin'>");
+    sb.append("<div class='decl'>");
+
+    sb.append(cell(n.getSimpleArgName(), "simpleName") + cell(fullName, FULLNAME));
+    final String instance;
+    final String[] instances = n.getDefaultInstanceAsStrings();
+    if (instances.length != 0) {
+      StringBuffer sb2 = new StringBuffer(" = " + stripPrefix(instances[0], pack));
+      for (int i = 1; i < instances.length; i++) {
+        sb2.append("," + stripPrefix(instances[i], pack));
+      }
+      instance = sb2.toString();
+    } else {
+      instance = "";
+    }
+    sb.append(cell(instance, "instance"));
+    StringBuffer doc = new StringBuffer();
+    if (!n.getDocumentation().equals("")) {
+      doc.append(n.getDocumentation());
+    }
+    sb.append(cell(doc, "doc"));
+    StringBuffer uses = new StringBuffer();
+    for (String u : getUsesOf(n)) {
+      uses.append("<a href='#" + u + "'>" + stripPrefix(u, pack) + "</a> ");
+    }
+    sb.append(cell(uses, USES));
+    StringBuffer setters = new StringBuffer();
+    for (String f : getSettersOf(n)) {
+      setters.append("<a href='#" + f + "'>" + stripPrefix(f, pack) + "</a> ");
+    }
+    sb.append(cell(setters, SETTERS));
+    sb.append("</div>");
+    sb.append("</div>");
+    return row(sb);
+  }
+
+  public String toHtmlString(ClassNode<?> n, String pack) {
+    String fullName = stripPrefix(n.getFullName(), pack);
+
+    final String type;
+    try {
+      if (ch.classForName(n.getFullName()).isInterface()) {
+        type = "interface";
+      } else {
+        type = "class";
+      }
+    } catch (ClassNotFoundException e) {
+      throw new RuntimeException(e);
+    }
+    StringBuffer sb = new StringBuffer();
+
+    sb.append("<div class='decl-margin' id='" + n.getFullName() + "'>");
+    sb.append("<div class='decl'>");
+    sb.append(cell(type, "simpleName") + cell(fullName, FULLNAME));
+    final String instance;
+    if (n.getDefaultImplementation() != null) {
+      instance = " = " + stripPrefix(n.getDefaultImplementation(), pack);
+    } else {
+      instance = "";
+    }
+    sb.append(cell(instance, "simpleName"));
+    sb.append(cell("", "fullName")); // TODO: Documentation string?
+    StringBuffer uses = new StringBuffer();
+    for (String u : getUsesOf(n)) {
+      uses.append("<a href='#" + u + "'>" + stripPrefix(u, pack) + "</a> ");
+    }
+    sb.append(cell(uses, USES));
+    StringBuffer setters = new StringBuffer();
+    for (String f : getSettersOf(n)) {
+      setters.append("<a href='#" + f + "'>" + stripPrefix(f, pack) + "</a> ");
+    }
+    sb.append(cell(setters, SETTERS));
+    sb.append("</div>");
+    sb.append("</div>");
+    return row(sb);
+  }
+
+  public String startPackage(String pack) {
+    return "<div class=\"package\">" + pack + "</div>";
+//    return "<tr><td colspan='6'><br><b>" + pack + "</b></td></tr>";
+  }
+
+  public String endPackage() {
+    return "";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/TracingMonotonicMap.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/TracingMonotonicMap.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/TracingMonotonicMap.java
new file mode 100644
index 0000000..54e27be
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/TracingMonotonicMap.java
@@ -0,0 +1,25 @@
+/**
+ * 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.util;
+
+import java.util.Map;
+
+public interface TracingMonotonicMap<K, V> extends Map<K, V> {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/TracingMonotonicTreeMap.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/TracingMonotonicTreeMap.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/TracingMonotonicTreeMap.java
new file mode 100644
index 0000000..53c833b
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/TracingMonotonicTreeMap.java
@@ -0,0 +1,146 @@
+/**
+ * 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.util;
+
+import org.apache.reef.tang.BindLocation;
+import org.apache.reef.tang.implementation.StackBindLocation;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+public final class TracingMonotonicTreeMap<K, V> implements TracingMonotonicMap<K, V> {
+  private final MonotonicTreeMap<K, EntryImpl> innerMap = new MonotonicTreeMap<>();
+
+  @Override
+  public void clear() {
+    innerMap.clear();
+  }
+
+  @Override
+  public boolean containsKey(Object key) {
+    return innerMap.containsKey(key);
+  }
+
+  @Override
+  public boolean containsValue(Object value) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Set<java.util.Map.Entry<K, V>> entrySet() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public V get(Object key) {
+    EntryImpl ret = innerMap.get(key);
+    return ret != null ? ret.getKey() : null;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return innerMap.isEmpty();
+  }
+
+  @Override
+  public Set<K> keySet() {
+    return innerMap.keySet();
+  }
+
+  @Override
+  public V put(K key, V value) {
+    EntryImpl ret = innerMap.put(key, new EntryImpl(value, new StackBindLocation()));
+    return ret != null ? ret.getKey() : null;
+  }
+
+  @Override
+  public void putAll(Map<? extends K, ? extends V> m) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public V remove(Object key) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int size() {
+    return innerMap.size();
+  }
+
+  @Override
+  public Collection<V> values() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    final TracingMonotonicTreeMap that = (TracingMonotonicTreeMap) o;
+
+    if (innerMap != null ? !innerMap.equals(that.innerMap) : that.innerMap != null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    return innerMap != null ? innerMap.hashCode() : 0;
+  }
+
+  private class EntryImpl implements Map.Entry<V, BindLocation> {
+    private final V key;
+    private final BindLocation value;
+
+    EntryImpl(V key, BindLocation value) {
+      this.key = key;
+      this.value = value;
+    }
+
+    @Override
+    public V getKey() {
+      return key;
+    }
+
+    @Override
+    public BindLocation getValue() {
+      return value;
+    }
+
+    @Override
+    public BindLocation setValue(BindLocation value) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String toString() {
+      return "[" + key + "] set by " + value;
+    }
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/ValidateConfiguration.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/ValidateConfiguration.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/ValidateConfiguration.java
new file mode 100644
index 0000000..a990bad
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/ValidateConfiguration.java
@@ -0,0 +1,150 @@
+/**
+ * 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.util;
+
+import org.apache.reef.tang.*;
+import org.apache.reef.tang.annotations.Name;
+import org.apache.reef.tang.annotations.NamedParameter;
+import org.apache.reef.tang.annotations.Parameter;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.exceptions.InjectionException;
+import org.apache.reef.tang.formats.CommandLine;
+import org.apache.reef.tang.formats.ConfigurationFile;
+import org.apache.reef.tang.implementation.InjectionPlan;
+import org.apache.reef.tang.implementation.protobuf.ProtocolBufferClassHierarchy;
+import org.apache.reef.tang.proto.ClassHierarchyProto;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ValidateConfiguration {
+  private final String target;
+  private final File ch;
+  private final File inConfig;
+  private final File outConfig;
+//  @NamedParameter(short_name="ip")
+//  public class InjectionPlanOut implements Name<File> { }
+
+  @Inject
+  public ValidateConfiguration(
+      @Parameter(ClassHierarchyIn.class) File ch,
+      @Parameter(ConfigurationIn.class) File inConfig,
+      @Parameter(ConfigurationOut.class) File outConfig) {
+    this.target = null;
+    this.ch = ch;
+    this.inConfig = inConfig;
+    this.outConfig = outConfig;
+//    this.injectionPlan = injectionPlan;
+  }
+
+  @Inject
+  public ValidateConfiguration(
+      @Parameter(Target.class) String injectedClass,
+      @Parameter(ClassHierarchyIn.class) File ch,
+      @Parameter(ConfigurationIn.class) File inConfig,
+      @Parameter(ConfigurationOut.class) File outConfig) {
+    this.target = injectedClass;
+    this.ch = ch;
+    this.inConfig = inConfig;
+    this.outConfig = outConfig;
+  }
+
+  public static void main(String[] argv) throws IOException, BindException, InjectionException {
+    @SuppressWarnings("unchecked")
+    JavaConfigurationBuilder cb = Tang.Factory.getTang().newConfigurationBuilder((Class<? extends ExternalConstructor<?>>[]) new Class[]{FileParser.class});
+    CommandLine cl = new CommandLine(cb);
+    cl.processCommandLine(argv,
+        Target.class,
+        ClassHierarchyIn.class,
+        ConfigurationIn.class,
+        ConfigurationOut.class);
+    ValidateConfiguration bip = Tang.Factory.getTang().newInjector(cb.build()).getInstance(ValidateConfiguration.class);
+    bip.validatePlan();
+  }
+
+  public void validatePlan() throws IOException, BindException, InjectionException {
+
+    final Tang t = Tang.Factory.getTang();
+
+    final ClassHierarchyProto.Node root;
+    try (final InputStream chin = new FileInputStream(this.ch)) {
+      root = ClassHierarchyProto.Node.parseFrom(chin);
+    }
+
+    final ClassHierarchy ch = new ProtocolBufferClassHierarchy(root);
+    final ConfigurationBuilder cb = t.newConfigurationBuilder(ch);
+
+    if (!inConfig.canRead()) {
+      throw new IOException("Cannot read input config file: " + inConfig);
+    }
+
+    ConfigurationFile.addConfiguration(cb, inConfig);
+
+    if (target != null) {
+      final Injector i = t.newInjector(cb.build());
+      final InjectionPlan<?> ip = i.getInjectionPlan(target);
+      if (!ip.isInjectable()) {
+        throw new InjectionException(target + " is not injectable: " + ip.toCantInjectString());
+      }
+    }
+
+    ConfigurationFile.writeConfigurationFile(cb.build(), outConfig);
+
+//    Injector i = t.newInjector(cb.build());
+//    InjectionPlan<?> ip = i.getInjectionPlan(target);
+//    try (final OutputStream ipout = new FileOutputStream(injectionPlan)) {
+//      new ProtocolBufferInjectionPlan().serialize(ip).writeTo(ipout);
+//    }
+  }
+
+  public static class FileParser implements ExternalConstructor<File> {
+    private final File f;
+
+    @Inject
+    FileParser(String name) {
+      f = new File(name);
+    }
+
+    @Override
+    public File newInstance() {
+      return f;
+    }
+
+  }
+//  private final File injectionPlan;
+
+  @NamedParameter(short_name = "class")
+  public class Target implements Name<String> {
+  }
+
+  @NamedParameter(short_name = "ch")
+  public class ClassHierarchyIn implements Name<File> {
+  }
+
+  @NamedParameter(short_name = "in")
+  public class ConfigurationIn implements Name<File> {
+  }
+
+  @NamedParameter(short_name = "out")
+  public class ConfigurationOut implements Name<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/util/package-info.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/package-info.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/package-info.java
new file mode 100644
index 0000000..9e5cb43
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/package-info.java
@@ -0,0 +1,24 @@
+/**
+ * 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.
+ */
+/**
+ * Miscellaneous utilities, including command line static analysis (Tint), data structures and reflection stuff.
+ */
+
+package org.apache.reef.tang.util;
+

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/AbstractClassHierarchyNodeVisitor.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/AbstractClassHierarchyNodeVisitor.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/AbstractClassHierarchyNodeVisitor.java
new file mode 100644
index 0000000..5083a3d
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/AbstractClassHierarchyNodeVisitor.java
@@ -0,0 +1,79 @@
+/**
+ * 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.util.walk;
+
+import org.apache.reef.tang.types.ClassNode;
+import org.apache.reef.tang.types.NamedParameterNode;
+import org.apache.reef.tang.types.Node;
+import org.apache.reef.tang.types.PackageNode;
+
+/**
+ * Generic interface to traverse nodes of the class hierarchy.
+ * Dispatches between ClassNode, PackageNode, and NamedParameterNode types.
+ * It is used e.g. in Walk.preorder()
+ */
+public abstract class AbstractClassHierarchyNodeVisitor implements NodeVisitor<Node> {
+
+  /**
+   * Manually dispatch between different types of Nodes and call a proper visit() method.
+   * Currently dispatches between ClassNode, PackageNode, and NamedParameterNode types.
+   *
+   * @param node TANG configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   * @throws ClassCastException if Node is not one of ClassNode, PackageNode,
+   *                            or NamedParameterNode.
+   */
+  @Override
+  public boolean visit(final Node node) {
+    if (node instanceof ClassNode) {
+      return visit((ClassNode<?>) node);
+    } else if (node instanceof PackageNode) {
+      return visit((PackageNode) node);
+    } else if (node instanceof NamedParameterNode) {
+      return visit((NamedParameterNode<?>) node);
+    }
+    throw new ClassCastException(
+        "Node " + node.getClass() + " cannot be casted to one of the known subclasses."
+            + " Override this method to handle the case.");
+  }
+
+  /**
+   * Process current configuration node of ClassNode type.
+   *
+   * @param node Current configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  public abstract boolean visit(ClassNode<?> node);
+
+  /**
+   * Process current configuration node of PackageNode type.
+   *
+   * @param node Current configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  public abstract boolean visit(PackageNode node);
+
+  /**
+   * Process current configuration node of NamedParameterNode type.
+   *
+   * @param node Current configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  public abstract boolean visit(NamedParameterNode<?> node);
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/AbstractInjectionPlanNodeVisitor.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/AbstractInjectionPlanNodeVisitor.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/AbstractInjectionPlanNodeVisitor.java
new file mode 100644
index 0000000..6cf7131
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/AbstractInjectionPlanNodeVisitor.java
@@ -0,0 +1,80 @@
+/**
+ * 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.util.walk;
+
+import org.apache.reef.tang.implementation.Constructor;
+import org.apache.reef.tang.implementation.InjectionPlan;
+import org.apache.reef.tang.implementation.Subplan;
+import org.apache.reef.tang.implementation.java.JavaInstance;
+
+/**
+ * Generic interface to traverse nodes of the injection plan.
+ * Dispatches between Constructor, Subplan, RequiredSingleton, and JavaInstance types.
+ * It is used e.g. in Walk.preorder()
+ */
+public abstract class AbstractInjectionPlanNodeVisitor implements NodeVisitor<InjectionPlan<?>> {
+
+  /**
+   * Manually dispatch between different types of injection plan objects and call proper
+   * visit() method. Currently dispatches between Constructor, Subplan, RequiredSingleton,
+   * and JavaInstance types.
+   *
+   * @param node TANG injection plan node.
+   * @return true to proceed with the next node, false to cancel.
+   * @throws ClassCastException if argument is not one of Constructor, Subplan,
+   *                            RequiredSingleton, or JavaInstance.
+   */
+  @Override
+  public boolean visit(final InjectionPlan<?> node) {
+    if (node instanceof Constructor<?>) {
+      return visit((Constructor<?>) node);
+    } else if (node instanceof Subplan<?>) {
+      return visit((Subplan<?>) node);
+    } else if (node instanceof JavaInstance<?>) {
+      return visit((JavaInstance<?>) node);
+    }
+    throw new ClassCastException(
+        "Node " + node.getClass() + " cannot be casted to one of the known subclasses."
+            + " Override this method to handle the case.");
+  }
+
+  /**
+   * Process current injection plan node of Constructor type.
+   *
+   * @param node Current injection plan node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  public abstract boolean visit(Constructor<?> node);
+
+  /**
+   * Process current injection plan node of JavaInstance type.
+   *
+   * @param node Current injection plan node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  public abstract boolean visit(JavaInstance<?> node);
+
+  /**
+   * Process current injection plan node of Subplan type.
+   *
+   * @param node Current injection plan node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  public abstract boolean visit(Subplan<?> node);
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/EdgeVisitor.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/EdgeVisitor.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/EdgeVisitor.java
new file mode 100644
index 0000000..bc159a8
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/EdgeVisitor.java
@@ -0,0 +1,35 @@
+/**
+ * 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.util.walk;
+
+/**
+ * Generic interface to traverse edges of the configuration graph.
+ * It is used e.g. in Walk.preorder()
+ */
+public interface EdgeVisitor<T> {
+
+  /**
+   * Process current edge of the configuration graph.
+   *
+   * @param nodeFrom Current configuration node.
+   * @param nodeTo   Destination configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  boolean visit(T nodeFrom, T nodeTo);
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/NodeVisitor.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/NodeVisitor.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/NodeVisitor.java
new file mode 100644
index 0000000..cfe4f01
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/NodeVisitor.java
@@ -0,0 +1,35 @@
+/**
+ * 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.util.walk;
+
+
+/**
+ * Generic interface to traverse nodes of the configuration graph.
+ * It is used e.g. in Walk.preorder()
+ */
+public interface NodeVisitor<T> {
+
+  /**
+   * Process current configuration node.
+   *
+   * @param node Current configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  boolean visit(T node);
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/Walk.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/Walk.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/Walk.java
new file mode 100644
index 0000000..7b761ec
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/Walk.java
@@ -0,0 +1,66 @@
+/**
+ * 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.util.walk;
+
+import org.apache.reef.tang.types.Traversable;
+
+/**
+ * Generic graph traversal.
+ */
+public final class Walk {
+
+  /**
+   * This is a utility class that has only static methods - do not instantiate.
+   *
+   * @throws IllegalAccessException always.
+   */
+  private Walk() throws IllegalAccessException {
+    throw new IllegalAccessException("Do not instantiate this class.");
+  }
+
+  /**
+   * Traverse the configuration (sub)tree in preorder, starting from the given node.
+   * FIXME: handle loopy graphs correctly!
+   *
+   * @param nodeVisitor node visitor. Can be null.
+   * @param edgeVisitor edge visitor. Can be null.
+   * @param node        current node of the configuration tree.
+   * @return true if all nodes has been walked, false if visitor stopped early.
+   */
+  public static <T extends Traversable<T>> boolean preorder(
+      final NodeVisitor<T> nodeVisitor, final EdgeVisitor<T> edgeVisitor, final T node) {
+    if (nodeVisitor != null && nodeVisitor.visit(node)) {
+      if (edgeVisitor != null) {
+        for (final T child : node.getChildren()) {
+          if (!(edgeVisitor.visit(node, child)
+              && preorder(nodeVisitor, edgeVisitor, child))) {
+            return false;
+          }
+        }
+      } else {
+        for (final T child : node.getChildren()) {
+          if (!preorder(nodeVisitor, edgeVisitor, child)) {
+            return false;
+          }
+        }
+      }
+    }
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/GraphvizConfigVisitor.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/GraphvizConfigVisitor.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/GraphvizConfigVisitor.java
new file mode 100644
index 0000000..cecd04f
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/GraphvizConfigVisitor.java
@@ -0,0 +1,244 @@
+/**
+ * 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.util.walk.graphviz;
+
+import org.apache.reef.tang.Configuration;
+import org.apache.reef.tang.types.ClassNode;
+import org.apache.reef.tang.types.NamedParameterNode;
+import org.apache.reef.tang.types.Node;
+import org.apache.reef.tang.types.PackageNode;
+import org.apache.reef.tang.util.walk.AbstractClassHierarchyNodeVisitor;
+import org.apache.reef.tang.util.walk.EdgeVisitor;
+import org.apache.reef.tang.util.walk.Walk;
+
+/**
+ * Build a Graphviz representation of the configuration graph.
+ */
+public final class GraphvizConfigVisitor
+    extends AbstractClassHierarchyNodeVisitor implements EdgeVisitor<Node> {
+
+  /**
+   * Legend for the configuration graph in Graphviz format
+   */
+  private static final String LEGEND =
+      "  subgraph cluster_legend {\n"
+          + "    label=\"Legend\";\n"
+          + "    shape=box;\n"
+          + "    subgraph cluster_1 {\n"
+          + "      style=invis; label=\"\";\n"
+          + "      ex1l [shape=point, label=\"\"]; ex1r [shape=point, label=\"\"];\n"
+          + "      ex2l [shape=point, label=\"\"]; ex2r [shape=point, label=\"\"];\n"
+          + "      ex3l [shape=point, label=\"\"]; ex3r [shape=point, label=\"\"];\n"
+          + "      ex4l [shape=point, label=\"\"]; ex4r [shape=point, label=\"\"];\n"
+          + "      ex1l -> ex1r [style=solid, dir=back, arrowtail=diamond, label=\"contains\"];\n"
+          + "      ex2l -> ex2r [style=dashed, dir=back, arrowtail=empty, label=\"implements\"];\n"
+          + "      ex3l -> ex3r [style=\"dashed,bold\", dir=back, arrowtail=empty, label=\"external\"];\n"
+          + "      ex4l -> ex4r [style=solid, dir=back, arrowtail=normal, label=\"binds\"];\n"
+          + "    }\n"
+          + "    subgraph cluster_2 {\n"
+          + "      style=invis; label=\"\";\n"
+          + "      PackageNode [shape=folder];\n"
+          + "      ClassNode [shape=box];\n"
+          + "      Singleton [shape=box, style=filled];\n"
+          + "      NamedParameterNode [shape=oval];\n"
+          + "    }\n"
+          + "  }\n";
+
+  /**
+   * Accumulate string representation of the graph here.
+   */
+  private final transient StringBuilder graphStr = new StringBuilder(
+      "digraph ConfigMain {\n"
+          + "  rankdir=LR;\n");
+
+  /**
+   * Entire TANG configuration object.
+   */
+  private final transient Configuration config;
+
+  /**
+   * If true, plot IS-A edges for know implementations.
+   */
+  private final transient boolean showImpl;
+
+  /**
+   * Create a new TANG configuration visitor.
+   *
+   * @param aConfig     Entire TANG configuration object.
+   * @param aShowImpl   If true, plot IS-A edges for know implementations.
+   * @param aShowLegend If true, add legend to the plot.
+   */
+  public GraphvizConfigVisitor(final Configuration config,
+                               final boolean showImpl, final boolean showLegend) {
+    super();
+    this.config = config;
+    this.showImpl = showImpl;
+    if (showLegend) {
+      this.graphStr.append(LEGEND);
+    }
+  }
+
+  /**
+   * Produce a Graphviz DOT string for a given TANG configuration.
+   *
+   * @param config     TANG configuration object.
+   * @param showImpl   If true, plot IS-A edges for know implementations.
+   * @param showLegend If true, add legend to the plot.
+   * @return configuration graph represented as a string in Graphviz DOT format.
+   */
+  public static String getGraphvizString(final Configuration config,
+                                         final boolean showImpl, final boolean showLegend) {
+    final GraphvizConfigVisitor visitor = new GraphvizConfigVisitor(config, showImpl, showLegend);
+    final Node root = config.getClassHierarchy().getNamespace();
+    Walk.preorder(visitor, visitor, root);
+    return visitor.toString();
+  }
+
+  /**
+   * @return TANG configuration represented as a Graphviz DOT string.
+   */
+  @Override
+  public String toString() {
+    return this.graphStr.toString() + "}\n";
+  }
+
+  /**
+   * Process current class configuration node.
+   *
+   * @param node Current configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  @Override
+  public boolean visit(final ClassNode<?> node) {
+
+    this.graphStr
+        .append("  ")
+        .append(node.getName())
+        .append(" [label=\"")
+        .append(node.getName())
+        .append("\", shape=box")
+//            .append(config.isSingleton(node) ? ", style=filled" : "")
+        .append("];\n");
+
+    final ClassNode<?> boundImplNode = config.getBoundImplementation(node);
+    if (boundImplNode != null) {
+      this.graphStr
+          .append("  ")
+          .append(node.getName())
+          .append(" -> ")
+          .append(boundImplNode.getName())
+          .append(" [style=solid, dir=back, arrowtail=normal];\n");
+    }
+
+    for (final Object implNodeObj : node.getKnownImplementations()) {
+      final ClassNode<?> implNode = (ClassNode<?>) implNodeObj;
+      if (implNode != boundImplNode && implNode != node
+          && (implNode.isExternalConstructor() || this.showImpl)) {
+        this.graphStr
+            .append("  ")
+            .append(node.getName())
+            .append(" -> ")
+            .append(implNode.getName())
+            .append(" [style=\"dashed")
+            .append(implNode.isExternalConstructor() ? ",bold" : "")
+            .append("\", dir=back, arrowtail=empty];\n");
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Process current package configuration node.
+   *
+   * @param node Current configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  @Override
+  public boolean visit(final PackageNode node) {
+    if (!node.getName().isEmpty()) {
+      this.graphStr
+          .append("  ")
+          .append(node.getName())
+          .append(" [label=\"")
+          .append(node.getFullName())
+          .append("\", shape=folder];\n");
+    }
+    return true;
+  }
+
+  /**
+   * Process current configuration node for the named parameter.
+   *
+   * @param node Current configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  @Override
+  public boolean visit(final NamedParameterNode<?> node) {
+    this.graphStr
+        .append("  ")
+        .append(node.getName())
+        .append(" [label=\"")
+        .append(node.getSimpleArgName())           // parameter type, e.g. "Integer"
+        .append("\\n")
+        .append(node.getName())                    // short name, e.g. "NumberOfThreads"
+        .append(" = ")
+        .append(config.getNamedParameter(node))   // bound value, e.g. "16"
+        .append("\\n(default = ")
+        .append(instancesToString(node.getDefaultInstanceAsStrings())) // default value, e.g. "4"
+        .append(")\", shape=oval];\n");
+    return true;
+  }
+
+  private String instancesToString(String[] s) {
+    if (s.length == 0) {
+      return "null";
+    } else if (s.length == 1) {
+      return s[0];
+    } else {
+      StringBuffer sb = new StringBuffer("[" + s[0]);
+      for (int i = 1; i < s.length; i++) {
+        sb.append(",");
+        sb.append(s[i]);
+      }
+      sb.append("]");
+      return sb.toString();
+    }
+  }
+
+  /**
+   * Process current edge of the configuration graph.
+   *
+   * @param nodeFrom Current configuration node.
+   * @param nodeTo   Destination configuration node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  @Override
+  public boolean visit(final Node nodeFrom, final Node nodeTo) {
+    if (!nodeFrom.getName().isEmpty()) {
+      this.graphStr
+          .append("  ")
+          .append(nodeFrom.getName())
+          .append(" -> ")
+          .append(nodeTo.getName())
+          .append(" [style=solid, dir=back, arrowtail=diamond];\n");
+    }
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/GraphvizInjectionPlanVisitor.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/GraphvizInjectionPlanVisitor.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/GraphvizInjectionPlanVisitor.java
new file mode 100644
index 0000000..2c5f785
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/GraphvizInjectionPlanVisitor.java
@@ -0,0 +1,168 @@
+/**
+ * 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.util.walk.graphviz;
+
+import org.apache.reef.tang.implementation.Constructor;
+import org.apache.reef.tang.implementation.InjectionPlan;
+import org.apache.reef.tang.implementation.Subplan;
+import org.apache.reef.tang.implementation.java.JavaInstance;
+import org.apache.reef.tang.util.walk.AbstractInjectionPlanNodeVisitor;
+import org.apache.reef.tang.util.walk.EdgeVisitor;
+import org.apache.reef.tang.util.walk.Walk;
+
+/**
+ * Build a Graphviz representation of the injection plan graph.
+ */
+public final class GraphvizInjectionPlanVisitor
+    extends AbstractInjectionPlanNodeVisitor implements EdgeVisitor<InjectionPlan<?>> {
+  /**
+   * Legend for the configuration graph in Graphviz format
+   */
+  private static final String LEGEND =
+      "  subgraph cluster_legend {\n"
+          + "    shape=box;\n"
+          + "    label=\"Legend\";\n"
+          + "    Constructor [shape=box];\n"
+          + "    JavaInstance [shape=box, style=bold];\n"
+          + "    Subplan [shape=oval, style=dashed];\n"
+          + "    RequiredSingleton [shape=box, style=filled];\n"
+          + "    Subplan -> Constructor -> RequiredSingleton -> JavaInstance [style=invis];\n"
+          + "  }\n";
+
+  /**
+   * Accumulate string representation of the graph here.
+   */
+  private final transient StringBuilder graphStr =
+      new StringBuilder("digraph InjectionPlanMain {\n");
+
+  /**
+   * Create a new visitor to build a graphviz string for the injection plan.
+   *
+   * @param aShowLegend if true, show legend on the graph.
+   */
+  public GraphvizInjectionPlanVisitor(final boolean showLegend) {
+    if (showLegend) {
+      this.graphStr.append(LEGEND);
+    }
+    this.graphStr.append("subgraph cluster_main {\n  style=invis;\n");
+  }
+
+  /**
+   * Produce a Graphviz DOT string for a given TANG injection plan.
+   *
+   * @param injectionPlan TANG injection plan.
+   * @param showLegend    if true, show legend on the graph.
+   * @return Injection plan represented as a string in Graphviz DOT format.
+   */
+  public static String getGraphvizString(
+      final InjectionPlan<?> injectionPlan, final boolean showLegend) {
+    final GraphvizInjectionPlanVisitor visitor = new GraphvizInjectionPlanVisitor(showLegend);
+    Walk.preorder(visitor, visitor, injectionPlan);
+    return visitor.toString();
+  }
+
+  /**
+   * Process current injection plan node of Constructor type.
+   *
+   * @param node Current injection plan node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  @Override
+  public boolean visit(final Constructor<?> node) {
+    this.graphStr
+        .append("  \"")
+        .append(node.getClass())
+        .append('_')
+        .append(node.getNode().getName())
+        .append("\" [label=\"")
+        .append(node.getNode().getName())
+        .append("\", shape=box];\n");
+    return true;
+  }
+
+  /**
+   * Process current injection plan node of JavaInstance type.
+   *
+   * @param node Current injection plan node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  @Override
+  public boolean visit(final JavaInstance<?> node) {
+    this.graphStr
+        .append("  \"")
+        .append(node.getClass())
+        .append('_')
+        .append(node.getNode().getName())
+        .append("\" [label=\"")
+        .append(node.getNode().getName())
+        .append(" = ")
+        .append(node.getInstanceAsString())
+        .append("\", shape=box, style=bold];\n");
+    return true;
+  }
+
+  /**
+   * Process current injection plan node of Subplan type.
+   *
+   * @param node Current injection plan node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  @Override
+  public boolean visit(final Subplan<?> node) {
+    this.graphStr
+        .append("  \"")
+        .append(node.getClass())
+        .append('_')
+        .append(node.getNode().getName())
+        .append("\" [label=\"")
+        .append(node.getNode().getName())
+        .append("\", shape=oval, style=dashed];\n");
+    return true;
+  }
+
+  /**
+   * Process current edge of the injection plan.
+   *
+   * @param nodeFrom Current injection plan node.
+   * @param nodeTo   Destination injection plan node.
+   * @return true to proceed with the next node, false to cancel.
+   */
+  @Override
+  public boolean visit(final InjectionPlan<?> nodeFrom, final InjectionPlan<?> nodeTo) {
+    this.graphStr
+        .append("  \"")
+        .append(nodeFrom.getClass())
+        .append('_')
+        .append(nodeFrom.getNode().getName())
+        .append("\" -> \"")
+        .append(nodeTo.getClass())
+        .append('_')
+        .append(nodeTo.getNode().getName())
+        .append("\" [style=solid];\n");
+    return true;
+  }
+
+  /**
+   * @return TANG injection plan represented as a Graphviz DOT string.
+   */
+  @Override
+  public String toString() {
+    return this.graphStr.toString() + "}}\n";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/package-info.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/package-info.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/package-info.java
new file mode 100644
index 0000000..f0cadb6
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/graphviz/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * 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.
+ */
+/**
+ * produce Graphviz representation of TANG configuration graph and injection plan.
+ */
+package org.apache.reef.tang.util.walk.graphviz;

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/package-info.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/package-info.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/package-info.java
new file mode 100644
index 0000000..9478e74
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/util/walk/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * 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.
+ */
+/**
+ * Utility classes for configuration graph and injection plan traversal.
+ */
+package org.apache.reef.tang.util.walk;

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/proto/.gitignore
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/proto/.gitignore b/lang/java/reef-tang/tang/src/main/proto/.gitignore
new file mode 100644
index 0000000..8143e15
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/proto/.gitignore
@@ -0,0 +1 @@
+*.cs

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/proto/class_hierarchy.proto
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/proto/class_hierarchy.proto b/lang/java/reef-tang/tang/src/main/proto/class_hierarchy.proto
new file mode 100644
index 0000000..d304b62
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/proto/class_hierarchy.proto
@@ -0,0 +1,167 @@
+// 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.
+option java_package = "org.apache.reef.tang.proto";
+option java_outer_classname = "ClassHierarchyProto";
+//option java_generic_services = true;
+//option java_generate_equals_and_hash = true;
+
+/*
+  Node is the abstract base type for all the data encoded in a class hierarchy.
+  It is represented as an (un)tagged union, so only one of the three optional
+  fields can be non-null. 
+ */
+message Node {
+  /*
+    The short name of this node (e.g., a class name without the enclosing
+    package, or a package name without the name of the enclosing package.
+   */
+  required string name = 1;
+  /*
+    For some languages it can be difficult to map from a list of nodes to
+    the correct short name, so for now, the full name is (redundantly)
+    encoded here.  In Java, the full name is a bunch of short names that
+    have been concatenated with "." and/or "$".
+   */
+  required string full_name = 2;
+  /*
+     Exactly one of the next three fields must be defined.  See below for
+     their documentation.
+   */
+  optional ClassNode class_node = 3;
+  optional NamedParameterNode named_parameter_node = 4;
+  optional PackageNode package_node = 5;
+  /*
+     Class hierarchy nodes are arranged in a tree that mirrors the language
+     namespaces / packages that contain class names (e.g., Java packages,
+     C++ namespaces).
+     
+     A NamedParameterNode cannot have any children.  A ClassNode can not have
+     any package nodes as children.  PackageNodes cannot have PackageNodes as
+     children.
+   */
+  repeated Node children = 6;
+}
+
+message ClassNode {
+  /*
+     Some classes cannot be injected for language-specific reasons.  For
+     example, Java's non-static inner classes need a reference to the outer
+     class in order to be instantiated.  Set this boolean to false if there
+     is some reason why Tang cannot possibly inject this class.
+   */
+  required bool is_injection_candidate = 1;
+  /*
+     This field will be set to true if this class is a Tang
+     ExternalConstructor implementation.
+     
+     If this is set to true, then some other class *must* contain this 
+     ClassNode's name in its impl_full_names field.
+   */
+  required bool is_external_constructor = 2;
+  /*
+     This field will be set to true if this class is annotated as a Tang
+     Unit.
+   */
+  required bool is_unit = 3;
+  /*
+     A list of all the constructors that are defined by this class and 
+     annotated to be injectable.
+   */
+  repeated ConstructorDef InjectableConstructors = 4;
+  /*
+     A list of all the other constructors (so that they can be registered
+     as legacy constructors if the configuration tells us to treat them
+     as though they were annotated with an Inject).
+   */
+  repeated ConstructorDef OtherConstructors = 5;
+  /*
+     A list of all the ClassNodes that implement this class, including
+     legacy constructors. 
+   */
+  repeated string impl_full_names = 6;
+  optional string default_implementation = 7;
+}
+
+message NamedParameterNode {
+  /*
+     The short name (Node.name) of the type of argument this node names.
+   */
+  required string simple_arg_class_name = 1;
+  /*
+     The full name (Node.full_name) of the type of argument that this node
+     names.
+   */
+  required string full_arg_class_name = 2;
+  required bool is_set = 3;
+  required bool is_list = 4;
+  /*
+     An optional human readable documentation string describing the purpose
+     of this NamedParameter.
+   */
+  optional string documentation = 5;
+  /*
+     A shorter name for this parameter.  This is used for command line
+     processing. (So utilities that see --short_name=xxx will set this
+     NamedParameter to "xxx", for example).  
+   */
+  optional string short_name = 6;
+  /*
+     Optional named parameters must specify default values.  This must be
+     a string encoding of that value.  In order for this to be valid, Tang
+     must be able to parse the default value.  This is achieved by looking
+     for a parser for the ClassNode associated with full_arg_class_name.
+     
+     By default, Tang knows how to parse Strings (it simply copies them
+     unchanged), class names for the language environment Tang is running
+     inside of, and primitive types, such as int and float.
+     
+     Parsers of other types must be explicitly registered by the code that
+     bootstraps Tang.
+   */
+  // calling this "default_instance" breaks protoc.
+  repeated string instance_default = 7;
+}
+
+message PackageNode {
+  /*
+     Intentionally left blank.  Packages don't have any interesting
+     attributes except their names and children.
+   */
+}
+
+message ConstructorDef {
+  /*
+     The full name of the class this constructor returns.
+   */
+  required string full_class_name = 1;
+  /*
+     A (potentially empty) list of arguments required by this constructor.
+   */
+  repeated ConstructorArg args = 2;
+}
+message ConstructorArg {
+  /*
+     The full name of the class that should be passed into this
+     constructor argument.
+   */
+  required string full_arg_class_name = 1;
+  /*
+     The named parameter (if any) that this argument is annotated with.
+   */
+  optional string named_parameter_name = 2;
+  required bool is_injection_future = 3;
+}
\ 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/proto/injection_plan.proto
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/proto/injection_plan.proto b/lang/java/reef-tang/tang/src/main/proto/injection_plan.proto
new file mode 100644
index 0000000..9ca1a3d
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/proto/injection_plan.proto
@@ -0,0 +1,38 @@
+// 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.
+option java_package = "org.apache.reef.tang.proto";
+option java_outer_classname = "InjectionPlanProto";
+//option java_generic_services = true;
+//option java_generate_equals_and_hash = true;
+
+message InjectionPlan {
+  required string name = 1;
+  optional Constructor constructor = 2;
+  optional Instance instance = 3;
+  optional Subplan subplan = 4;
+}
+
+message Subplan {
+  optional sint32 selected_plan = 1;
+  repeated InjectionPlan plans = 2;
+}
+message Constructor {
+  repeated InjectionPlan args = 1;
+}
+message Instance {
+  required string value = 1;
+}

http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/test/java/org/apache/reef/tang/ClassHierarchyDeserializationTest.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/test/java/org/apache/reef/tang/ClassHierarchyDeserializationTest.java b/lang/java/reef-tang/tang/src/test/java/org/apache/reef/tang/ClassHierarchyDeserializationTest.java
new file mode 100644
index 0000000..1091ae4
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/test/java/org/apache/reef/tang/ClassHierarchyDeserializationTest.java
@@ -0,0 +1,112 @@
+/**
+ * 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;
+
+import org.apache.reef.tang.exceptions.NameResolutionException;
+import org.apache.reef.tang.formats.AvroConfigurationSerializer;
+import org.apache.reef.tang.formats.ConfigurationSerializer;
+import org.apache.reef.tang.implementation.protobuf.ProtocolBufferClassHierarchy;
+import org.apache.reef.tang.proto.ClassHierarchyProto;
+import org.apache.reef.tang.types.NamedParameterNode;
+import org.apache.reef.tang.types.Node;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
+
+/**
+ * Test case for class hierarchy deserialization.
+ */
+public class ClassHierarchyDeserializationTest {
+
+  @Test
+  public void testDeserializationForTasks() {
+    try (final InputStream chin = Thread.currentThread().getContextClassLoader()
+        .getResourceAsStream("Task.bin")) {
+      final ClassHierarchyProto.Node root = ClassHierarchyProto.Node.parseFrom(chin); // A
+      final ClassHierarchy ch = new ProtocolBufferClassHierarchy(root);
+      Node n1 = ch.getNode("Microsoft.Reef.Tasks.StreamTask1, Microsoft.Reef.Tasks, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
+      Assert.assertTrue(n1.getFullName().equals("Microsoft.Reef.Tasks.StreamTask1, Microsoft.Reef.Tasks, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"));
+
+      Node n2 = ch.getNode("Microsoft.Reef.Tasks.HelloTask, Microsoft.Reef.Tasks, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
+      Assert.assertTrue(n2.getFullName().equals("Microsoft.Reef.Tasks.HelloTask, Microsoft.Reef.Tasks, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"));
+
+      final ConfigurationBuilder taskConfigurationBuilder1 = Tang.Factory.getTang()
+          .newConfigurationBuilder(ch);
+
+      final ConfigurationBuilder taskConfigurationBuilder2 = Tang.Factory.getTang()
+          .newConfigurationBuilder(ch);
+    } catch (final IOException e) {
+      final String message = "Unable to load class hierarchy.";
+      throw new RuntimeException(message, e);
+    } catch (final NameResolutionException e) {
+      final String message = "Unable to get node from class hierarchy.";
+      throw new RuntimeException(message, e);
+    }
+  }
+
+  @Test
+  public void testDeserializationForEvent() {
+    try (final InputStream chin = Thread.currentThread().getContextClassLoader()
+        .getResourceAsStream("Event.bin")) {
+      final ClassHierarchyProto.Node root = ClassHierarchyProto.Node.parseFrom(chin);
+      final ClassHierarchy ch = new ProtocolBufferClassHierarchy(root);
+      final ConfigurationBuilder taskConfigurationBuilder = Tang.Factory.getTang()
+          .newConfigurationBuilder(ch);
+    } catch (final Exception e) {
+      final String message = "Unable to load class hierarchy.";
+      throw new RuntimeException(message, e);
+    }
+  }
+
+  @Test
+  //Test bindSetEntry(NamedParameterNode<Set<T>> iface, String impl) in ConfigurationBuilderImpl with deserialized class hierarchy
+  public void testBindSetEntryWithSetOfT() throws IOException {
+    final ClassHierarchy ns1 = Tang.Factory.getTang().getDefaultClassHierarchy();
+    ns1.getNode(SetOfClasses.class.getName());
+    final ClassHierarchy ns2 = new ProtocolBufferClassHierarchy(ProtocolBufferClassHierarchy.serialize(ns1));
+    final ConfigurationBuilder cb = Tang.Factory.getTang().newConfigurationBuilder(ns2);
+
+    final NamedParameterNode<Set<Number>> n2 = (NamedParameterNode<Set<Number>>) ns1.getNode(SetOfClasses.class.getName());
+    final Node fn = ns1.getNode(Float.class.getName());
+    cb.bindSetEntry(n2, fn);
+
+    final ConfigurationSerializer serializer = new AvroConfigurationSerializer();
+    final Configuration c = serializer.fromString(serializer.toString(cb.build()), ns2);
+  }
+
+  @Test
+  //Test public <T> void bindParameter(NamedParameterNode<T> name, String value) in ConfigurationBuilderImpl with deserialized class hierarchy
+  public void testBindSetEntryWithSetOfString() throws IOException {
+    final ClassHierarchy ns1 = Tang.Factory.getTang().getDefaultClassHierarchy();
+    ns1.getNode(SetOfStrings.class.getName());
+    final ClassHierarchy ns2 = new ProtocolBufferClassHierarchy(ProtocolBufferClassHierarchy.serialize(ns1));
+    final ConfigurationBuilder cb = Tang.Factory.getTang().newConfigurationBuilder(ns2);
+    cb.bindSetEntry(SetOfStrings.class.getName(), "four");
+    cb.bindSetEntry(SetOfStrings.class.getName(), "five");
+
+    final NamedParameterNode<Set<String>> n2 = (NamedParameterNode<Set<String>>) ns1.getNode(SetOfStrings.class.getName());
+    cb.bindSetEntry(n2, "six");
+
+    final ConfigurationSerializer serializer = new AvroConfigurationSerializer();
+    final Configuration c = serializer.fromString(serializer.toString(cb.build()), ns2);
+  }
+}