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 = " " + 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);
+ }
+}