protected FeatureTreeNode createNodes() throws FeatureModelException {

    int countFeatures = 1;
    Vector<FeatureTreeNode> fmNodes = new Vector<FeatureTreeNode>();

    String featureName = "R";
    countFeatures++;

    RootNode root = new RootNode(featureName, featureName);
    fmNodes.add(root);
    FeatureTreeNode parentNode = null;

    while (countFeatures <= numberOfFeatures) {

      parentNode = fmNodes.firstElement();
      fmNodes.removeElement(parentNode);

      int numberOfChildNodesToCreate =
          Math.min(
              numberOfFeatures - countFeatures + 1,
              (Math.abs(new Random().nextInt()) % (childFeaturesOdds - minChildFeature + 1))
                  + minChildFeature);

      // prevents an early end of the recursion when all nodes happen to have no children
      if (numberOfChildNodesToCreate == 0) {
        if (fmNodes.size() == 0) {
          numberOfChildNodesToCreate = 1;
        }
      }

      if (numberOfChildNodesToCreate > 0) {
        for (int i = 0; i < numberOfChildNodesToCreate && countFeatures <= numberOfFeatures; i++) {
          String childFeatureName = parentNode.getID().substring(1) + (i + 1);
          FeatureTreeNode randomNode =
              createRandomNode(
                  childFeatureName, solitaireOdds, groupOdds, minGroupCard, maxGroupCard);
          parentNode.add(randomNode);
          if (randomNode instanceof FeatureGroup) {
            FeatureGroup groupRandomNode = (FeatureGroup) randomNode;
            int countGroupedNodes = groupRandomNode.getChildCount();
            for (int j = 0; j < countGroupedNodes; j++) {
              fmNodes.add((FeatureTreeNode) groupRandomNode.getChildAt(j));
            }
            countFeatures += (countGroupedNodes);
          } else {
            fmNodes.add(randomNode);
            countFeatures++;
          }
        }
      }
    }

    return root;
  }