Package my.home.dsl.validation

Source Code of my.home.dsl.validation.DeepCloneJavaValidator

package my.home.dsl.validation;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import my.home.dsl.deepClone.BaseType;
import my.home.dsl.deepClone.ClassCloner;
import my.home.dsl.deepClone.ComplexField;
import my.home.dsl.deepClone.ContainerType;
import my.home.dsl.deepClone.DeepClonePackage;
import my.home.dsl.deepClone.FieldClonerType;
import my.home.dsl.utils.DeepCloneUtils;
import my.home.dsl.utils.ReflectionUtils;

import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.validation.Check;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Inject;

/**
* DeepClone DSL main validator. It is called after parsing, when model AST is
* created.
* <p>
* It also sets inferred Java types for all Field like objects. Attribute
* {@link BaseType#getJavaType()} is a transient attribute in the model.
*
* @author espinosa
*/
public class DeepCloneJavaValidator extends AbstractDeepCloneJavaValidator {

  @Inject ReflectionUtils utils;
 
  @Inject DeepCloneUtils dcUtils;

  /**
   * Prevent cloning of certain bean properties, like 'class', that is {@link Object#getClass()}
   * @param fieldCloner
   */
  @Check
  public void checkFieldForbiddenNames(FieldClonerType fieldCloner) {
    if (fieldCloner.getFieldName().equals("class")) {
      error("Field cannot be named 'class', introspection ", DeepClonePackage.Literals.FIELD_CLONER_TYPE__FIELD_NAME);
    }
  }
 
  public static final String FIELD_DEFINITION_NOT_UNIQUE_IN_CONTAINER_ERROR = "Missing field error";

  /**
   * Every member field must be defined once in its parent container
   * @param fieldCloner
   */
  @Check
  public void checkFieldUniquity(FieldClonerType fieldCloner) {
    ContainerType parentContainer = ((ContainerType)fieldCloner.eContainer());
    List<FieldClonerType> declaredfields = parentContainer.getFields();
    int uniquityCounter = 0;
    for (FieldClonerType otherField : declaredfields) {
      if (otherField.getFieldName().equals(fieldCloner.getFieldName())) uniquityCounter++;
      if (uniquityCounter>1) {
        error("Field " + fieldCloner.getFieldName() + " is declared more then once in container " + dcUtils.getNodeQualifiedName(parentContainer),
            fieldCloner,
            DeepClonePackage.Literals.FIELD_CLONER_TYPE__FIELD_NAME,
            FIELD_DEFINITION_NOT_UNIQUE_IN_CONTAINER_ERROR, fieldCloner.getFieldName());
        break;
      }
    }
  }

  /**
   * Check class existence for root Cloner. Root cloner has to have its type
   * specified in the model. All other field types will be inferred upon this
   * type.<br>
   * Set {@link BaseType#setJavaType(JvmTypeReference)}. Upon this value all
   * type inference is based recursively for all member fields and
   * sub-structures.
   *
   * @param rootCloner
   */
  @Check
  public void checkRootClonerClass(ClassCloner rootCloner) {
    // copying from one JvmTypeReference to another JvmTypeReference field is so straightforward
    // without defensive copy of the, the first field would get nullified, upon setting the second! (a xtext bug?)
    rootCloner.setJavaType(utils.createDefensiveCopyOfJvmTypeReference(rootCloner.getClassToClone()));
    // there is no real checking of type/class existence since we use XBase's JvmTypeReference and
    // it takes care of type existence already
  }

  /**
   * Infer field type based on parent container type and current field name.
   * Check its existence. Set {@link BaseType#setJavaType(JvmTypeReference)}.
   * Upon this value all type inference is based recursively for all member
   * fields and sub-structures.
   *
   * @param fieldCloner
   *            current field reference
   */
  @Check
  public void checkFieldClass(FieldClonerType fieldCloner) {
    JvmTypeReference parentType = ((ContainerType)fieldCloner.eContainer()).getJavaType();
    if (parentType!=null) {
      JvmTypeReference clazz = utils.getFieldType(
          utils.getTypeOrCollectionTypeParameter(
              parentType).getType(),
              fieldCloner.getFieldName());
      if (clazz != null) {
        fieldCloner.setJavaType(utils.createDefensiveCopyOfJvmTypeReference(clazz));
      }
    }
    // there is no real checking of type/class existence since we use XBase's JvmTypeReference and
    // it takes care of type existence already
  }

