Package my.home.dsl.utils

Source Code of my.home.dsl.utils.DeepCloneUtils

package my.home.dsl.utils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import my.home.dsl.deepClone.BaseType;
import my.home.dsl.deepClone.Body;
import my.home.dsl.deepClone.ClassCloner;
import my.home.dsl.deepClone.ContainerType;
import my.home.dsl.deepClone.DeepCloneFactory;
import my.home.dsl.deepClone.FieldClonerType;
import my.home.dsl.deepClone.SimpleField;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.impl.JvmUnknownTypeReferenceImplCustom;
import org.eclipse.xtext.common.types.util.TypeReferences;

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

@SuppressWarnings("restriction")
public class DeepCloneUtils {

  @Inject
  ReflectionUtils reflectionUtils;

  @Inject
  TypeReferences typeReferences;
 
  /**
   * Assure that given element has correctly assigned corresponding Java type.
   * <p>
   * If it does not trigger type inferring process which has to be done
   * for whole model top down from root, see {@link #assureAllJavaTypesAreInferred(EObject)}
   * for more details.
   *
   * @param element
   */
  public void assureJavaTypesIsInferred(BaseType element) {
    if (element.getJavaType() == null) {
      assureAllJavaTypesAreInferred(element);
    }
  }

  /**
   * Assure that all Java types for whole model AST are inferred. All cloner
   * fields - simple, complex, nested - have their Java type assigned to them.
   * <p>
   * In DeepClone Java types are stored directly in the model itself as
   * transient values; some operations on model may however cause reset of all
   * transient values. In some use cases it must be explicitly ensured that
   * all Java types are assigned.
   *
   *
   * @param anyElement
   *            root any node from the model AST; if the give element is not
   *            root, root is found based on given element bottom up
   */
  public void assureAllJavaTypesAreInferred(EObject anyElement) {
    inferJavaTypes(findModelRoot(anyElement));
  }

  /**
   * Infer Java types for given element and all its nested elements.
   * Recursive method typically called for root element to
   * ensure all Java types are assigned for the whole semantic model.
   *
   * @param parent element
   */
  public void inferJavaTypes(Body modelRoot) {
    inferJavaTypesForElement(modelRoot);
  }
 
  // TODO: refactor!
  protected void inferJavaTypesForElement(EObject modelElement) {
    for (EObject child : modelElement.eContents()) {
      if (child instanceof ClassCloner) {
        ClassCloner rootCloner = (ClassCloner) child;
        if (rootCloner.getClassToClone() != null && !rootCloner.getClassToClone().getType().eIsProxy()) {
          rootCloner.setJavaType(reflectionUtils
            .createDefensiveCopyOfJvmTypeReference(rootCloner
              .getClassToClone()));
        } else {
          rootCloner.setJavaType(reflectionUtils
            .createDefensiveCopyOfJvmTypeReference(JVM_UNKNOWN_TYPE));         
        }
      } else if (child instanceof FieldClonerType) {
        FieldClonerType fieldCloner = (FieldClonerType) child;
        JvmTypeReference parentType = ((ContainerType) fieldCloner
            .eContainer()).getJavaType();
        JvmField jvmField = reflectionUtils.getField(
            parentType.getType(), fieldCloner.getFieldName());
        if (jvmField != null) {
          fieldCloner.setJavaType(reflectionUtils
            .createDefensiveCopyOfJvmTypeReference(
                reflectionUtils.getTypeOrCollectionTypeParameter(jvmField.getType())));
        } else {
          fieldCloner.setJavaType(reflectionUtils
            .createDefensiveCopyOfJvmTypeReference(JVM_UNKNOWN_TYPE));         
        }
      }
      if (child.eContents() != null && child.eContents().size() > 0) {
        inferJavaTypesForElement(child);
      }
    }
  }
 
  /**
   * A dummy marker Jvm reference type to indicate that type cannot be inferred,
   * that is field declared in model is not present in Java class (validation error in fact).
   */
  public static final JvmTypeReference JVM_UNKNOWN_TYPE = new JvmUnknownTypeReferenceImplCustom();

  /**
   * Find model root from any model AST node
   *
   * @param element
   *            any node from model AST
   * @return root node
   */
  public Body findModelRoot(EObject element) {
    EObject parent;
    while ((parent = element.eContainer()) != null) {
      element = parent;
    }
    if (element instanceof Body) {
      return (Body)element;
    } else {
      throw new RuntimeException("Root element must of type " + Body.class);
    }
  }

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

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

