/**
  * 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(final NamedParameterNode<T> name) {
   final String[] vals = name.getDefaultInstanceAsStrings();
   final T[] ret = (T[]) new Object[vals.length];
   for (int i = 0; i < vals.length; i++) {
     final String val = vals[i];
     try {
       ret[i] = parse(name, val);
     } catch (final 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(final NamedParameterNode<T> np, final String value) throws ParseException {
   final ClassNode<T> iface;
   try {
     iface = (ClassNode<T>) getNode(np.getFullArgName());
   } catch (final 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 = classForName(iface.getFullName());
     fullName = null;
   } catch (final ClassNotFoundException e) {
     clazz = null;
     fullName = iface.getFullName();
   }
   try {
     if (clazz != null) {
       return (T) parameterParser.parse(clazz, value);
     } else {
       return parameterParser.parse(fullName, value);
     }
   } catch (final UnsupportedOperationException e) {
     try {
       final Node impl = getNode(value);
       if (impl instanceof ClassNode && isImplementation(iface, (ClassNode<?>) impl)) {
         return (T) impl;
       }
       throw new ParseException(
           "Name<"
               + iface.getFullName()
               + "> "
               + np.getFullName()
               + " cannot take non-subclass "
               + impl.getFullName(),
           e);
     } catch (final NameResolutionException e2) {
       throw new ParseException(
           "Name<"
               + iface.getFullName()
               + "> "
               + np.getFullName()
               + " cannot take non-class "
               + value,
           e);
     }
   }
 }
  private <T, U> Node buildPathToNode(final Class<U> clazz) throws ClassHierarchyException {
    final String[] path = clazz.getName().split("\\$");

    Node root = namespace;
    for (int i = 0; i < path.length - 1; i++) {
      root = root.get(path[i]);
    }

    if (root == null) {
      throw new NullPointerException();
    }
    final Node parent = root;

    final Type argType = ReflectionUtilities.getNamedParameterTargetOrNull(clazz);

    if (argType == null) {
      return JavaNodeFactory.createClassNode(parent, clazz);
    } else {

      // checked inside of NamedParameterNode, using reflection.
      @SuppressWarnings("unchecked")
      final NamedParameterNode<T> np =
          JavaNodeFactory.createNamedParameterNode(
              parent, (Class<? extends Name<T>>) clazz, argType);

      if (parameterParser.canParse(ReflectionUtilities.getFullName(argType))
          && clazz.getAnnotation(NamedParameter.class).default_class() != Void.class) {
        throw new ClassHierarchyException(
            "Named parameter "
                + ReflectionUtilities.getFullName(clazz)
                + " defines default implementation for parsable type "
                + ReflectionUtilities.getFullName(argType));
      }

      final String shortName = np.getShortName();
      if (shortName != null) {
        final NamedParameterNode<?> oldNode = shortNames.get(shortName);
        if (oldNode != null) {
          if (oldNode.getFullName().equals(np.getFullName())) {
            throw new IllegalStateException(
                "Tried to double bind " + oldNode.getFullName() + " to short name " + shortName);
          }
          throw new ClassHierarchyException(
              "Named parameters "
                  + oldNode.getFullName()
                  + " and "
                  + np.getFullName()
                  + " have the same short name: "
                  + shortName);
        }
        shortNames.put(shortName, np);
      }
      return np;
    }
  }
  private Node register(final String s) {
    final Class<?> c;
    try {
      c = classForName(s);
    } catch (final ClassNotFoundException e1) {
      return null;
    }
    try {
      final Node n = getAlreadyBoundNode(c);
      return n;
    } catch (final NameResolutionException e) {
      // node not bound yet
    }
    // First, walk up the class hierarchy, registering all out parents. This
    // can't be loopy.
    if (c.getSuperclass() != null) {
      register(ReflectionUtilities.getFullName(c.getSuperclass()));
    }
    for (final Class<?> i : c.getInterfaces()) {
      register(ReflectionUtilities.getFullName(i));
    }
    // Now, we'd like to register our enclosing classes. This turns out to be
    // safe.
    // Thankfully, Java doesn't allow:
    // class A implements A.B { class B { } }

    // It also doesn't allow cycles such as:
    // class A implements B.BB { interface AA { } }
    // class B implements A.AA { interface BB { } }

    // So, even though grafting arbitrary DAGs together can give us cycles, Java
    // seems
    // to have our back on this one.
    final Class<?> enclosing = c.getEnclosingClass();
    if (enclosing != null) {
      register(ReflectionUtilities.getFullName(enclosing));
    }

    // Now register the class. This has to be after the above so we know our
    // parents (superclasses and enclosing packages) are already registered.
    final Node n = registerClass(c);

    // Finally, do things that might introduce cycles that invlove c.
    // This has to be below registerClass, which ensures that any cycles
    // this stuff introduces are broken.
    for (final Class<?> innerClass : c.getDeclaredClasses()) {
      register(ReflectionUtilities.getFullName(innerClass));
    }
    if (n instanceof ClassNode) {
      final ClassNode<?> cls = (ClassNode<?>) n;
      for (final ConstructorDef<?> def : cls.getInjectableConstructors()) {
        for (final ConstructorArg arg : def.getArgs()) {
          register(arg.getType());
          if (arg.getNamedParameterName() != null) {
            final NamedParameterNode<?> np =
                (NamedParameterNode<?>) register(arg.getNamedParameterName());
            try {
              // TODO: When handling sets, need to track target of generic parameter, and check the
              // type here!
              if (!np.isSet()
                  && !np.isList()
                  && !ReflectionUtilities.isCoercable(
                      classForName(arg.getType()), classForName(np.getFullArgName()))) {
                throw new ClassHierarchyException(
                    "Named parameter type mismatch in "
                        + cls.getFullName()
                        + ".  Constructor expects a "
                        + arg.getType()
                        + " but "
                        + np.getName()
                        + " is a "
                        + np.getFullArgName());
              }
            } catch (final ClassNotFoundException e) {
              throw new ClassHierarchyException(
                  "Constructor refers to unknown class " + arg.getType(), e);
            }
          }
        }
      }
    } else if (n instanceof NamedParameterNode) {
      final NamedParameterNode<?> np = (NamedParameterNode<?>) n;
      register(np.getFullArgName());
    }
    return n;
  }