private static ClassHierarchyProto.Node newNamedParameterNode(
      String name,
      String fullName,
      String simpleArgClassName,
      String fullArgClassName,
      boolean isSet,
      boolean isList,
      String documentation, // can be null
      String shortName, // can be null
      String[] instanceDefault, // can be null
      Iterable<ClassHierarchyProto.Node> children) {
    ClassHierarchyProto.NamedParameterNode.Builder namedParameterNodeBuilder =
        ClassHierarchyProto.NamedParameterNode.newBuilder()
            .setSimpleArgClassName(simpleArgClassName)
            .setFullArgClassName(fullArgClassName)
            .setIsSet(isSet)
            .setIsList(isList);
    if (documentation != null) {
      namedParameterNodeBuilder.setDocumentation(documentation);
    }
    if (shortName != null) {
      namedParameterNodeBuilder.setShortName(shortName);
    }
    if (instanceDefault != null) {
      namedParameterNodeBuilder.addAllInstanceDefault(Arrays.asList(instanceDefault));
    }

    return ClassHierarchyProto.Node.newBuilder()
        .setName(name)
        .setFullName(fullName)
        .setNamedParameterNode(namedParameterNodeBuilder.build())
        .addAllChildren(children)
        .build();
  }
  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);
    }
  }