  public static final String MISSING_FIELD_ERROR = "Missing field error";

  public static final String SURPLUS_FIELD_ERROR = "Surplus field error";
 
  public static final String MISSING_FIELDS_ERROR = "Missing fields error";

  public static final String SURPLUS_FIELDS_ERROR = "Surplus fields error";

  /**
   * Check all member fields (bean properties), defined in the model against
   * introspection members. They must match perfectly. Every introspection
   * field must have equivalent in the cloner definition. Field is either
   * explicitly included or explicitly excluded. This include complex fields
   * and cloner references too.
   *
   * @param container
   *            a container type - ClassCloner or ComplexField - reference
   */
  @Check
  public void checkPresenceOfAllFields(ContainerType container) {
    List<FieldClonerType> declaredfields = container.getFields();
    if (container.getJavaType()==null) return;
    List<String> reflectionFields = utils.getFieldNamesForClassTreatCollections(container.getJavaType());

    Set<String> reflectionFieldNames = new HashSet<String>(reflectionFields);
    Set<String> declaredfieldsNames  = new HashSet<String>(Lists.transform(declaredfields, fieldClonerNameFunc));

    Set<String> missingDeclarations = Sets.difference(reflectionFieldNames, declaredfieldsNames);
    Set<String> surplusDeclarations = Sets.difference(declaredfieldsNames, reflectionFieldNames);

    int i = 0;
    for (String missingFieldName : missingDeclarations) {
      error("The cloner for type " + getContainerName(container) + " must exclude or include field " + missingFieldName, // message
          container,  // the error is associated with parent container
          findMarkingFeatureForMissingField(container), // structural feature; indication what to underline as an error, for a cloner it is its name
          MISSING_FIELD_ERROR,  // error code
          getIssueData(missingDeclarations, i) // issue data
          );
      i++;
    }

    i = 0;
    for (String surplusFieldName : surplusDeclarations) {
      error("The cloner for type " + getContainerName(container) + " references unknown field " + surplusFieldName, // message
          Iterables.find(container.getFields(), ReflectionUtils.byName(surplusFieldName)), // the error is associated with filed itself, we have to locate it first
          DeepClonePackage.Literals.FIELD_CLONER_TYPE__FIELD_NAME,  // structural feature; indication what to underline as an error, for a surplus filed it is its name
          SURPLUS_FIELD_ERROR,  // error code
          getIssueData(surplusDeclarations, i)    // issueData
          );
      i++;
    }
  }
 
  protected String[] getIssueData(Set<String> fields, int fieldCounter) {
    if (fieldCounter == 0) {
      return fields.toArray(new String[]{});
    } else {
      return null;
    }
  }
 
  protected String getContainerName(ContainerType container) {
    if (container instanceof ClassCloner) {
      return ((ClassCloner)container).getClassToClone().getQualifiedName();
    } else if (container instanceof ComplexField) {
      return ((ComplexField)container).getJavaType().getQualifiedName();
    } else {
      return "???";
    }
  }

  /**
   * Example results:
   * CLASS_CLONER__CLASS_TO_CLONE - color only class in ClassCloner  
   * null - color whole element (lots of red lines)
   * @param container
   * @return
   */
  protected EStructuralFeature findMarkingFeatureForMissingField(ContainerType container) {
    if (container instanceof ClassCloner) {
      if (((ClassCloner)container).getName() != null) {
        return DeepClonePackage.Literals.CLASS_CLONER__NAME;
      } else {
        return DeepClonePackage.Literals.CLASS_CLONER__CLASS_TO_CLONE;
      }
    } else if (container instanceof ComplexField) {
      return DeepClonePackage.Literals.FIELD_CLONER_TYPE__FIELD_NAME;
    } else {
      return null;
    }
  }

  /**
   * Predicate for Google Collections filtering
   */
  @SuppressWarnings("unused")
  private Function<Field, String> reflFieldNameFunc = new Function<Field, String>() {
    @Override public String apply(Field f) {
      return f.getName();
    }
  };

  /**
   * Predicate for Google Collections filtering
   */
  private Function<FieldClonerType, String> fieldClonerNameFunc = new Function<FieldClonerType, String>() {
    @Override public String apply(FieldClonerType f) {
      return f.getFieldName();
    }
  };
}
TOP

Related Classes of my.home.dsl.validation.DeepCloneJavaValidator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.