You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@reef.apache.org by we...@apache.org on 2015/01/23 00:46:47 UTC
[14/51] [partial] incubator-reef git commit: [REEF-93] Move java
sources to lang/java
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/formats/package-info.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/formats/package-info.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/formats/package-info.java
new file mode 100644
index 0000000..0513a03
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/formats/package-info.java
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * Tang format classes encode and decode information that Tang gathers at
+ * runtime. Such information comes in three forms: ClassHierarchy data
+ * that is derived directly from compiled application code, Configuration
+ * data that has been typechecked, and is derived from various user inputs,
+ * such as configuration files and command line arguments, and finally,
+ * InjectionPlans which encode the constructor invocations that Tang would
+ * make when instantiating a particular class.
+ */
+package org.apache.reef.tang.formats;
+
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationBuilderImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationBuilderImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationBuilderImpl.java
new file mode 100644
index 0000000..a9ead69
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationBuilderImpl.java
@@ -0,0 +1,378 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.*;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.exceptions.NameResolutionException;
+import org.apache.reef.tang.exceptions.ParseException;
+import org.apache.reef.tang.implementation.java.ClassHierarchyImpl;
+import org.apache.reef.tang.types.*;
+import org.apache.reef.tang.util.MonotonicMultiMap;
+import org.apache.reef.tang.util.TracingMonotonicMap;
+import org.apache.reef.tang.util.TracingMonotonicTreeMap;
+
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class ConfigurationBuilderImpl implements ConfigurationBuilder {
+ public final static String IMPORT = "import";
+ public final static String INIT = "<init>";
+ final TracingMonotonicMap<ClassNode<?>, ClassNode<?>> boundImpls = new TracingMonotonicTreeMap<>();
+ final TracingMonotonicMap<ClassNode<?>, ClassNode<? extends ExternalConstructor<?>>> boundConstructors = new TracingMonotonicTreeMap<>();
+ final Map<NamedParameterNode<?>, String> namedParameters = new TracingMonotonicTreeMap<>();
+ final Map<ClassNode<?>, ConstructorDef<?>> legacyConstructors = new TracingMonotonicTreeMap<>();
+ final MonotonicMultiMap<NamedParameterNode<Set<?>>, Object> boundSetEntries = new MonotonicMultiMap<>();
+ final TracingMonotonicMap<NamedParameterNode<List<?>>, List<Object>> boundLists = new TracingMonotonicTreeMap<>();
+ // TODO: None of these should be public! - Move to configurationBuilder. Have
+ // that wrap itself
+ // in a sane Configuration interface...
+ // TODO: Should be final again!
+ public ClassHierarchy namespace;
+
+ protected ConfigurationBuilderImpl() {
+ this.namespace = Tang.Factory.getTang().getDefaultClassHierarchy();
+ }
+
+ protected ConfigurationBuilderImpl(ClassHierarchy namespace) {
+ this.namespace = namespace;
+ }
+
+ protected ConfigurationBuilderImpl(URL[] jars, Configuration[] confs, Class<? extends ExternalConstructor<?>>[] parsers)
+ throws BindException {
+ this.namespace = Tang.Factory.getTang().getDefaultClassHierarchy(jars, parsers);
+ for (Configuration tc : confs) {
+ addConfiguration(((ConfigurationImpl) tc));
+ }
+ }
+
+ protected ConfigurationBuilderImpl(ConfigurationBuilderImpl t) {
+ this.namespace = t.getClassHierarchy();
+ try {
+ addConfiguration(t.getClassHierarchy(), t);
+ } catch (BindException e) {
+ throw new IllegalStateException("Could not copy builder", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected ConfigurationBuilderImpl(URL... jars) throws BindException {
+ this(jars, new Configuration[0], new Class[0]);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected ConfigurationBuilderImpl(Configuration... confs) throws BindException {
+ this(new URL[0], confs, new Class[0]);
+ }
+
+ @Override
+ public void addConfiguration(Configuration conf) throws BindException {
+ // XXX remove cast!
+ addConfiguration(conf.getClassHierarchy(), ((ConfigurationImpl) conf).builder);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> void addConfiguration(ClassHierarchy ns, ConfigurationBuilderImpl builder)
+ throws BindException {
+ namespace = namespace.merge(ns);
+ if ((namespace instanceof ClassHierarchyImpl || builder.namespace instanceof ClassHierarchyImpl)) {
+ if ((namespace instanceof ClassHierarchyImpl && builder.namespace instanceof ClassHierarchyImpl)) {
+ ((ClassHierarchyImpl) namespace).parameterParser
+ .mergeIn(((ClassHierarchyImpl) builder.namespace).parameterParser);
+ } else {
+ throw new IllegalArgumentException("Attempt to merge Java and non-Java class hierarchy! Not supported.");
+ }
+ }
+
+ for (ClassNode<?> cn : builder.boundImpls.keySet()) {
+ bind(cn.getFullName(), builder.boundImpls.get(cn).getFullName());
+ }
+ for (ClassNode<?> cn : builder.boundConstructors.keySet()) {
+ bind(cn.getFullName(), builder.boundConstructors.get(cn).getFullName());
+ }
+ // The namedParameters set contains the strings that can be used to
+ // instantiate new
+ // named parameter instances. Create new ones where we can.
+ for (NamedParameterNode<?> np : builder.namedParameters.keySet()) {
+ bind(np.getFullName(), builder.namedParameters.get(np));
+ }
+ for (ClassNode<?> cn : builder.legacyConstructors.keySet()) {
+ registerLegacyConstructor(cn, builder.legacyConstructors.get(cn)
+ .getArgs());
+ }
+ for (Entry<NamedParameterNode<Set<?>>, Object> e : builder.boundSetEntries) {
+ String name = ((NamedParameterNode<Set<T>>) (NamedParameterNode<?>) e.getKey()).getFullName();
+ if (e.getValue() instanceof Node) {
+ bindSetEntry(name, (Node) e.getValue());
+ } else if (e.getValue() instanceof String) {
+ bindSetEntry(name, (String) e.getValue());
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+ // The boundLists set contains bound lists with their target NamedParameters
+ for (NamedParameterNode<List<?>> np : builder.boundLists.keySet()) {
+ bindList(np.getFullName(), builder.boundLists.get(np));
+ }
+ }
+
+ @Override
+ public ClassHierarchy getClassHierarchy() {
+ return namespace;
+ }
+
+ @Override
+ public void registerLegacyConstructor(ClassNode<?> c,
+ final ConstructorArg... args) throws BindException {
+ String cn[] = new String[args.length];
+ for (int i = 0; i < args.length; i++) {
+ cn[i] = args[i].getType();
+ }
+ registerLegacyConstructor(c.getFullName(), cn);
+ }
+
+ @Override
+ public void registerLegacyConstructor(String s, final String... args)
+ throws BindException {
+ ClassNode<?> cn = (ClassNode<?>) namespace.getNode(s);
+ ClassNode<?>[] cnArgs = new ClassNode[args.length];
+ for (int i = 0; i < args.length; i++) {
+ cnArgs[i] = (ClassNode<?>) namespace.getNode(args[i]);
+ }
+ registerLegacyConstructor(cn, cnArgs);
+ }
+
+ @Override
+ public void registerLegacyConstructor(ClassNode<?> cn,
+ final ClassNode<?>... args) throws BindException {
+ legacyConstructors.put(cn, cn.getConstructorDef(args));
+ }
+
+ @Override
+ public <T> void bind(String key, String value) throws BindException {
+ Node n = namespace.getNode(key);
+ if (n instanceof NamedParameterNode) {
+ bindParameter((NamedParameterNode<?>) n, value);
+ } else if (n instanceof ClassNode) {
+ Node m = namespace.getNode(value);
+ bind((ClassNode<?>) n, (ClassNode<?>) m);
+ } else {
+ throw new IllegalStateException("getNode() returned " + n + " which is neither a ClassNode nor a NamedParameterNode");
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void bind(Node key, Node value) throws BindException {
+ if (key instanceof NamedParameterNode) {
+ bindParameter((NamedParameterNode<?>) key, value.getFullName());
+ } else if (key instanceof ClassNode) {
+ ClassNode<?> k = (ClassNode<?>) key;
+ if (value instanceof ClassNode) {
+ ClassNode<?> val = (ClassNode<?>) value;
+ if (val.isExternalConstructor() && !k.isExternalConstructor()) {
+ bindConstructor(k, (ClassNode) val);
+ } else {
+ bindImplementation(k, (ClassNode) val);
+ }
+ }
+ }
+ }
+
+ public <T> void bindImplementation(ClassNode<T> n, ClassNode<? extends T> m)
+ throws BindException {
+ if (namespace.isImplementation(n, m)) {
+ boundImpls.put(n, m);
+ } else {
+ throw new IllegalArgumentException("Class" + m + " does not extend " + n);
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public <T> void bindParameter(NamedParameterNode<T> name, String value)
+ throws BindException {
+ /* Parse and discard value; this is just for type checking */
+ if (namespace instanceof JavaClassHierarchy) {
+ ((JavaClassHierarchy) namespace).parse(name, value);
+ }
+ if (name.isSet()) {
+ bindSetEntry((NamedParameterNode) name, value);
+ } else {
+ namedParameters.put(name, value);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void bindSetEntry(String iface, String impl)
+ throws BindException {
+ boundSetEntries.put((NamedParameterNode<Set<?>>) namespace.getNode(iface), impl);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void bindSetEntry(String iface, Node impl)
+ throws BindException {
+ boundSetEntries.put((NamedParameterNode<Set<?>>) namespace.getNode(iface), impl);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> void bindSetEntry(NamedParameterNode<Set<T>> iface, String impl)
+ throws BindException {
+ if (namespace instanceof ClassHierarchyImpl) {
+ JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace;
+ try {
+ javanamespace.parse(iface, impl);
+ } catch (ParseException e) {
+ throw new IllegalStateException("Could not parse " + impl + " which was passed to " + iface);
+ }
+ }
+ boundSetEntries.put((NamedParameterNode<Set<?>>) (NamedParameterNode<?>) iface, impl);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> void bindSetEntry(NamedParameterNode<Set<T>> iface, Node impl)
+ throws BindException {
+ boundSetEntries.put((NamedParameterNode<Set<?>>) (NamedParameterNode<?>) iface, impl);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> void bindList(NamedParameterNode<List<T>> iface, List implList) {
+ // Check parsability of list items
+ for (Object item : implList) {
+ if (item instanceof String) {
+ JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace;
+ try {
+ // Just for parsability checking.
+ javanamespace.parse(iface, (String) item);
+ } catch (ParseException e) {
+ throw new IllegalStateException("Could not parse " + item + " which was passed to " + iface);
+ }
+ }
+ }
+ boundLists.put((NamedParameterNode<List<?>>) (NamedParameterNode<?>) iface, implList);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void bindList(String iface, List implList) {
+ NamedParameterNode<List<?>> ifaceNode = (NamedParameterNode<List<?>>) namespace.getNode(iface);
+ // Check parsability of list items
+ for (Object item : implList) {
+ if (item instanceof String) {
+ JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace;
+ try {
+ // Just for parsability checking.
+ javanamespace.parse(ifaceNode, (String) item);
+ } catch (ParseException e) {
+ throw new IllegalStateException("Could not parse " + item + " which was passed to " + iface);
+ }
+ }
+ }
+ boundLists.put(ifaceNode, implList);
+ }
+
+ @Override
+ public <T> void bindConstructor(ClassNode<T> k,
+ ClassNode<? extends ExternalConstructor<? extends T>> v) {
+ boundConstructors.put(k, v);
+ }
+
+ @Override
+ public ConfigurationImpl build() {
+ return new ConfigurationImpl(new ConfigurationBuilderImpl(this));
+ }
+
+ @Override
+ public String classPrettyDefaultString(String longName) throws NameResolutionException {
+ final NamedParameterNode<?> param = (NamedParameterNode<?>) namespace
+ .getNode(longName);
+ return param.getSimpleArgName() + "=" + join(",", param.getDefaultInstanceAsStrings());
+ }
+
+ private String join(String sep, String[] s) {
+ if (s.length == 0) {
+ return null;
+ } else {
+ StringBuilder sb = new StringBuilder(s[0]);
+ for (int i = 1; i < s.length; i++) {
+ sb.append(sep);
+ sb.append(s[i]);
+ }
+ return sb.toString();
+ }
+ }
+
+ @Override
+ public String classPrettyDescriptionString(String fullName)
+ throws NameResolutionException {
+ final NamedParameterNode<?> param = (NamedParameterNode<?>) namespace
+ .getNode(fullName);
+ return param.getDocumentation() + "\n" + param.getFullName();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ConfigurationBuilderImpl that = (ConfigurationBuilderImpl) o;
+
+ if (boundConstructors != null ? !boundConstructors.equals(that.boundConstructors) : that.boundConstructors != null) {
+ return false;
+ }
+ if (boundImpls != null ? !boundImpls.equals(that.boundImpls) : that.boundImpls != null) {
+ return false;
+ }
+ if (boundSetEntries != null ? !boundSetEntries.equals(that.boundSetEntries) : that.boundSetEntries != null) {
+ return false;
+ }
+ if (boundLists != null ? !boundLists.equals(that.boundLists) : that.boundLists != null) {
+ return false;
+ }
+ if (legacyConstructors != null ? !legacyConstructors.equals(that.legacyConstructors) : that.legacyConstructors != null) {
+ return false;
+ }
+ if (namedParameters != null ? !namedParameters.equals(that.namedParameters) : that.namedParameters != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = boundImpls != null ? boundImpls.hashCode() : 0;
+ result = 31 * result + (boundConstructors != null ? boundConstructors.hashCode() : 0);
+ result = 31 * result + (namedParameters != null ? namedParameters.hashCode() : 0);
+ result = 31 * result + (legacyConstructors != null ? legacyConstructors.hashCode() : 0);
+ result = 31 * result + (boundSetEntries != null ? boundSetEntries.hashCode() : 0);
+ result = 31 * result + (boundLists != null ? boundLists.hashCode() : 0);
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationImpl.java
new file mode 100644
index 0000000..076435f
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ConfigurationImpl.java
@@ -0,0 +1,143 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.ClassHierarchy;
+import org.apache.reef.tang.Configuration;
+import org.apache.reef.tang.ConfigurationBuilder;
+import org.apache.reef.tang.ExternalConstructor;
+import org.apache.reef.tang.types.ClassNode;
+import org.apache.reef.tang.types.ConstructorDef;
+import org.apache.reef.tang.types.NamedParameterNode;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class ConfigurationImpl implements Configuration {
+ final ConfigurationBuilderImpl builder;
+
+ protected ConfigurationImpl(final ConfigurationBuilderImpl builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public String getNamedParameter(final NamedParameterNode<?> np) {
+ return builder.namedParameters.get(np);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> ClassNode<ExternalConstructor<T>> getBoundConstructor(
+ final ClassNode<T> cn) {
+ return (ClassNode<ExternalConstructor<T>>) builder.boundConstructors.get(cn);
+ }
+
+ @Override
+ public Set<ClassNode<?>> getBoundImplementations() {
+ return builder.boundImpls.keySet();
+ }
+
+ @Override
+ public Set<ClassNode<?>> getBoundConstructors() {
+ return builder.boundConstructors.keySet();
+ }
+
+ @Override
+ public Set<NamedParameterNode<?>> getNamedParameters() {
+ return builder.namedParameters.keySet();
+ }
+
+ @Override
+ public Set<ClassNode<?>> getLegacyConstructors() {
+ return builder.legacyConstructors.keySet();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> ClassNode<T> getBoundImplementation(final ClassNode<T> cn) {
+ return (ClassNode<T>) builder.boundImpls.get(cn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> ConstructorDef<T> getLegacyConstructor(final ClassNode<T> cn) {
+ return (ConstructorDef<T>) builder.legacyConstructors.get(cn);
+ }
+
+ @Override
+ public ConfigurationBuilder newBuilder() {
+ return builder.build().builder;
+ }
+
+ @Override
+ public ClassHierarchy getClassHierarchy() {
+ return builder.namespace;
+ }
+
+ @Override
+ public Set<Object> getBoundSet(NamedParameterNode<Set<?>> np) {
+ return this.builder.boundSetEntries.getValuesForKey(np);
+ }
+
+ @Override
+ public List<Object> getBoundList(NamedParameterNode<List<?>> np) {
+ return this.builder.boundLists.get(np);
+ }
+
+ @Override
+ public Iterable<Entry<NamedParameterNode<Set<?>>, Object>> getBoundSets() {
+ return new Iterable<Entry<NamedParameterNode<Set<?>>, Object>>() {
+
+ @Override
+ public Iterator<Entry<NamedParameterNode<Set<?>>, Object>> iterator() {
+ return builder.boundSetEntries.iterator();
+ }
+ };
+ }
+
+ @Override
+ public Set<NamedParameterNode<List<?>>> getBoundLists() {
+ return builder.boundLists.keySet();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final ConfigurationImpl that = (ConfigurationImpl) o;
+
+ if (builder != null ? !builder.equals(that.builder) : that.builder != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return builder != null ? builder.hashCode() : 0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Constructor.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Constructor.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Constructor.java
new file mode 100644
index 0000000..b41682d
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Constructor.java
@@ -0,0 +1,186 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.types.ClassNode;
+import org.apache.reef.tang.types.ConstructorDef;
+
+import java.util.*;
+
+final public class Constructor<T> extends InjectionPlan<T> {
+
+ final ConstructorDef<T> constructor;
+ final InjectionPlan<?>[] args;
+ final int numAlternatives;
+ final boolean isAmbiguous;
+ final boolean isInjectable;
+
+ public Constructor(final ClassNode<T> classNode,
+ final ConstructorDef<T> constructor, final InjectionPlan<?>[] args) {
+ super(classNode);
+ this.constructor = constructor;
+ this.args = args;
+ int curAlternatives = 1;
+ boolean curAmbiguous = false;
+ boolean curInjectable = true;
+ for (final InjectionPlan<?> plan : args) {
+ curAlternatives *= plan.getNumAlternatives();
+ curAmbiguous |= plan.isAmbiguous();
+ curInjectable &= plan.isInjectable();
+ }
+ this.numAlternatives = curAlternatives;
+ this.isAmbiguous = curAmbiguous;
+ this.isInjectable = curInjectable;
+ }
+
+ public InjectionPlan<?>[] getArgs() {
+ return args;
+ }
+
+ /**
+ * Get child elements of the injection plan tree.
+ * This method is inherited from the Traversable interface.
+ * TODO: use ArrayList internally (and maybe for input, too).
+ *
+ * @return A list of injection plans for the constructor's arguments.
+ */
+ @Override
+ public Collection<InjectionPlan<?>> getChildren() {
+ return Collections.unmodifiableList(Arrays.asList(this.args));
+ }
+
+ public ConstructorDef<T> getConstructorDef() {
+ return constructor;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public ClassNode<T> getNode() {
+ return (ClassNode<T>) node;
+ }
+
+ @Override
+ public int getNumAlternatives() {
+ return numAlternatives;
+ }
+
+ @Override
+ public boolean isAmbiguous() {
+ return isAmbiguous;
+ }
+
+ @Override
+ public boolean isInjectable() {
+ return isInjectable;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("new " + getNode().getName() + '(');
+ if (args.length > 0) {
+ sb.append(args[0]);
+ for (int i = 1; i < args.length; i++) {
+ sb.append(", " + args[i]);
+ }
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ private String shallowArgString(final InjectionPlan<?> arg) {
+ if (arg instanceof Constructor || arg instanceof Subplan) {
+ return arg.getClass().getName() + ": " + arg.getNode().getName();
+ } else {
+ return arg.toShallowString();
+ }
+ }
+
+ @Override
+ public String toShallowString() {
+ final StringBuilder sb = new StringBuilder("new " + getNode().getName() + '(');
+ if (args.length > 0) {
+ sb.append(shallowArgString(args[0]));
+ for (int i = 1; i < args.length; i++) {
+ sb.append(", " + shallowArgString(args[i]));
+ }
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ /**
+ * @return A string describing ambiguous constructor arguments.
+ * @throws IllegalArgumentException if constructor is not ambiguous.
+ */
+ @Override
+ protected String toAmbiguousInjectString() {
+
+ if (!isAmbiguous) {
+ throw new IllegalArgumentException(getNode().getFullName() + " is NOT ambiguous.");
+ }
+
+ final StringBuilder sb = new StringBuilder(
+ getNode().getFullName() + " has ambiguous arguments: [ ");
+
+ for (final InjectionPlan<?> plan : args) {
+ if (plan.isAmbiguous()) {
+ sb.append(plan.toAmbiguousInjectString());
+ }
+ }
+
+ sb.append(']');
+ return sb.toString();
+ }
+
+ @Override
+ protected String toInfeasibleInjectString() {
+
+ final List<InjectionPlan<?>> leaves = new ArrayList<>();
+
+ for (final InjectionPlan<?> ip : args) {
+ if (!ip.isFeasible()) {
+ if (ip.isInfeasibleLeaf()) {
+ leaves.add(ip);
+ } else {
+ return ip.toInfeasibleInjectString();
+ }
+ }
+ }
+
+ if (leaves.isEmpty()) {
+ throw new IllegalArgumentException(getNode().getFullName() + " has NO infeasible leaves.");
+ }
+
+ if (leaves.size() == 1) {
+ return getNode().getFullName() + " missing argument " + leaves.get(0).getNode().getFullName();
+ } else {
+ final StringBuffer sb = new StringBuffer(getNode().getFullName() + " missing arguments: [\n\t");
+ for (final InjectionPlan<?> leaf : leaves) {
+ sb.append(leaf.getNode().getFullName() + "\n\t");
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+ }
+
+ @Override
+ protected boolean isInfeasibleLeaf() {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionFuturePlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionFuturePlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionFuturePlan.java
new file mode 100644
index 0000000..b57eeec
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionFuturePlan.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+
+public class InjectionFuturePlan<T> extends InjectionPlan<T> {
+
+ public InjectionFuturePlan(Node name) {
+ super(name);
+ }
+
+ @Override
+ public int getNumAlternatives() {
+ return 1;
+ }
+
+ @Override
+ public boolean isAmbiguous() {
+ return false;
+ }
+
+ @Override
+ public boolean isInjectable() {
+ return true;
+ }
+
+ @Override
+ protected String toAmbiguousInjectString() {
+ throw new UnsupportedOperationException("InjectionFuturePlan cannot be ambiguous!");
+ }
+
+ @Override
+ protected String toInfeasibleInjectString() {
+ throw new UnsupportedOperationException("InjectionFuturePlan is always feasible!");
+ }
+
+ @Override
+ protected boolean isInfeasibleLeaf() {
+ return false;
+ }
+
+ @Override
+ public String toShallowString() {
+ return "InjectionFuture<" + getNode().getFullName() + ">";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionPlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionPlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionPlan.java
new file mode 100644
index 0000000..ee113b1
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/InjectionPlan.java
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+import org.apache.reef.tang.types.Traversable;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public abstract class InjectionPlan<T> implements Traversable<InjectionPlan<?>> {
+
+ protected final Node node;
+
+ public InjectionPlan(final Node node) {
+ this.node = node;
+ }
+
+ private static void newline(StringBuffer pretty, int indent) {
+ pretty.append('\n');
+ for (int j = 0; j < indent * 2; j++) {
+ pretty.append(' ');
+ }
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+ /**
+ * Get child elements of the injection plan tree.
+ * By default, returns an empty list.
+ *
+ * @return An empty list.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public Collection<InjectionPlan<?>> getChildren() {
+ return Collections.EMPTY_LIST;
+ }
+
+ public abstract int getNumAlternatives();
+
+ public boolean isFeasible() {
+ return getNumAlternatives() > 0;
+ }
+
+ abstract public boolean isAmbiguous();
+
+ abstract public boolean isInjectable();
+
+ protected void pad(StringBuffer sb, int n) {
+ for (int i = 0; i < n; i++) {
+ sb.append(" ");
+ }
+ }
+
+ public String toPrettyString() {
+ String ugly = node.getFullName() + ":\n" + toString();
+ StringBuffer pretty = new StringBuffer();
+ int currentIndent = 1;
+ for (int i = 0; i < ugly.length(); i++) {
+ char c = ugly.charAt(i);
+ if (c == '(') {
+ if (ugly.charAt(i + 1) == ')') {
+ pretty.append("()");
+ i++;
+ } else {
+ newline(pretty, currentIndent);
+ currentIndent++;
+ pretty.append(c);
+ pretty.append(' ');
+ }
+ } else if (c == '[') {
+ if (ugly.charAt(i + 1) == ']') {
+ pretty.append("[]");
+ i++;
+ } else {
+ newline(pretty, currentIndent);
+ currentIndent++;
+ pretty.append(c);
+ pretty.append(' ');
+ }
+ } else if (c == ')' || c == ']') {
+ currentIndent--;
+ newline(pretty, currentIndent);
+ pretty.append(c);
+ } else if (c == '|') {
+ newline(pretty, currentIndent);
+ pretty.append(c);
+ } else if (c == ',') {
+ currentIndent--;
+ newline(pretty, currentIndent);
+ pretty.append(c);
+ currentIndent++;
+ } else {
+ pretty.append(c);
+ }
+ }
+ return pretty.toString();
+ }
+
+ /**
+ * Algorithm for generating cant inject string:
+ * <p/>
+ * For infeasible plans:
+ * <p/>
+ * Some node types are "leaves":
+ * <ul>
+ * <li>NamedParameterNode</li>
+ * <li>ClassNode with no @Inject constructors</li>
+ * </ul>
+ * We do a depth first search of the injection plan, starting with the left
+ * most constructor arguments. When we encounter a constructor whose arguments
+ * are all either injectable or non-injectable leaf nodes, we return the name
+ * of its parent, and the name of the non-injectable leaves.
+ * <p/>
+ * For ambiguous plans:
+ * <p/>
+ * We perform a depth first search of the ambiguous constructors, as above. We
+ * return the name of the first class that has multiple constructors that are
+ * feasible or ambiguous (as opposed to having a single constructor with an
+ * ambiguous argument, or a constructor with an infeasible argument and an
+ * ambiguous argument).
+ */
+ public final String toCantInjectString() {
+ if (!isFeasible()) {
+ return toInfeasibleInjectString();
+ } else if (isAmbiguous()) {
+ return toAmbiguousInjectString();
+ } else {
+ throw new IllegalArgumentException(
+ "toCantInjectString() called on injectable constructor:"
+ + this.toPrettyString());
+ }
+ }
+
+ protected abstract String toAmbiguousInjectString();
+
+ protected abstract String toInfeasibleInjectString();
+
+ protected abstract boolean isInfeasibleLeaf();
+
+ public abstract String toShallowString();
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ListInjectionPlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ListInjectionPlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ListInjectionPlan.java
new file mode 100644
index 0000000..236820c
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/ListInjectionPlan.java
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ListInjectionPlan<T> extends InjectionPlan<T> {
+ private final List<InjectionPlan<T>> entries = new ArrayList<>();
+ private final int numAlternatives;
+ private final boolean isAmbiguous;
+ private final boolean isInjectable;
+
+ public ListInjectionPlan(Node name, List<InjectionPlan<T>> entries) {
+ super(name);
+ this.entries.addAll(entries);
+ int numAlternatives = 1;
+ boolean isAmbiguous = false;
+ boolean isInjectable = true;
+ for (InjectionPlan<T> ip : entries) {
+ numAlternatives *= ip.getNumAlternatives();
+ isAmbiguous |= ip.isAmbiguous();
+ isInjectable &= ip.isInjectable();
+ }
+ this.numAlternatives = numAlternatives;
+ this.isAmbiguous = isAmbiguous;
+ this.isInjectable = isInjectable;
+ }
+
+ @Override
+ public int getNumAlternatives() {
+ return numAlternatives;
+ }
+
+ @Override
+ public boolean isAmbiguous() {
+ return isAmbiguous;
+ }
+
+ @Override
+ public boolean isInjectable() {
+ return isInjectable;
+ }
+
+ public List<InjectionPlan<T>> getEntryPlans() {
+ return new ArrayList<>(this.entries);
+ }
+
+ @Override
+ protected String toAmbiguousInjectString() {
+ StringBuilder sb = new StringBuilder(getNode().getFullName() + "(list) includes ambiguous plans [");
+ for (InjectionPlan<T> ip : entries) {
+ if (ip.isAmbiguous()) {
+ sb.append("\n" + ip.toAmbiguousInjectString());
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @Override
+ protected String toInfeasibleInjectString() {
+ StringBuilder sb = new StringBuilder(getNode().getFullName() + "(list) includes infeasible plans [");
+ for (InjectionPlan<T> ip : entries) {
+ if (!ip.isFeasible()) {
+ sb.append("\n" + ip.toInfeasibleInjectString());
+ }
+ }
+ sb.append("\n]");
+ return sb.toString();
+ }
+
+ @Override
+ protected boolean isInfeasibleLeaf() {
+ return false;
+ }
+
+ @Override
+ public String toShallowString() {
+ StringBuilder sb = new StringBuilder("list { ");
+ for (InjectionPlan<T> ip : entries) {
+ sb.append("\n" + ip.toShallowString());
+ }
+ sb.append("\n } ");
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/SetInjectionPlan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/SetInjectionPlan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/SetInjectionPlan.java
new file mode 100644
index 0000000..e39a17f
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/SetInjectionPlan.java
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+import org.apache.reef.tang.util.MonotonicHashSet;
+
+import java.util.Set;
+
+public class SetInjectionPlan<T> extends InjectionPlan<T> {
+ private final Set<InjectionPlan<T>> entries = new MonotonicHashSet<>();
+ private final int numAlternatives;
+ private final boolean isAmbiguous;
+ private final boolean isInjectable;
+
+ public SetInjectionPlan(Node name, Set<InjectionPlan<T>> entries) {
+ super(name);
+ this.entries.addAll(entries);
+ int numAlternatives = 1;
+ boolean isAmbiguous = false;
+ boolean isInjectable = true;
+ for (InjectionPlan<T> ip : entries) {
+ numAlternatives *= ip.getNumAlternatives();
+ isAmbiguous |= ip.isAmbiguous();
+ isInjectable &= ip.isInjectable();
+ }
+ this.numAlternatives = numAlternatives;
+ this.isAmbiguous = isAmbiguous;
+ this.isInjectable = isInjectable;
+ }
+
+ @Override
+ public int getNumAlternatives() {
+ return numAlternatives;
+ }
+
+ @Override
+ public boolean isAmbiguous() {
+ return isAmbiguous;
+ }
+
+ @Override
+ public boolean isInjectable() {
+ return isInjectable;
+ }
+
+ public Set<InjectionPlan<T>> getEntryPlans() {
+ return new MonotonicHashSet<>(this.entries);
+ }
+
+ @Override
+ protected String toAmbiguousInjectString() {
+ StringBuilder sb = new StringBuilder(getNode().getFullName() + "(set) includes ambiguous plans [");
+ for (InjectionPlan<T> ip : entries) {
+ if (ip.isAmbiguous()) {
+ sb.append("\n" + ip.toAmbiguousInjectString());
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @Override
+ protected String toInfeasibleInjectString() {
+ StringBuilder sb = new StringBuilder(getNode().getFullName() + "(set) includes infeasible plans [");
+ for (InjectionPlan<T> ip : entries) {
+ if (!ip.isFeasible()) {
+ sb.append("\n" + ip.toInfeasibleInjectString());
+ }
+ }
+ sb.append("\n]");
+ return sb.toString();
+ }
+
+ @Override
+ protected boolean isInfeasibleLeaf() {
+ return false;
+ }
+
+ @Override
+ public String toShallowString() {
+ StringBuilder sb = new StringBuilder("set { ");
+ for (InjectionPlan<T> ip : entries) {
+ sb.append("\n" + ip.toShallowString());
+ }
+ sb.append("\n } ");
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/StackBindLocation.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/StackBindLocation.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/StackBindLocation.java
new file mode 100644
index 0000000..1349001
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/StackBindLocation.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.BindLocation;
+
+import java.util.Arrays;
+
+public class StackBindLocation implements BindLocation {
+ final StackTraceElement[] stack;
+
+ public StackBindLocation() {
+ StackTraceElement[] stack = new Throwable().getStackTrace();
+ if (stack.length != 0) {
+ this.stack = Arrays.copyOfRange(stack, 1, stack.length);
+ } else {
+ this.stack = new StackTraceElement[0];
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer("[\n");
+ for (StackTraceElement e : stack) {
+ sb.append(e.toString() + "\n");
+ }
+ sb.append("]\n");
+ return sb.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Subplan.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Subplan.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Subplan.java
new file mode 100644
index 0000000..5866703
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/Subplan.java
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.types.Node;
+
+import java.util.*;
+
+final public class Subplan<T> extends InjectionPlan<T> {
+ final InjectionPlan<? extends T>[] alternatives;
+ final int numAlternatives;
+ final int selectedIndex;
+
+ @SafeVarargs
+ public Subplan(Node node, int selectedIndex, InjectionPlan<T>... alternatives) {
+ super(node);
+ this.alternatives = alternatives;
+ if (selectedIndex < -1 || selectedIndex >= alternatives.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ this.selectedIndex = selectedIndex;
+ if (selectedIndex != -1) {
+ this.numAlternatives = alternatives[selectedIndex].getNumAlternatives();
+ } else {
+ int numAlternatives = 0;
+ for (InjectionPlan<? extends T> a : alternatives) {
+ numAlternatives += a.getNumAlternatives();
+ }
+ this.numAlternatives = numAlternatives;
+ }
+ }
+
+ @SafeVarargs
+ public Subplan(Node node, InjectionPlan<T>... alternatives) {
+ this(node, -1, alternatives);
+ }
+
+ /**
+ * Get child elements of the injection plan tree.
+ * TODO: use ArrayList internally (and maybe for input, too).
+ *
+ * @return A list of injection sub-plans.
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ @Override
+ public Collection<InjectionPlan<?>> getChildren() {
+ return (Collection) Collections.unmodifiableCollection(Arrays.asList(this.alternatives));
+ }
+
+ @Override
+ public int getNumAlternatives() {
+ return this.numAlternatives;
+ }
+
+ /**
+ * Even if there is only one sub-plan, it was registered as a default plan,
+ * and is therefore ambiguous.
+ */
+ @Override
+ public boolean isAmbiguous() {
+ if (selectedIndex == -1) {
+ return true;
+ }
+ return alternatives[selectedIndex].isAmbiguous();
+ }
+
+ @Override
+ public boolean isInjectable() {
+ if (selectedIndex == -1) {
+ return false;
+ } else {
+ return alternatives[selectedIndex].isInjectable();
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (alternatives.length == 1) {
+ return getNode().getName() + " = " + alternatives[0];
+ } else if (alternatives.length == 0) {
+ return getNode().getName() + ": no injectable constructors";
+ }
+ StringBuilder sb = new StringBuilder("[");
+ sb.append(getNode().getName() + " = " + alternatives[0]);
+ for (int i = 1; i < alternatives.length; i++) {
+ sb.append(" | " + alternatives[i]);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public String toShallowString() {
+ if (alternatives.length == 1) {
+ return getNode().getName() + " = " + alternatives[0].toShallowString();
+ } else if (alternatives.length == 0) {
+ return getNode().getName() + ": no injectable constructors";
+ }
+ StringBuilder sb = new StringBuilder("[");
+ sb.append(getNode().getName() + " = " + alternatives[0].toShallowString());
+ for (int i = 1; i < alternatives.length; i++) {
+ sb.append(" | " + alternatives[i].toShallowString());
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public int getSelectedIndex() {
+ return selectedIndex;
+ }
+
+ public InjectionPlan<? extends T> getDelegatedPlan() {
+ if (selectedIndex == -1) {
+ throw new IllegalStateException();
+ } else {
+ return alternatives[selectedIndex];
+ }
+ }
+
+ @Override
+ protected String toAmbiguousInjectString() {
+ if (alternatives.length == 1) {
+ return alternatives[0].toAmbiguousInjectString();
+ } else if (selectedIndex != -1) {
+ return alternatives[selectedIndex].toAmbiguousInjectString();
+ } else {
+ List<InjectionPlan<?>> alts = new ArrayList<>();
+ List<InjectionPlan<?>> ambig = new ArrayList<>();
+ for (InjectionPlan<?> alt : alternatives) {
+ if (alt.isFeasible()) {
+ alts.add(alt);
+ }
+ if (alt.isAmbiguous()) {
+ ambig.add(alt);
+ }
+ }
+ StringBuffer sb = new StringBuffer("Ambigous subplan " + getNode().getFullName());
+ for (InjectionPlan<?> alt : alts) {
+ sb.append("\n " + alt.toShallowString() + " ");
+ }
+ for (InjectionPlan<?> alt : ambig) {
+ sb.append("\n " + alt.toShallowString() + " ");
+ }
+ sb.append("\n]");
+ return sb.toString();
+ }
+ }
+
+ @Override
+ protected String toInfeasibleInjectString() {
+ if (alternatives.length == 1) {
+ return alternatives[0].toInfeasibleInjectString();
+ } else if (alternatives.length == 0) {
+ return "No known implementations / injectable constructors for "
+ + this.getNode().getFullName();
+ } else if (selectedIndex != -1) {
+ return alternatives[selectedIndex].toInfeasibleInjectString();
+ } else {
+ return "Multiple infeasible plans: " + toPrettyString();
+ }
+ }
+
+ @Override
+ protected boolean isInfeasibleLeaf() {
+ return false;
+ }
+
+ public InjectionPlan<?>[] getPlans() {
+ return Arrays.copyOf(alternatives, alternatives.length);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/TangImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/TangImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/TangImpl.java
new file mode 100644
index 0000000..52e0569
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/TangImpl.java
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation;
+
+import org.apache.reef.tang.*;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.implementation.java.ClassHierarchyImpl;
+import org.apache.reef.tang.implementation.java.InjectorImpl;
+import org.apache.reef.tang.implementation.java.JavaConfigurationBuilderImpl;
+
+import java.net.URL;
+import java.util.*;
+
+public class TangImpl implements Tang {
+
+ private static Map<SetValuedKey, JavaClassHierarchy> defaultClassHierarchy = new HashMap<>();
+
+ /**
+ * Only for testing. Deletes Tang's current database of known classes, forcing
+ * it to rebuild them over time.
+ */
+ public static void reset() {
+ defaultClassHierarchy = new HashMap<>(); //new ClassHierarchyImpl();
+ }
+
+ @Override
+ public Injector newInjector(Configuration... confs) throws BindException {
+ return new InjectorImpl(new JavaConfigurationBuilderImpl(confs).build());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public JavaConfigurationBuilder newConfigurationBuilder() {
+ try {
+ return newConfigurationBuilder(new URL[0], new Configuration[0], new Class[0]);
+ } catch (BindException e) {
+ throw new IllegalStateException(
+ "Caught unexpeceted bind exception! Implementation bug.", e);
+ }
+ }
+
+ @Override
+ public ConfigurationBuilder newConfigurationBuilder(ClassHierarchy ch) {
+ return new ConfigurationBuilderImpl(ch);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public JavaConfigurationBuilder newConfigurationBuilder(URL... jars) {
+ try {
+ return newConfigurationBuilder(jars, new Configuration[0], new Class[0]);
+ } catch (BindException e) {
+ throw new IllegalStateException(
+ "Caught unexpeceted bind exception! Implementation bug.", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public JavaConfigurationBuilder newConfigurationBuilder(
+ Configuration... confs) throws BindException {
+ return newConfigurationBuilder(new URL[0], confs, new Class[0]);
+ }
+
+ @Override
+ public final JavaConfigurationBuilder newConfigurationBuilder(
+ @SuppressWarnings("unchecked") Class<? extends ExternalConstructor<?>>... parsers) throws BindException {
+ return newConfigurationBuilder(new URL[0], new Configuration[0], parsers);
+ }
+
+ @Override
+ public JavaConfigurationBuilder newConfigurationBuilder(URL[] jars,
+ Configuration[] confs, Class<? extends ExternalConstructor<?>>[] parameterParsers) throws BindException {
+ JavaConfigurationBuilder cb = new JavaConfigurationBuilderImpl(jars, confs, parameterParsers);
+// for (Configuration c : confs) {
+// cb.addConfiguration(c);
+// }
+ return cb;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public JavaClassHierarchy getDefaultClassHierarchy() {
+ return getDefaultClassHierarchy(new URL[0], new Class[0]);
+ }
+
+ @Override
+ public JavaClassHierarchy getDefaultClassHierarchy(URL[] jars, Class<? extends ExternalConstructor<?>>[] parameterParsers) {
+ SetValuedKey key = new SetValuedKey(jars, parameterParsers);
+
+ JavaClassHierarchy ret = defaultClassHierarchy.get(key);
+ if (ret == null) {
+ ret = new ClassHierarchyImpl(jars, parameterParsers);
+ defaultClassHierarchy.put(key, ret);
+ }
+ return ret;
+ }
+
+ @Override
+ public Injector newInjector(Configuration confs) {
+ try {
+ return newInjector(new Configuration[]{confs});
+ } catch (BindException e) {
+ throw new IllegalStateException("Unexpected error cloning configuration", e);
+ }
+ }
+
+ @Override
+ public Injector newInjector() {
+ try {
+ return newInjector(new Configuration[]{});
+ } catch (BindException e) {
+ throw new IllegalStateException("Unexpected error from empty configuration", e);
+ }
+ }
+
+ private class SetValuedKey {
+ public final Set<Object> key;
+
+ public SetValuedKey(Object[] ts, Object[] us) {
+ key = new HashSet<Object>(Arrays.asList(ts));
+ key.addAll(Arrays.asList(us));
+ }
+
+ @Override
+ public int hashCode() {
+ int i = 0;
+ for (Object t : key) {
+ i += t.hashCode();
+ }
+ return i;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ SetValuedKey other = (SetValuedKey) o;
+ if (other.key.size() != this.key.size()) {
+ return false;
+ }
+ return key.containsAll(other.key);
+ }
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/53ea32cc/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/ClassHierarchyImpl.java
----------------------------------------------------------------------
diff --git a/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/ClassHierarchyImpl.java b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/ClassHierarchyImpl.java
new file mode 100644
index 0000000..4368aa8
--- /dev/null
+++ b/lang/java/reef-tang/tang/src/main/java/org/apache/reef/tang/implementation/java/ClassHierarchyImpl.java
@@ -0,0 +1,448 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.reef.tang.implementation.java;
+
+import org.apache.reef.tang.ClassHierarchy;
+import org.apache.reef.tang.ExternalConstructor;
+import org.apache.reef.tang.JavaClassHierarchy;
+import org.apache.reef.tang.annotations.Name;
+import org.apache.reef.tang.annotations.NamedParameter;
+import org.apache.reef.tang.exceptions.BindException;
+import org.apache.reef.tang.exceptions.ClassHierarchyException;
+import org.apache.reef.tang.exceptions.NameResolutionException;
+import org.apache.reef.tang.exceptions.ParseException;
+import org.apache.reef.tang.formats.ParameterParser;
+import org.apache.reef.tang.types.*;
+import org.apache.reef.tang.util.MonotonicTreeMap;
+import org.apache.reef.tang.util.ReflectionUtilities;
+
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.*;
+
+public class ClassHierarchyImpl implements JavaClassHierarchy {
+ // TODO Want to add a "register namespace" method, but Java is not designed
+ // to support such things.
+ // There are third party libraries that would help, but they can fail if the
+ // relevant jar has not yet been loaded. Tint works around this using such
+ // a library.
+
+ /**
+ * The ParameterParser that this ClassHierarchy uses to parse default values.
+ * Custom parameter parsers allow applications to extend the set of classes
+ * that Tang can parse.
+ */
+ public final ParameterParser parameterParser = new ParameterParser();
+ /**
+ * The classloader that was used to populate this class hierarchy.
+ */
+ private final URLClassLoader loader;
+ /**
+ * The jars that are reflected by that loader. These are in addition to
+ * whatever jars are available by the default classloader (i.e., the one
+ * that loaded Tang. We need this list so that we can merge class hierarchies
+ * that are backed by different classpaths.
+ */
+ private final List<URL> jars;
+ /**
+ * A reference to the root package which is a root of a tree of nodes.
+ * The children of each node in the tree are accessible by name, and the
+ * structure of the tree mirrors Java's package namespace.
+ */
+ private final PackageNode namespace;
+ /**
+ * A map from short name to named parameter node. This is only used to
+ * sanity check short names so that name clashes get resolved.
+ */
+ private final Map<String, NamedParameterNode<?>> shortNames = new MonotonicTreeMap<>();
+
+ @SuppressWarnings("unchecked")
+ public ClassHierarchyImpl() {
+ this(new URL[0], new Class[0]);
+ }
+
+ @SuppressWarnings("unchecked")
+ public ClassHierarchyImpl(URL... jars) {
+ this(jars, new Class[0]);
+ }
+
+ public ClassHierarchyImpl(URL[] jars, Class<? extends ExternalConstructor<?>>[] parameterParsers) {
+ this.namespace = JavaNodeFactory.createRootPackageNode();
+ this.jars = new ArrayList<>(Arrays.asList(jars));
+ this.loader = new URLClassLoader(jars, this.getClass().getClassLoader());
+ for (Class<? extends ExternalConstructor<?>> p : parameterParsers) {
+ try {
+ parameterParser.addParser(p);
+ } catch (BindException e) {
+ throw new IllegalArgumentException("Could not register parameter parsers", e);
+ }
+ }
+ }
+
+ /**
+ * A helper method that returns the parsed default value of a given
+ * NamedParameter.
+ *
+ * @return null or an empty set if there is no default value, the default value (or set of values) otherwise.
+ * @throws ClassHierarchyException if a default value was specified, but could not be parsed, or if a set of
+ * values were specified for a non-set parameter.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T parseDefaultValue(NamedParameterNode<T> name) {
+ String[] vals = name.getDefaultInstanceAsStrings();
+ T[] ret = (T[]) new Object[vals.length];
+ for (int i = 0; i < vals.length; i++) {
+ String val = vals[i];
+ try {
+ ret[i] = parse(name, val);
+ } catch (ParseException e) {
+ throw new ClassHierarchyException("Could not parse default value", e);
+ }
+ }
+ if (name.isSet()) {
+ return (T) new HashSet<T>(Arrays.asList(ret));
+ } else if (name.isList()) {
+ return (T) new ArrayList<T>(Arrays.asList(ret));
+ } else {
+ if (ret.length == 0) {
+ return null;
+ } else if (ret.length == 1) {
+ return ret[0];
+ } else {
+ throw new IllegalStateException("Multiple defaults for non-set named parameter! " + name.getFullName());
+ }
+ }
+ }
+
+ /**
+ * Parse a string, assuming that it is of the type expected by a given NamedParameter.
+ * <p/>
+ * This method does not deal with sets; if the NamedParameter is set valued, then the provided
+ * string should correspond to a single member of the set. It is up to the caller to call parse
+ * once for each value that should be parsed as a member of the set.
+ *
+ * @return a non-null reference to the parsed value.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T parse(NamedParameterNode<T> np, String value) throws ParseException {
+ final ClassNode<T> iface;
+ try {
+ iface = (ClassNode<T>) getNode(np.getFullArgName());
+ } catch (NameResolutionException e) {
+ throw new IllegalStateException("Could not parse validated named parameter argument type. NamedParameter is " + np.getFullName() + " argument type is " + np.getFullArgName());
+ }
+ Class<?> clazz;
+ String fullName;
+ try {
+ clazz = (Class<?>) classForName(iface.getFullName());
+ fullName = null;
+ } catch (ClassNotFoundException e) {
+ clazz = null;
+ fullName = iface.getFullName();
+ }
+ try {
+ if (clazz != null) {
+ return (T) parameterParser.parse(clazz, value);
+ } else {
+ return parameterParser.parse(fullName, value);
+ }
+ } catch (UnsupportedOperationException e) {
+ try {
+ final Node impl = getNode(value);
+ if (impl instanceof ClassNode) {
+ if (isImplementation(iface, (ClassNode<?>) impl)) {
+ return (T) impl;
+ }
+ }
+ throw new ParseException("Name<" + iface.getFullName() + "> " + np.getFullName() + " cannot take non-subclass " + impl.getFullName(), e);
+ } catch (NameResolutionException e2) {
+ throw new ParseException("Name<" + iface.getFullName() + "> " + np.getFullName() + " cannot take non-class " + value, e);
+ }
+ }
+ }
+
+ /**
+ * Helper method that converts a String to a Class using this
+ * ClassHierarchy's classloader.
+ */
+ @Override
+ public Class<?> classForName(String name) throws ClassNotFoundException {
+ return ReflectionUtilities.classForName(name, loader);
+ }
+
+ private <T, U> Node buildPathToNode(Class<U> clazz)
+ throws ClassHierarchyException {
+ String[] path = clazz.getName().split("\\$");
+
+ Node root = namespace;
+ for (int i = 0; i < path.length - 1; i++) {
+ root = root.get(path[i]);
+ }
+
+ if (root == null) {
+ throw new NullPointerException();
+ }
+ Node parent = root;
+
+ Type argType = ReflectionUtilities.getNamedParameterTargetOrNull(clazz);
+
+ if (argType == null) {
+ return JavaNodeFactory.createClassNode(parent, clazz);
+ } else {
+
+ @SuppressWarnings("unchecked")
+ // checked inside of NamedParameterNode, using reflection.
+ NamedParameterNode<T> np = JavaNodeFactory.createNamedParameterNode(
+ parent, (Class<? extends Name<T>>) clazz, argType);
+
+ if (parameterParser.canParse(ReflectionUtilities.getFullName(argType))) {
+ if (clazz.getAnnotation(NamedParameter.class).default_class() != Void.class) {
+ throw new ClassHierarchyException("Named parameter " + ReflectionUtilities.getFullName(clazz) + " defines default implementation for parsable type " + ReflectionUtilities.getFullName(argType));
+ }
+ }
+
+ String shortName = np.getShortName();
+ if (shortName != null) {
+ NamedParameterNode<?> oldNode = shortNames.get(shortName);
+ if (oldNode != null) {
+ if (oldNode.getFullName().equals(np.getFullName())) {
+ throw new IllegalStateException("Tried to double bind "
+ + oldNode.getFullName() + " to short name " + shortName);
+ }
+ throw new ClassHierarchyException("Named parameters " + oldNode.getFullName()
+ + " and " + np.getFullName() + " have the same short name: "
+ + shortName);
+ }
+ shortNames.put(shortName, np);
+ }
+ return np;
+ }
+ }
+
+ private Node getAlreadyBoundNode(Class<?> clazz) throws NameResolutionException {
+ return getAlreadyBoundNode(ReflectionUtilities.getFullName(clazz));
+ }
+
+ @Override
+ public Node getNode(Class<?> clazz) {
+ try {
+ return getNode(ReflectionUtilities.getFullName(clazz));
+ } catch (NameResolutionException e) {
+ throw new ClassHierarchyException("JavaClassHierarchy could not resolve " + clazz
+ + " which is definitely avalable at runtime", e);
+ }
+ }
+
+ @Override
+ public synchronized Node getNode(String name) throws NameResolutionException {
+ Node n = register(name);
+ if (n == null) {
+ // This will never succeed; it just generates a nice exception.
+ getAlreadyBoundNode(name);
+ throw new IllegalStateException("IMPLEMENTATION BUG: Register failed, "
+ + "but getAlreadyBoundNode succeeded!");
+ }
+ return n;
+ }
+
+ private Node getAlreadyBoundNode(String name) throws NameResolutionException {
+ Node root = namespace;
+ String[] toks = name.split("\\$");
+ String outerClassName = toks[0];
+ root = root.get(outerClassName);
+ if (root == null) {
+ throw new NameResolutionException(name, outerClassName);
+ }
+ for (int i = 1; i < toks.length; i++) {
+ root = root.get(toks[i]);
+ if (root == null) {
+ StringBuilder sb = new StringBuilder(outerClassName);
+ for (int j = 0; j < i; j++) {
+ sb.append(toks[j]);
+ if (j != i - 1) {
+ sb.append(".");
+ }
+ }
+ throw new NameResolutionException(name, sb.toString());
+ }
+ }
+ return root;
+ }
+
+ private Node register(String s) {
+ final Class<?> c;
+ try {
+ c = classForName(s);
+ } catch (ClassNotFoundException e1) {
+ return null;
+ }
+ try {
+ Node n = getAlreadyBoundNode(c);
+ return n;
+ } catch (NameResolutionException e) {
+ }
+ // First, walk up the class hierarchy, registering all out parents. This
+ // can't be loopy.
+ if (c.getSuperclass() != null) {
+ register(ReflectionUtilities.getFullName(c.getSuperclass()));
+ }
+ for (Class<?> i : c.getInterfaces()) {
+ register(ReflectionUtilities.getFullName(i));
+ }
+ // Now, we'd like to register our enclosing classes. This turns out to be
+ // safe.
+ // Thankfully, Java doesn't allow:
+ // class A implements A.B { class B { } }
+
+ // It also doesn't allow cycles such as:
+ // class A implements B.BB { interface AA { } }
+ // class B implements A.AA { interface BB { } }
+
+ // So, even though grafting arbitrary DAGs together can give us cycles, Java
+ // seems
+ // to have our back on this one.
+ Class<?> enclosing = c.getEnclosingClass();
+ if (enclosing != null) {
+ register(ReflectionUtilities.getFullName(enclosing));
+ }
+
+ // Now register the class. This has to be after the above so we know our
+ // parents (superclasses and enclosing packages) are already registered.
+ Node n = registerClass(c);
+
+ // Finally, do things that might introduce cycles that invlove c.
+ // This has to be below registerClass, which ensures that any cycles
+ // this stuff introduces are broken.
+ for (Class<?> inner_class : c.getDeclaredClasses()) {
+ register(ReflectionUtilities.getFullName(inner_class));
+ }
+ if (n instanceof ClassNode) {
+ ClassNode<?> cls = (ClassNode<?>) n;
+ for (ConstructorDef<?> def : cls.getInjectableConstructors()) {
+ for (ConstructorArg arg : def.getArgs()) {
+ register(arg.getType());
+ if (arg.getNamedParameterName() != null) {
+ NamedParameterNode<?> np = (NamedParameterNode<?>) register(arg
+ .getNamedParameterName());
+ try {
+ if (np.isSet()) {
+ /// XXX When handling sets, need to track target of generic parameter, and check the type here!
+ } else if (np.isList()) {
+
+ } else {
+ if (!ReflectionUtilities.isCoercable(classForName(arg.getType()),
+ classForName(np.getFullArgName()))) {
+ throw new ClassHierarchyException(
+ "Named parameter type mismatch in " + cls.getFullName() + ". Constructor expects a "
+ + arg.getType() + " but " + np.getName() + " is a "
+ + np.getFullArgName());
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ throw new ClassHierarchyException("Constructor refers to unknown class "
+ + arg.getType(), e);
+ }
+ }
+ }
+ }
+ } else if (n instanceof NamedParameterNode) {
+ NamedParameterNode<?> np = (NamedParameterNode<?>) n;
+ register(np.getFullArgName());
+ }
+ return n;
+ }
+
+ /**
+ * Assumes that all of the parents of c have been registered already.
+ *
+ * @param c
+ */
+ @SuppressWarnings("unchecked")
+ private <T> Node registerClass(final Class<T> c)
+ throws ClassHierarchyException {
+ if (c.isArray()) {
+ throw new UnsupportedOperationException("Can't register array types");
+ }
+ try {
+ return getAlreadyBoundNode(c);
+ } catch (NameResolutionException e) {
+ }
+
+ final Node n = buildPathToNode(c);
+
+ if (n instanceof ClassNode) {
+ ClassNode<T> cn = (ClassNode<T>) n;
+ Class<T> superclass = (Class<T>) c.getSuperclass();
+ if (superclass != null) {
+ try {
+ ((ClassNode<T>) getAlreadyBoundNode(superclass)).putImpl(cn);
+ } catch (NameResolutionException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ for (Class<?> interf : c.getInterfaces()) {
+ try {
+ ((ClassNode<T>) getAlreadyBoundNode(interf)).putImpl(cn);
+ } catch (NameResolutionException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+ return n;
+ }
+
+ @Override
+ public PackageNode getNamespace() {
+ return namespace;
+ }
+
+ @Override
+ public synchronized boolean isImplementation(ClassNode<?> inter, ClassNode<?> impl) {
+ return impl.isImplementationOf(inter);
+ }
+
+ @Override
+ public synchronized ClassHierarchy merge(ClassHierarchy ch) {
+ if (this == ch) {
+ return this;
+ }
+ if (!(ch instanceof ClassHierarchyImpl)) {
+ throw new UnsupportedOperationException("Can't merge java and non-java class hierarchies yet!");
+ }
+ if (this.jars.size() == 0) {
+ return ch;
+ }
+ ClassHierarchyImpl chi = (ClassHierarchyImpl) ch;
+ HashSet<URL> otherJars = new HashSet<>();
+ otherJars.addAll(chi.jars);
+ HashSet<URL> myJars = new HashSet<>();
+ myJars.addAll(this.jars);
+ if (myJars.containsAll(otherJars)) {
+ return this;
+ } else if (otherJars.containsAll(myJars)) {
+ return ch;
+ } else {
+ myJars.addAll(otherJars);
+ return new ClassHierarchyImpl(myJars.toArray(new URL[0]));
+ }
+ }
+}