package my.home.dsl.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import my.home.dsl.deepClone.FieldClonerType;
import my.home.dsl.deepClone.SimpleField;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.util.TypeReferences;
import com.google.common.base.Predicate;
import com.google.inject.Inject;
/**
* Utility methods for handling introspection-like tasks. Like finding all
* member fields for a given Class/JvmType.
* <p>
* Original version of my ReflectionUtils really used Java Introspection. Now I
* use xtext.common.types. Why? I had ClassLoader issues
* (ClassNotFoundException) when run in full blown Eclipse environment and
* second, Introspection gives you run-time sort of information.
* xtext.common.types framework gives you build time sort of data, including
* generics (hopefully) otherwise erased from definition.
*
* @author espinosa
*/
@SuppressWarnings("restriction")
public class ReflectionUtils {
@Inject TypeReferences typeReferences;
/**
* Get all member fields (bean properties) for a given JVM Type (Java Class)
*
* @param jvmTypeRef
*/
public List<String> getFieldNamesForClass(JvmTypeReference jvmTypeRef) {
List<String> result = new ArrayList<String>();
if (jvmTypeRef.getType() instanceof JvmDeclaredType) {
JvmDeclaredType declaredType = (JvmDeclaredType)jvmTypeRef.getType();
for (JvmField field : declaredType.getDeclaredFields()) {
result.add(field.getSimpleName());
}
}
return result;
}
/**
* Check first for {@link Collection} type. If the parent class is
* {@link Collection} then look for generic type of the collection and use
* that instead for {@link #getFieldNamesForClass(JvmTypeReference)}
*
* @param clazz
* @return list of field names, class members, bean properties
*/
public List<String> getFieldNamesForClassTreatCollections(JvmTypeReference clazz) {
if (typeReferences.isInstanceOf(clazz, Collection.class)) {
clazz = typeReferences.getArgument(clazz, 0);
}
return getFieldNamesForClass(clazz);
}
/**
* If given type reference is a collection, then return collection type parameter.
* If given type reference is not a collection, return the type as it is.
* Example:
* For java.util.List<a.b.m.Section> returns a.b.m.Section
* For a.b.m.Book it returns a.b.m.Book
* @param clazz
* @return
*/
public JvmTypeReference getTypeOrCollectionTypeParameter(JvmTypeReference clazz) {
if (clazz==null) return null;
if (typeReferences.isInstanceOf(clazz, Collection.class)) {
clazz = typeReferences.getArgument(clazz, 0);
}
return clazz;
}
/**
* Get first type parameter for a JvmTypeReference
* @param clazz List<a.b.m.Section>
* @return fully qualified name, example: "a.b.m.Section"
*/
public String getTypeParameter(JvmTypeReference clazz) {
if (clazz==null) return null;
JvmTypeReference paramTypeClazz = typeReferences.getArgument(clazz, 0);
return paramTypeClazz.getQualifiedName();
}
/**
* Return field (bean property) for the given JVM Type (Java Class) and
* field name
*
* @param parentContainerType
* container class reference
* @param fieldName
* name of a filed (bean property) within this class
* @return full JVM Field definition
*/
public JvmField getField(JvmType parentContainerType, String fieldName) {
if (parentContainerType instanceof JvmDeclaredType) {
JvmDeclaredType declaredType = (JvmDeclaredType)parentContainerType;
for (JvmField field : declaredType.getDeclaredFields()) {
if (field.getSimpleName().equals(fieldName)) {
return field;
}
}
}
return null;
}
/**
* Return field type (bean property) for the given JVM Type (Java Class) and
* field name
*
* @param containerClass container class reference
* @param fieldName name of a filed within this class
*
* @return field Java class type or null if field is not declared in container class
*/
public JvmTypeReference getFieldType(JvmType containerClass, String fieldName) {
JvmField field = getField(containerClass, fieldName);
if (field != null) {
return field.getType();
} else {
return null;
}
}
/**
* Handling JvmTypeReference is somewhat tricky. Some operations require to
* use a defensive copy. Example:
*
* <pre>
* JvmTypeReference typeFoo1 = A.getField123Type()
* B.setField567Type(typeFoo1)
* </pre>
*
* ..would cause (unwanted) nullification of A.field123Type! Assigning type to a new field causes
* detachement from original place. Defensive copy fixes this, nothing is detached.
*
* @param typeReference
* @return
*/
public JvmTypeReference createDefensiveCopyOfJvmTypeReference(JvmTypeReference typeReference) {
//return typeReferences.createTypeRef(typeReference.getType());
return EcoreUtil.copy(typeReference);
}
public static Predicate<FieldClonerType> byName(String name) {
return new ByNamePredicate(name);
}
public static class ByNamePredicate implements Predicate<FieldClonerType> {
String name;
public ByNamePredicate(String name) {
this.name = name;
}
@Override
public boolean apply(FieldClonerType input) {
return name.equals(input.getFieldName());
}
};
}