  /**
   * @return true if given field is a collection type, like List, Set, .. Used
   *         to check 1:N relations between beans.
   */
  public boolean isCollection(FieldClonerType field) {
    return typeReferences.isInstanceOf(field.getJavaType(),
        Collection.class);
  }

  /**
   * Get full name for given Model element. Intended for use in toString()
   * style methods and error messages. Skip parts with no name or unrecognised
   * name.
   */
  public String getNodeQualifiedName(EObject element) {
    List<String> result = new LinkedList<String>();
    do {
      String name = getSuitableNameForElement(element);
      if (name != null) {
        result.add(name);
      }
    } while ((element = element.eContainer()) != null);
    return Joiner.on(".").join(Lists.reverse(result));
  }

  /**
   * Get suitable name for given Model element. Intended for use in toString()
   * style methods and error messages. Get the name for known types from the
   * DSL only.
   */
  public String getSuitableNameForElement(EObject element) {
    String result = null;
    if (element instanceof FieldClonerType) {
      result = ((FieldClonerType) element).getFieldName();
    } else if (element instanceof ClassCloner) {
      ClassCloner cc = (ClassCloner) element;
      if (cc.getName() != null) {
        result = cc.getName();
      } else if (cc.getClassToClone() != null
          && cc.getClassToClone().getType() != null) {
        result = cc.getClassToClone().getType().getSimpleName();
      }
    }
    return result;
  }

  /**
   * Remove given fields from a given parent container, Container type field
   * or root cloner. Model AST manipulation method.
   *
   * @param parentContainer
   * @param fieldsToBeRemoved
   */
  public void removeFields(ContainerType parentContainer,
      List<String> fieldsToBeRemoved) {
    Preconditions.checkNotNull(parentContainer);
    List<FieldClonerType> fields = parentContainer.getFields();
    for (String fieldToBeRemoved : fieldsToBeRemoved) {
      Iterator<FieldClonerType> fieldIterator = fields.iterator();
      while (fieldIterator.hasNext()) {
        if (fieldIterator.next().getFieldName()
            .equals(fieldToBeRemoved)) {
          fieldIterator.remove();
        }
      }
    }
  }

  /**
   * Add new fields to a parent container, Container type field or root
   * cloner. Model AST manipulation method.
   *
   * @param parentContainer
   * @param fieldsToBeAdded
   *            list of field names
   */
  public void addFields(ContainerType parentContainer,
      List<String> fieldsToBeAdded) {
    DeepCloneFactory deepCloneFactory = DeepCloneFactory.eINSTANCE;
    for (String newfieldName : fieldsToBeAdded) {
      SimpleField field = deepCloneFactory.createSimpleField();
      field.setFieldName(newfieldName);
      parentContainer.getFields().add(field);
    }
  }

  /**
   * Convenient casting extension method, handy for "one-liners"
   *
   * @param field
   * @return
   */
  public ContainerType asContainer(BaseType field) {
    return (ContainerType) field;
  }
 
  /**
   * Find all missing fields (undeclared in model) for a container element based on
   * referenced Java bean class members.
   * <p>
   * In another words, find the difference between class fields (fields declared in Java class, all cloneable Java Bean members)
   * and model fields (fields declared for given container element so far).
   * <p>
   * Example:
   * <p>
   * If model model declares:
   * <pre>
   * a.b.c.Person PersonCloner {
   *    firstName
   *    surname
   * }
   * </pre>
   * And Java class declares:
   * <pre>
   * class a.b.c.Person {
   *    String firstName;
   *    String surname
   *    String middleName
   *    Date dateOfBirth
   * }
   * </pre>
   * Return would be set:
   * <pre>
   * ["middlename", "dateOfBirth"]
   * </pre>
   *
   * @param container model container type element, only containers can have fields (sub-elements)
   * @return field names or empty set
   */
  public Set<String> findMissingFieldsInContainerElement(
      ContainerType container) {
    assureJavaTypesIsInferred(container);
    List<FieldClonerType> declaredfields = container.getFields();
    List<String> classFields = reflectionUtils.getFieldNamesForClass(container.getJavaType());
    Set<String> classFieldsSet = new HashSet<String>(classFields);
    Set<String> modelFieldsSet  = new HashSet<String>(Lists.transform(declaredfields, fieldClonerNameFunc));
    Set<String> missingFieldsSet = Sets.difference(classFieldsSet, modelFieldsSet);
    return missingFieldsSet;
  }
}
TOP

Related Classes of my.home.dsl.utils.DeepCloneUtils

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.