public static Feature createTypeOfFeatureInMetaMetaModel(String name, ValueType valueType) {
    Feature typeOfFeature =
        createFeatureWithoutProperties(
            name, name, ConfigState.UNDECIDED_LITERAL, ValueType.NONE_LITERAL, null, 0, 1);
    Feature valueOfFeature =
        createFeatureWithoutProperties(
            "Value", "Value", ConfigState.UNDECIDED_LITERAL, valueType, null, 0, 1);
    Feature defaultValueOfFeature =
        createFeatureWithoutProperties(
            "Default Value", "DefaultValue", ConfigState.UNDECIDED_LITERAL, valueType, null, 0, 1);
    typeOfFeature.getChildren().add(valueOfFeature);
    typeOfFeature.getChildren().add(defaultValueOfFeature);

    return typeOfFeature;
  }
  public static Feature createFeatureWithProperties(
      String name,
      String id,
      ConfigState state,
      ValueType valueType,
      Object value,
      Object defaultValue,
      int min,
      int max,
      Feature metaMetaModel) {
    Feature feature = FmpFactory.eINSTANCE.createFeature();
    Feature metaMetaFeature = (Feature) metaMetaModel.getChildren().get(0);
    feature.setProperties(ModelManipulation.INSTANCE.configure(metaMetaFeature));
    setFeatureAttributes(feature, name, id, state, valueType, value, defaultValue, min, max);

    return feature;
  }
  public static Reference createReferenceWithProperties(
      Feature feature, String id, ConfigState state, int min, int max, Feature metaMetaModel) {
    Reference reference = FmpFactoryImpl.eINSTANCE.createReference();
    Feature metaMetaReference = (Feature) metaMetaModel.getChildren().get(2);
    reference.setProperties((Feature) ModelManipulation.INSTANCE.configure(metaMetaReference));
    setReferenceAttributes(reference, feature, id, state, min, max);

    return reference;
  }
  public static FeatureGroup createFeatureGroupWithProperties(
      String id, int min, int max, Feature metaMetaModel) {
    FeatureGroup featureGroup = FmpFactory.eINSTANCE.createFeatureGroup();
    Feature metaMetaFeatureGroup = (Feature) metaMetaModel.getChildren().get(1);
    featureGroup.setProperties(ModelManipulation.INSTANCE.configure(metaMetaFeatureGroup));
    setFeatureGroupAttributes(featureGroup, id, min, max);

    return featureGroup;
  }
  public static Feature makeModel(Project project) {
    Feature metaModel = project.getMetaModel();

    Feature metaFeature = (Feature) metaModel.getChildren().get(0);
    // Feature metaFeatureGroup = (Feature)metaModel.getChildren().get(1);
    // Feature metaReference = (Feature) metaModel.getChildren().get(2);

    Feature model = FmpFactory.eINSTANCE.createFeature();
    model.setProperties(ModelManipulation.INSTANCE.configure(metaFeature));
    setFeatureAttributes(
        model,
        "My Feature Model",
        "My Feature Model",
        ConfigState.UNDECIDED_LITERAL,
        ValueType.NONE_LITERAL,
        null,
        null,
        1,
        1);

    return model;
  }
  /**
   * Set feature attributes using the specified values. When setting the value type, this function
   * also sets the checkboxes underneath "Attribute" node appropriately. Ideally, this should be
   * done using choice propagation, as there is a feature group constraint underneath the
   * "Attribute" node. However, because we use lazy construction of configurator, enabling choice
   * propagation is in efficient when creating so many configurations of metameta and meta features.
   * So we select and eliminate the type features in the properties view manually.
   *
   * @param feature
   * @param name
   * @param id
   * @param state
   * @param valueType
   * @param value
   * @param min
   * @param max
   */
  public static void setFeatureAttributes(
      Feature feature,
      String name,
      String id,
      ConfigState state,
      ValueType valueType,
      Object value,
      Object defaultValue,
      int min,
      int max) {
    feature.setName(name);
    feature.setId(id);
    feature.setState(state);
    feature.setValueType(valueType);

    if (valueType != ValueType.NONE_LITERAL) {
      TypedValue typedValue = null;
      if (value != null) {
        typedValue = FmpFactory.eINSTANCE.createTypedValue();
        if (value instanceof Integer) typedValue.setIntegerValue((Integer) value);
        else if (value instanceof Float) typedValue.setFloatValue((Float) value);
        else if (value instanceof String) typedValue.setStringValue((String) value);
        else if (value instanceof Feature) typedValue.setFeatureValue((Feature) value);

        feature.setTypedValue(typedValue);
      }
      TypedValue defaultTypedValue = null;
      if (defaultValue != null) {
        defaultTypedValue = FmpFactory.eINSTANCE.createTypedValue();
        if (defaultValue instanceof Integer)
          defaultTypedValue.setIntegerValue((Integer) defaultValue);
        else if (defaultValue instanceof Float)
          defaultTypedValue.setFloatValue((Float) defaultValue);
        else if (defaultValue instanceof String)
          defaultTypedValue.setStringValue((String) defaultValue);
        else if (defaultValue instanceof Feature)
          defaultTypedValue.setFeatureValue((Feature) defaultValue);

        feature.setDefaultValue(defaultTypedValue);
      }

      if (feature.getProperties() != null) {
        int typeIndex = -1;
        if (valueType == ValueType.INTEGER_LITERAL) typeIndex = 0;
        else if (valueType == ValueType.FLOAT_LITERAL) typeIndex = 1;
        else if (valueType == ValueType.STRING_LITERAL) typeIndex = 2;
        else if (valueType == ValueType.FEATURE_LITERAL) typeIndex = 3;

        Feature attributeNode =
            (Feature)
                ModelNavigation.INSTANCE.getNodes(feature.getProperties(), "Attribute").get(0);
        attributeNode.setState(ConfigState.USER_SELECTED_LITERAL);

        for (int i = 0; i < 4; i++) {
          Feature typeNode =
              (Feature) ((FeatureGroup) attributeNode.getChildren().get(0)).getChildren().get(i);
          ConfigState typeSelectionState =
              (i == typeIndex)
                  ? ConfigState.USER_SELECTED_LITERAL
                  : ConfigState.MACHINE_ELIMINATED_LITERAL;

          // set the state of the type node ("Integer", "String", or whatever)
          typeNode.setState(typeSelectionState);

          // if the type is eliminated, then eliminate all of its children as well
          if (typeSelectionState == ConfigState.MACHINE_ELIMINATED_LITERAL) {
            ((Feature) typeNode.getChildren().get(0)).setState(typeSelectionState);
            ((Feature) typeNode.getChildren().get(1)).setState(typeSelectionState);
          }
          // otherwise, select the Value node if there is a value
          // and default value node if there is a default value
          else {
            if (typedValue != null)
              ((Feature) typeNode.getChildren().get(0)).setState(typeSelectionState);
            if (defaultTypedValue != null)
              ((Feature) typeNode.getChildren().get(1)).setState(typeSelectionState);
          }
        }
      }
    }
    feature.setMin(min);
    feature.setMax(max);
  }
  public static Feature makeMetaModel(Project project) {
    Feature metaMetaModel = project.getMetaMetaModel();
    // Root of the metamodel
    Feature metaModel =
        createFeatureWithProperties(
            "MetaModel",
            "MetaModel",
            ConfigState.UNDECIDED_LITERAL,
            ValueType.NONE_LITERAL,
            null,
            null,
            1,
            1,
            metaMetaModel);

    // this block creates "Feature" meta
    {
      Feature metaFeature =
          createFeatureWithProperties(
              "Feature",
              "Feature",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.NONE_LITERAL,
              null,
              null,
              1,
              1,
              metaMetaModel);
      metaModel.getChildren().add(metaFeature);

      Feature nameOfFeature =
          createFeatureWithProperties(
              "Name",
              "Name",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.STRING_LITERAL,
              null,
              null,
              1,
              1,
              metaMetaModel);
      metaFeature.getChildren().add(nameOfFeature);

      Feature idOfFeature =
          createFeatureWithProperties(
              "Id",
              "Id",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.STRING_LITERAL,
              null,
              "featureVar",
              1,
              1,
              metaMetaModel);
      metaFeature.getChildren().add(idOfFeature);

      Feature descOfFeature =
          createFeatureWithProperties(
              "Description",
              "Description",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.STRING_LITERAL,
              null,
              null,
              0,
              1,
              metaMetaModel);
      metaFeature.getChildren().add(descOfFeature);

      Feature attributeOfFeature =
          createFeatureWithProperties(
              "Attribute",
              "Attribute",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.NONE_LITERAL,
              null,
              null,
              0,
              1,
              metaMetaModel);
      metaFeature.getChildren().add(attributeOfFeature);
      FeatureGroup typeGroup = createFeatureGroupWithProperties("TypeGroup", 0, 1, metaMetaModel);
      attributeOfFeature.getChildren().add(typeGroup);
      typeGroup
          .getChildren()
          .add(createTypeOfFeatureInMetaModel("Integer", ValueType.INTEGER_LITERAL, metaMetaModel));
      typeGroup
          .getChildren()
          .add(createTypeOfFeatureInMetaModel("Float", ValueType.FLOAT_LITERAL, metaMetaModel));
      typeGroup
          .getChildren()
          .add(createTypeOfFeatureInMetaModel("String", ValueType.STRING_LITERAL, metaMetaModel));
      typeGroup
          .getChildren()
          .add(createTypeOfFeatureInMetaModel("Feature", ValueType.FEATURE_LITERAL, metaMetaModel));

      Feature minOfFeature =
          createFeatureWithProperties(
              "Min",
              "Min",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.INTEGER_LITERAL,
              null,
              new Integer(0),
              1,
              1,
              metaMetaModel);
      metaFeature.getChildren().add(minOfFeature);

      Feature maxOfFeature =
          createFeatureWithProperties(
              "Max",
              "Max",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.INTEGER_LITERAL,
              null,
              new Integer(1),
              1,
              1,
              metaMetaModel);
      metaFeature.getChildren().add(maxOfFeature);
    }

    // this block creates "FeatureGroup" meta
    {
      Feature metaFeatureGroup =
          createFeatureWithProperties(
              "FeatureGroup",
              "FeatureGroup",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.NONE_LITERAL,
              null,
              null,
              1,
              1,
              metaMetaModel);
      metaModel.getChildren().add(metaFeatureGroup);

      Feature idOfFeatureGroup =
          createFeatureWithProperties(
              "Id",
              "Id",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.STRING_LITERAL,
              null,
              "group",
              1,
              1,
              metaMetaModel);
      metaFeatureGroup.getChildren().add(idOfFeatureGroup);

      Feature minOfFeatureGroup =
          createFeatureWithProperties(
              "Min",
              "Min",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.INTEGER_LITERAL,
              null,
              new Integer(1),
              1,
              1,
              metaMetaModel);
      metaFeatureGroup.getChildren().add(minOfFeatureGroup);

      Feature maxOfFeatureGroup =
          createFeatureWithProperties(
              "Max",
              "Max",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.INTEGER_LITERAL,
              null,
              new Integer(1),
              1,
              1,
              metaMetaModel);
      metaFeatureGroup.getChildren().add(maxOfFeatureGroup);
    }

    // this block creates "Reference" meta
    {
      Feature metaReference =
          createFeatureWithProperties(
              "Reference",
              "Reference",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.NONE_LITERAL,
              null,
              null,
              1,
              1,
              metaMetaModel);
      metaModel.getChildren().add(metaReference);

      Reference referenceOfReference =
          createReferenceWithProperties(
              null, "Reference", ConfigState.UNDECIDED_LITERAL, 1, 1, metaMetaModel);
      metaReference.getChildren().add(referenceOfReference);

      Feature idOfReference =
          createFeatureWithProperties(
              "Id",
              "Id",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.STRING_LITERAL,
              null,
              "reference",
              1,
              1,
              metaMetaModel);
      metaReference.getChildren().add(idOfReference);

      Feature minOfReference =
          createFeatureWithProperties(
              "Min",
              "Min",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.INTEGER_LITERAL,
              null,
              new Integer(0),
              1,
              1,
              metaMetaModel);
      metaReference.getChildren().add(minOfReference);

      Feature maxOfReference =
          createFeatureWithProperties(
              "Max",
              "Max",
              ConfigState.UNDECIDED_LITERAL,
              ValueType.INTEGER_LITERAL,
              null,
              new Integer(1),
              1,
              1,
              metaMetaModel);
      metaReference.getChildren().add(maxOfReference);
    }

    return metaModel;
  }