blob: f0296bc1c3b7c9c5ba183462251a0327101d8615 [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*/
package org.eclipse.osbp.autowirehelper;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.osbp.autowirehelper.AutowireHelper.LocalResourceAccess;
import org.eclipse.osbp.runtime.common.annotations.Filter;
import org.eclipse.osbp.runtime.common.annotations.Range;
import org.eclipse.osbp.runtime.common.annotations.UniqueEntry;
import org.eclipse.xtext.common.types.JvmAnnotationReference;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmFeature;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmParameterizedTypeReference;
import org.eclipse.xtext.common.types.JvmPrimitiveType;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmVisibility;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.common.types.util.RawSuperTypes;
import org.eclipse.xtext.resource.IReferenceDescription;
import org.eclipse.xtext.ui.editor.findrefs.IReferenceFinder;
import org.eclipse.xtext.util.IAcceptor;
import org.eclipse.xtext.xbase.lib.StringExtensions;
import com.google.inject.Inject;
/**
* Helper class to collect all properties for a given JvmType.
*/
@SuppressWarnings("restriction")
public class JvmTypeProperties {
@Inject
private IReferenceFinder referenceFinder;
/**
* Normalizes the method name.
*
* @param simpleName
* @return
*/
public static String toPropertyName(String simpleName) {
if (simpleName == null) {
return null;
}
String tempName = null;
if (isSetter(simpleName)) {
tempName = StringExtensions.toFirstLower(simpleName.replaceFirst(
"set", ""));
} else if (isGetter(simpleName)) {
if (simpleName.startsWith("get")) {
tempName = StringExtensions.toFirstLower(simpleName
.replaceFirst("get", ""));
} else {
tempName = StringExtensions.toFirstLower(simpleName
.replaceFirst("is", ""));
}
}
return tempName;
}
public static boolean isGetter(String simpleName) {
if (simpleName == null) {
return false;
}
return simpleName.startsWith("get") || simpleName.startsWith("is");
}
public static boolean isSetter(String simpleName) {
return simpleName != null && simpleName.startsWith("set");
}
/**
* Calculates the operation infos for the given type.
*
* @param type
* @return
*/
public static Map<String, Info> getOperationInfos(JvmDeclaredType type) {
return getOperationInfos(type, null);
}
/**
* Calculates the operation infos for the given info.
*
* @param info
* @return
*/
public static Map<String, Info> getOperationInfos(Info info) {
JvmType type = null;
if (info.isMany()) {
type = info.getParameterizedType();
} else {
type = info.getType();
}
Map<String, Info> result = null;
if (type instanceof JvmDeclaredType) {
result = getOperationInfos((JvmDeclaredType) type);
} else {
result = new HashMap<String, JvmTypeProperties.Info>();
}
// apply the info as a parent
for (Info temp : result.values()) {
temp.setParent(info);
}
return result;
}
/**
* Calculates the operation infos for the given type.
*
* @param type
* @param filterName
* - is used to filter only methods property names matching the
* filter name.
* @return
*/
public static Map<String, Info> getOperationInfos(JvmDeclaredType type,
String filterName) {
Map<String, Info> infos = new HashMap<String, Info>();
for (JvmFeature feature : type.getAllFeatures()) {
if (!(feature instanceof JvmOperation)) {
continue;
}
JvmOperation operation = (JvmOperation) feature;
if (operation.getVisibility() != JvmVisibility.PUBLIC) {
continue;
}
if (!isSetter(operation.getSimpleName())
&& operation.getParameters().size() > 1) {
continue;
}
String propertyName = toPropertyName(operation.getSimpleName());
if (propertyName == null) {
continue;
}
if (filterName != null && !filterName.equals(propertyName)) {
continue;
}
if (operation.getSimpleName().equals("getClass")) {
continue;
}
if (!isGetter(operation.getSimpleName())
&& !isSetter(operation.getSimpleName())) {
continue;
}
String id = calcId(operation.getDeclaringType(),
operation.getSimpleName());
if (!infos.containsKey(id)) {
Info info = new Info();
info.id = id;
info.name = propertyName;
infos.put(id, info);
}
Info info = infos.get(id);
if (isGetter(operation.getSimpleName())) {
info.getter = operation;
} else {
if (!propertyName.equals("dirty")) {
info.setter = operation;
}
}
}
// apply readonly and create descriptions
for (Info info : infos.values()) {
if (info.getter == null) {
continue;
}
if (info.setter == null) {
info.readonly = true;
}
}
for (JvmFeature member : type.getAllFeatures()) {
if (member instanceof JvmField) {
JvmField field = (JvmField) member;
String id = calcFieldId(field.getDeclaringType(),
field.getSimpleName());
if (infos.containsKey(id)) {
Info info = infos.get(id);
info.setField(field);
info.type = field.getType().getType();
info.primitive = info.type instanceof JvmPrimitiveType
|| info.type.getQualifiedName().equals(
String.class.getName());
// collect all super types and check if collection is part
// of them
Set<String> superTypes = new RawSuperTypes()
.collectNames(info.type);
for (String typeName : superTypes) {
if (typeName.equals(Collection.class.getName())) {
info.many = true;
break;
}
}
if (info.many) {
JvmParameterizedTypeReference typeRef = (JvmParameterizedTypeReference) field
.getType();
if (!typeRef.getArguments().isEmpty()) {
info.parameterizedType = typeRef.getArguments()
.get(0).getType();
}
}
}
}
}
return infos;
}
/**
* Normalizes the name.
*
* @param declaringType
* @param simpleName
* @return
*/
public static String calcId(JvmDeclaredType declaringType, String simpleName) {
String tempName = toPropertyName(simpleName);
if (tempName == null) {
return null;
}
return declaringType.getQualifiedName() + ":" + tempName;
}
/**
* Normalizes the name.
*
* @param declaringType
* @param simpleName
* @return
*/
public static String calcFieldId(JvmDeclaredType declaringType,
String simpleName) {
return declaringType.getQualifiedName() + ":" + simpleName;
}
/**
* Finds all sub types of the given dtoType.
*/
public Set<URI> findSubTypes(JvmType dtoType, ResourceSet rs) {
Set<URI> result = new HashSet<>();
Set<URI> jvmTypeURIs = new HashSet<>();
jvmTypeURIs.add(EcoreUtil.getURI(dtoType));
IAcceptor<IReferenceDescription> acceptor = e -> {
if (e.getEReference() == TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE__TYPE) {
if (e.getSourceEObjectUri().fragment()
.endsWith("/@superTypes.0")) {
URI uri = e.getSourceEObjectUri();
result.add(URI.createURI(uri.toString().replace(
"/@superTypes.0", "")));
}
}
};
referenceFinder.findAllReferences(jvmTypeURIs, new LocalResourceAccess(
rs), acceptor, null);
return result;
}
public static class Info implements Comparable<Info> {
/**
* Can by any object that requested the info. For instance a YBeanSlot,
* an Entity, a JvmField,... The root should only be set for the top
* most parent.
*/
private Object root;
/**
* The parent which requested this instance of info.
*/
private Info parent;
private String id;
private String name;
private boolean readonly;
private boolean primitive;
private JvmOperation getter;
private JvmOperation setter;
private JvmField field;
private JvmType type;
private JvmType parameterizedType;
private boolean many;
public Info getParent() {
return parent;
}
public void setParent(Info parent) {
this.parent = parent;
}
public Info getTopParent() {
if (getParent() != null) {
return getParent().getTopParent();
}
return this;
}
public Object getRoot() {
if (this.root == null && getParent() != null) {
return getParent().getRoot();
}
return root;
}
public void setRoot(Object root) {
this.root = root;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isReadonly() {
return readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
}
public JvmOperation getGetter() {
return getter;
}
public void setGetter(JvmOperation getter) {
this.getter = getter;
}
public JvmOperation getSetter() {
return setter;
}
public void setSetter(JvmOperation setter) {
this.setter = setter;
}
public JvmField getField() {
return field;
}
public void setField(JvmField field) {
this.field = field;
}
public boolean isPrimitive() {
return primitive;
}
public void setPrimitive(boolean primitive) {
this.primitive = primitive;
}
public JvmType getType() {
return type;
}
public void setType(JvmType type) {
this.type = type;
}
public boolean isMany() {
return many;
}
public void setMany(boolean many) {
this.many = many;
}
public JvmType getParameterizedType() {
return parameterizedType;
}
public void setParameterizedType(JvmType parameterizedType) {
this.parameterizedType = parameterizedType;
}
/**
* Returns true, if the info has an annotation matching the given
* annotationType.
*
* @param annotationType
* @return
*/
public boolean hasAnnotation(Class<?> annotationType) {
if (field == null) {
return false;
}
for (JvmAnnotationReference annotation : field.getAnnotations()) {
if (annotation.getAnnotation().getQualifiedName()
.equals(annotationType.getName())) {
return true;
}
}
return false;
}
/**
* Returns the dot'ed attribute path for this info and its parents. For
* instance <code>person.address.name</code>.
*
* @return
*/
public String getAttributePath() {
return getAttributePath(parent, name);
}
protected String getAttributePath(Info parent, String postFix) {
if (parent != null) {
String temp = parent.getAttributePath();
return temp + "." + postFix;
} else {
return postFix;
}
}
public boolean isRange() {
return hasAnnotation(Range.class);
}
public boolean isFilter() {
return hasAnnotation(Filter.class);
}
public boolean isUniqueEntry() {
return hasAnnotation(UniqueEntry.class);
}
@Override
public int compareTo(Info other) {
if (name == null || other == null) {
return -1;
}
return name.compareTo(other.getName());
}
}
}