package util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.emf.codegen.util.ImportManager;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
/**
* This helper class is used from JET templates to simplify access
* to an Ecore model.
*
* @author patrik.nordwall
*
*/
public class EcoreGenerationHelper {
private Map<String, String> typeNameMap = new HashMap<String, String>();
private Map<String, String> primitive2ObjectTypeNameMap = new HashMap<String, String>();
private ImportManager importManager = new ImportManager("");
private boolean autoImport = true;
public EcoreGenerationHelper() {
initTypeMap();
}
/**
* Mapping between primitive Ecore types and
* Java types are defined here.
*/
private void initTypeMap() {
typeNameMap.put("ecore.EBoolean", "boolean");
typeNameMap.put("ecore.EInt", "int");
typeNameMap.put("ecore.EDate", "java.util.Date");
typeNameMap.put("ecore.EBigDecimal", "java.math.BigDecimal");
typeNameMap.put("ecore.EString", "String");
// TODO more
typeNameMap.put("Map", "java.util.Map");
typeNameMap.put("List", "java.util.List");
typeNameMap.put("Set", "java.util.Set");
primitive2ObjectTypeNameMap.put("int", "Integer");
primitive2ObjectTypeNameMap.put("long", "Long");
primitive2ObjectTypeNameMap.put("double", "Double");
}
/**
* This map contains the mapping between Ecore types
* and Java types. Additional mapping can be added
* to the map.
*/
public Map<String, String> getTypeNameMap() {
return typeNameMap;
}
public ImportManager getImportManager() {
return importManager;
}
public ImportManager makeImportManager(EPackage ePackage) {
return makeImportManager(getQualifiedName(ePackage));
}
public ImportManager makeImportManager(String packageName) {
importManager = new ImportManager(packageName);
return importManager;
}
/**
* When auto import is on then imports are
* automatically added to the ImportManager
* when some methods are used, such as
* {@link #getTypeName(ETypedElement)}.
* By default auto import is on.
*/
public boolean isAutoImport() {
return autoImport;
}
/**
* Switch off auto import.
* @see #isAutoImport()
*/
public void setAutoImport(boolean autoImport) {
this.autoImport = autoImport;
}
/**
* Add a specific import manually.
* @param name fully qualified class name
*/
public void addImport(String name) {
if (name.equals("void")) {
return;
}
name = removeArrayBrackets(name);
getImportManager().addImport(name);
}
private String removeArrayBrackets(String name) {
if (name.endsWith("[]")) {
return name.substring(0, name.length() - 2);
} else {
return name;
}
}
/**
* Convenience method to get the name of an Ecore element.
*/
public String getName(ENamedElement namedElement) {
if (autoImport && (namedElement instanceof EClassifier)) {
addImport(getQualifiedName((EClassifier) namedElement));
}
return namedElement.getName();
}
/**
* Get the full name, including package name, of
* an element.
*/
public String getQualifiedName(EClassifier element) {
String qualifiedName = "";
if (element.getEPackage() != null) {
qualifiedName += getQualifiedName(element.getEPackage());
}
qualifiedName += "." + element.getName();
return qualifiedName;
}
/**
* The full package name, including parent packages.
*/
public String getQualifiedName(EPackage pack) {
String qualifiedName = pack.getName();
EPackage parentPackage = pack.getESuperPackage();
while (parentPackage != null) {
qualifiedName = parentPackage.getName() + "." + qualifiedName;
parentPackage = parentPackage.getESuperPackage();
}
return qualifiedName;
}
/**
* Some of the CRUD operations can be mapped
* by this method to support different names
* in different classes.
*/
public String getMappedOperationName(EOperation op) {
// a few naming mapping conventions
String mappedOpName = getName(op);
// forId is same as findById
if (mappedOpName.equals("forId")) {
mappedOpName = "findById";
}
// add is same as create
if (mappedOpName.equals("add")) {
mappedOpName = "create";
}
return mappedOpName;
}
/**
* Get the throws clause of an operation by looking up the 'throws' annotation.
* The exceptions can be comma separated. The exceptions are added
* to the ImportManager if autoImport is on.
*/
public String getThrows(EOperation op) {
if (getAnnotation(op, "throws") == null) {
return "";
} else {
String s = " throws ";
String throwsAnnotation = getAnnotation(op, "throws");
StringTokenizer st = new StringTokenizer(throwsAnnotation, ",");
while (st.hasMoreTokens()) {
String qualifiedName = st.nextToken().trim();
if (autoImport) {
addImport(qualifiedName);
}
String shortName = shortName(qualifiedName);
s += shortName;
if (st.hasMoreTokens()) {
s += ", ";
}
}
return s;
}
}
/**
* Returns the primitive or class name for the given Type. Class names will
* be added as imports to the ImportManager, and the imported
* form will be returned.
*/
public String getTypeName(ETypedElement element) {
return getTypeName(element, true);
}
public String getTypeName(ETypedElement element, boolean convertArrayTypes) {
String typeQualifiedName = getTypeQualifiedName(element);
if (autoImport) {
addImport(typeQualifiedName);
}
String shortName = shortName(typeQualifiedName);
// remove array brackets
if (shortName.endsWith("[]")) {
shortName = removeArrayBrackets(shortName);
if (convertArrayTypes) {
// use typed List instead of arrays
if (autoImport) {
addImport("java.util.List");
}
shortName = "List<" + shortName + ">";
}
}
return shortName;
}
/**
* Get the generic type declaration for generic
* access objects.
*/
public String getGenericType(EOperation op) {
String mappedOpName = getMappedOperationName(op);
if (mappedOpName.equals("findById")) {
EParameter idParam = getParameters(op).get(0);
String idTypeName = getTypeName(idParam);
// use object types
if (primitive2ObjectTypeNameMap.containsKey(idTypeName)) {
idTypeName = primitive2ObjectTypeNameMap.get(idTypeName);
}
return "<" + getTypeName(op) + ", " + idTypeName + ">";
} else if (mappedOpName.equals("findAll") ||
mappedOpName.equals("findByQuery") ||
mappedOpName.equals("findByExample")) {
return "<" + getTypeName(op, false) + ">";
} else if (mappedOpName.equals("create") ||
mappedOpName.equals("update") ||
mappedOpName.equals("delete")) {
EParameter firstParam = getParameters(op).get(0);
return "<" + getTypeName(firstParam) + ">";
} else {
return "";
}
}
/**
* Collection type can be set, list, bag or map.
* It corresponds to the Hibernate collection types.
*/
public String getCollectionType(EReference ref) {
return getAnnotation(ref, "collectionType", "set").toLowerCase();
}
/**
* Java interface for the collection type.
* @see #getCollectionType(EReference)
*/
public String getCollectionInterfaceType(EReference ref) {
String collectionType = getCollectionType(ref);
if ("list".equals(collectionType)) {
return "List";
} else if ("bag".equals(collectionType)) {
return "List";
} else if ("map".equals(collectionType)) {
return "Map";
} else {
return "Set";
}
}
/**
* Java implementation class for the collection type.
* @see #getCollectionType(EReference)
*/
public String getCollectionImplType(EReference ref) {
String collectionType = getCollectionType(ref);
if ("list".equals(collectionType)) {
return "ArrayList";
} else if ("bag".equals(collectionType)) {
return "ArrayList";
} else if ("map".equals(collectionType)) {
return "HashMap";
} else {
return "HashSet";
}
}
private String shortName(String qualifiedName) {
int i = qualifiedName.lastIndexOf('.');
if (i == -1) {
return qualifiedName;
} else {
return qualifiedName.substring(i+1);
}
}
/**
* Get the qualified name of the type of an element, e.g.
* the return type of an operation.
*/
public String getTypeQualifiedName(ETypedElement element) {
if (element.getEType() == null) {
return "void";
}
String mappedName = (String) typeNameMap.get(getQualifiedName(element.getEType()));
if (mappedName == null) {
return getQualifiedName(element.getEType());
} else {
return mappedName;
}
}
/**
* First character to upper case.
*/
public String capName(String name) {
if (name.length() == 0) {
return name;
} else {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
}
/**
* First character to lower case.
*/
public String uncapName(String name) {
if (name.length() == 0) {
return name;
} else {
return name.substring(0, 1).toLowerCase() + name.substring(1);
}
}
/**
* Lower all except the last upper case character if there are
* more than one upper case characters in the beginning.
* e.g. XSDElementContent -> xsdElementContent
* However if the whole string is uppercase, the whole string
* is turned into lower case.
* e.g. CPU -> cpu
*/
public String uncapPrefixedName(String name) {
if (name.length() == 0) {
return name;
} else {
String lowerName = name.toLowerCase();
int i;
for (i = 0; i < name.length(); i++) {
if (name.charAt(i) == lowerName.charAt(i)) {
break;
}
}
if (i > 1 && i < name.length()) {
--i;
}
return name.substring(0, i).toLowerCase() + name.substring(i);
}
}
/**
* Get-accessor method name of a property, according to
* JavaBeans naming conventions.
*/
public String getGetAccessor(EStructuralFeature feature) {
String capName = capName(getName(feature));
String result = isBooleanType(feature) ? "is" + capName : "get"
+ ("Class".equals(capName) ? "Class_" : capName);
return result;
}
public String getAccessorName(EStructuralFeature feature) {
return capName(getName(feature));
}
/**
* List of super types.
* If eClass is an interface the list contains all
* interfaces that it extends. If eClass is a class
* the list contains one (or zero) element with
* the class it extends.
*/
public List<EClass> getExtends(EClass eClass) {
List<EClass> superTypes = eClass.getESuperTypes();
if (eClass.isInterface()) {
return getImplements(eClass);
} else {
for (EClass t : superTypes) {
if (!t.isInterface()) {
List<EClass> list = new ArrayList<EClass>();
list.add(t);
return list;
}
}
// no class found
return Collections.emptyList();
}
}
/**
* List of super types.
* If eClass is an interface the list contains all
* interfaces that it extends. If eClass is a class
* the list contains all interfaces it implements.
*
*/
public List<EClass> getImplements(EClass eClass) {
List<EClass> superTypes = eClass.getESuperTypes();
List<EClass> interfaces = new ArrayList<EClass>();
for (EClass t : superTypes) {
if (t.isInterface()) {
interfaces.add(t);
}
}
return interfaces;
}
/**
* The normal Java extends and implements String for the class.
*/
public String getExtendsAndImplementsLitteral(EClass eClass) {
List<EClass> interfaces = getImplements(eClass);
if (eClass.isInterface()) {
if (interfaces.isEmpty()) {
return "";
} else {
return "extends " + toCommaSeparatedString(interfaces);
}
} else {
StringBuffer sb = new StringBuffer();
List<EClass> extendsClass = getExtends(eClass);
if (!extendsClass.isEmpty()) {
sb.append("extends ").append(getName(extendsClass.get(0)));
}
if (!interfaces.isEmpty()) {
if (sb.length() != 0) {
sb.append(" ");
}
sb.append("implements " + toCommaSeparatedString(interfaces));
}
return sb.toString();
}
}
private String toCommaSeparatedString(List<? extends ENamedElement> list) {
StringBuffer sb = new StringBuffer();
for (Iterator<? extends ENamedElement> iter = list.iterator(); iter.hasNext();) {
sb.append(getName(iter.next()));
if (iter.hasNext()) {
sb.append(", ");
}
}
return sb.toString();
}
/**
* @param eClass the super class
* @return subclasses to eClass in same package
*/
public List<EClass> getSubClasses(EClass eClass) {
List<EClass> subClasses = new ArrayList<EClass>();
EPackage ePackage = eClass.getEPackage();
for (EClass c : getClasses(ePackage)) {
if (getExtends(c).contains(eClass)) {
subClasses.add(c);
}
}
return subClasses;
}
public boolean isBooleanType(ETypedElement element) {
return getTypeName(element).equalsIgnoreCase("boolean");
}
public boolean isStringType(ETypedElement element) {
return getTypeName(element).equals("String");
}
/**
* The parameters of the operation as a comma separated String.
* Each item includes the parameter type and name.
*/
public String getParameterList(EOperation operation) {
return getParameterList(operation, true);
}
public String getParameterList(EOperation operation, boolean formal) {
StringBuffer result = new StringBuffer();
Iterator<EParameter> iter = operation.getEParameters().iterator();
while (iter.hasNext()) {
EParameter parameter = iter.next();
if (formal) {
result.append(getTypeName(parameter));
result.append(' ');
}
String paramName = parameter.getName();
if (paramName != null && paramName.trim().length() == 0) {
paramName = null;
}
result.append(paramName == null ?
"arg" + operation.getEParameters().indexOf(parameter)
: paramName);
if (iter.hasNext()) {
result.append(", ");
}
}
return result.toString();
}
/**
* List of references with multiplicity > 1
*/
public List<EReference> getAllManyReferences(EClass eClass) {
List<EReference> allReferences = eClass.getEReferences();
List<EReference> allManyReferences = new ArrayList<EReference>();
for (EReference ref : allReferences) {
if (isManyReference(ref)) {
allManyReferences.add(ref);
}
}
return allManyReferences;
}
/**
* List of references with multiplicity = 1
*/
public List<EReference> getAllOneReferences(EClass eClass) {
List<EReference> allReferences = eClass.getEReferences();
List<EReference> allOneReferences = new ArrayList<EReference>();
for (EReference ref : allReferences) {
if (isOneReference(ref)) {
allOneReferences.add(ref);
}
}
return allOneReferences;
}
/**
* List of references with multiplicity > 1, which opposite
* reference also has multiplicity > 1, i.e. many-to-many.
*/
public List<EReference> getManyToManyReferences(EClass eClass) {
List<EReference> many2ManyReferences = new ArrayList<EReference>();
for (EReference ref : getAllManyReferences(eClass)) {
if (isManyReference(ref.getEOpposite())) {
many2ManyReferences.add(ref);
}
}
return many2ManyReferences;
}
/**
* List of references with multiplicity > 1, which opposite
* reference has multiplicity = 1, i.e. many-to-one.
*/
public List<EReference> getManyToOneReferences(EClass eClass) {
List<EReference> many2OneReferences = new ArrayList<EReference>();
for (EReference ref : getAllManyReferences(eClass)) {
if (isOneReference(ref.getEOpposite())) {
many2OneReferences.add(ref);
}
}
return many2OneReferences;
}
/**
* @return true if multiplicity > 1 (or unbounded)
*/
public boolean isManyReference(EReference ref) {
return (ref.getUpperBound() == ETypedElement.UNBOUNDED_MULTIPLICITY) || (ref.getUpperBound() > 1);
}
/**
* @return true if multiplicity = 1 (or unspecified)
*/
private boolean isOneReference(EReference ref) {
return (ref.getUpperBound() == ETypedElement.UNSPECIFIED_MULTIPLICITY) || (ref.getUpperBound() == 1);
}
/**
* Convenience method to get the annotation associated with
* an element.
* @param element the element to look at
* @param key key name of the annotation
* @param defaultValue if the annotation is not defined this value will be returned
* @return the value of the annotation, or defaultValue if it is not defined
*/
public String getAnnotation(EModelElement element, String key, String defaultValue) {
String value = getAnnotation(element, key);
if (value == null) {
return defaultValue;
} else {
return value;
}
}
public String getAnnotation(EModelElement element, String key) {
List<EAnnotation> annotations = element.getEAnnotations();
for (EAnnotation a : annotations) {
String value = (String) a.getDetails().get(key);
if (value != null) {
return value;
}
}
// none found
return null;
}
/**
* Convenience method to get a EAttribute typed List of the attributes
* of the class.
*/
public List<EAttribute> getAttributes(EClass eClass) {
List<EAttribute> attributes = eClass.getEAttributes();
return attributes;
}
/**
* Convenience method to get a EOperation typed List of the operations
& |