/**
   * Read Node config from file config.nodeConfigFileName, including available nodes and their
   * possible connections
   */
  public NodeFactory(Config config, ProblemData problemData) {
    this.config = config;
    this.problemData = problemData;
    try {
      FileInputStream in = new FileInputStream(config.nodeConfigFileName);
      Properties props = new Properties();
      props.load(in);
      in.close();

      String separator = "\\s*,\\s*";

      String tmp = props.getProperty("sets");
      String[] sets = tmp.split(separator);

      nodeSets = new NodeSet[sets.length];

      int i, j, k;
      for (i = 0; i < sets.length; i++) {
        nodeSets[i] = new NodeSet(sets[i]);
        String[] nodes;
        tmp = props.getProperty(sets[i] + "Functions");
        if (tmp == null)
          Logger.log(
              "Warning: when reading "
                  + config.nodeConfigFileName
                  + ", no functions found for set "
                  + sets[i]);
        else {
          nodes = tmp.split(separator);
          for (j = 0; j < nodes.length; j++) {
            Class cl = Class.forName("gpalta.nodes." + nodes[j]);
            java.lang.reflect.Constructor co = cl.getConstructor();
            nodeSets[i].addFunction((Node) co.newInstance());
          }
        }
        tmp = props.getProperty(sets[i] + "Terminals");
        if (tmp == null)
          Logger.log(
              "Warning: when reading "
                  + config.nodeConfigFileName
                  + ", no terminals found for set "
                  + sets[i]);
        else {
          nodes = tmp.split(separator);
          for (j = 0; j < nodes.length; j++) {
            Class cl = Class.forName("gpalta.nodes." + nodes[j]);
            if (nodes[j].contains("Var")) {
              for (k = 0; k < problemData.nVars; k++) {
                java.lang.reflect.Constructor[] co = cl.getConstructors();
                nodeSets[i].addTerminal((Node) co[0].newInstance(k + 1));
              }
            } else if (nodes[j].contains("Angle")) {
              for (k = 0; k < problemData.nVars - 1; k++) {
                java.lang.reflect.Constructor[] co = cl.getConstructors();
                nodeSets[i].addTerminal((Node) co[0].newInstance(k + 1));
              }
            } else {
              java.lang.reflect.Constructor co = cl.getConstructor();
              nodeSets[i].addTerminal((Node) co.newInstance());
            }
          }
        }
      }

      for (i = 0; i < nodeSets.length; i++) {
        for (Node n : nodeSets[i].getAll()) {
          n.setType(nodeSets[i]);
        }
      }

      for (i = 0; i < nodeSets.length; i++) {
        for (Node n : nodeSets[i].getAll()) {
          tmp = props.getProperty("kids" + n.getClass().getSimpleName());
          if (tmp != null) {
            String[] kids = tmp.split(separator);
            if (kids.length != n.nKids()) {
              Logger.log(
                  "Error reading "
                      + config.nodeConfigFileName
                      + ": must specify "
                      + n.nKids()
                      + " kids for "
                      + n.getClass().getSimpleName());
            }
            for (j = 0; j < n.nKids(); j++) {
              for (k = 0; k < nodeSets.length; k++) {
                if (nodeSets[k].getName().equals(kids[j])) {
                  n.setTypeOfKids(j, nodeSets[k]);
                  break;
                }
              }
              if (k == nodeSets.length)
                Logger.log(
                    "Error reading "
                        + config.nodeConfigFileName
                        + ": Setting kids for "
                        + n.getClass().getSimpleName()
                        + ", "
                        + kids[j]
                        + " doesn't match any set name");
            }
          } else for (j = 0; j < n.nKids(); j++) n.setTypeOfKids(j, nodeSets[i]);
        }
      }
      tmp = props.getProperty("treeRoot");
      if (tmp == null) {
        Logger.log(
            "Error reading " + config.nodeConfigFileName + ": property \"treeRoot\" not present");
      }
      for (i = 0; i < nodeSets.length; i++) {
        if (nodeSets[i].getName().equals(tmp)) {
          treeRoot = nodeSets[i];
          break;
        }
      }
      if (i == nodeSets.length)
        Logger.log(
            "Error reading "
                + config.nodeConfigFileName
                + ": Setting treeRoot, "
                + tmp
                + " doesn't match any set name");
    } catch (IOException e) {
      Logger.log("Error reading " + config.nodeConfigFileName + ":");
      Logger.log(e);
    } catch (ClassNotFoundException e) {
      Logger.log("Error reading " + config.nodeConfigFileName + ":");
      Logger.log(e);
    } catch (NoSuchMethodException e) {
      Logger.log("Error reading " + config.nodeConfigFileName + ":");
      Logger.log(e);
    } catch (InstantiationException e) {
      Logger.log("Error reading " + config.nodeConfigFileName + ":");
      Logger.log(e);
    } catch (IllegalAccessException e) {
      Logger.log("Error reading " + config.nodeConfigFileName + ":");
      Logger.log(e);
    } catch (InvocationTargetException e) {
      Logger.log("Error reading " + config.nodeConfigFileName + ":");
      Logger.log(e);
    }
  }