private static void parseSubHierarchy(Node parent, ClassHierarchyProto.Node n) {
    final Node parsed;
    if (n.hasPackageNode()) {
      parsed = new PackageNodeImpl(parent, n.getName(), n.getFullName());
    } else if (n.hasNamedParameterNode()) {
      ClassHierarchyProto.NamedParameterNode np = n.getNamedParameterNode();
      parsed =
          new NamedParameterNodeImpl<Object>(
              parent,
              n.getName(),
              n.getFullName(),
              np.getFullArgClassName(),
              np.getSimpleArgClassName(),
              np.getIsSet(),
              np.getIsList(),
              np.getDocumentation(),
              np.getShortName(),
              np.getInstanceDefaultList().toArray(new String[0]));
    } else if (n.hasClassNode()) {
      ClassHierarchyProto.ClassNode cn = n.getClassNode();
      List<ConstructorDef<?>> injectableConstructors = new ArrayList<>();
      List<ConstructorDef<?>> allConstructors = new ArrayList<>();

      for (ClassHierarchyProto.ConstructorDef injectable : cn.getInjectableConstructorsList()) {
        ConstructorDef<?> def = parseConstructorDef(injectable, true);
        injectableConstructors.add(def);
        allConstructors.add(def);
      }
      for (ClassHierarchyProto.ConstructorDef other : cn.getOtherConstructorsList()) {
        ConstructorDef<?> def = parseConstructorDef(other, false);
        allConstructors.add(def);
      }
      @SuppressWarnings("unchecked")
      ConstructorDef<Object>[] dummy = new ConstructorDef[0];
      parsed =
          new ClassNodeImpl<>(
              parent,
              n.getName(),
              n.getFullName(),
              cn.getIsUnit(),
              cn.getIsInjectionCandidate(),
              cn.getIsExternalConstructor(),
              injectableConstructors.toArray(dummy),
              allConstructors.toArray(dummy),
              cn.getDefaultImplementation());
    } else {
      throw new IllegalStateException("Bad protocol buffer: got abstract node" + n);
    }
    for (ClassHierarchyProto.Node child : n.getChildrenList()) {
      parseSubHierarchy(parsed, child);
    }
  }
  @SuppressWarnings({"rawtypes", "unchecked"})
  private void wireUpInheritanceRelationships(final ClassHierarchyProto.Node n) {
    if (n.hasClassNode()) {
      final ClassHierarchyProto.ClassNode cn = n.getClassNode();
      final ClassNode iface;
      try {
        iface = (ClassNode) getNode(n.getFullName());
      } catch (NameResolutionException e) {
        throw new IllegalStateException(
            "When reading protocol buffer node "
                + n.getFullName()
                + " does not exist.  Full record is "
                + n,
            e);
      }
      for (String impl : cn.getImplFullNamesList()) {
        try {
          iface.putImpl((ClassNode) getNode(impl));
        } catch (NameResolutionException e) {
          throw new IllegalStateException(
              "When reading protocol buffer node "
                  + n
                  + " refers to non-existent implementation:"
                  + impl);
        } catch (ClassCastException e) {
          try {
            throw new IllegalStateException(
                "When reading protocol buffer node "
                    + n
                    + " found implementation"
                    + getNode(impl)
                    + " which is not a ClassNode!");
          } catch (NameResolutionException e2) {
            throw new IllegalStateException(
                "Got 'cant happen' exception when producing error message for " + e);
          }
        }
      }
    }

    for (ClassHierarchyProto.Node child : n.getChildrenList()) {
      wireUpInheritanceRelationships(child);
    }
  }