blob: 7fb6f4bbf82aae3b5fd15460f41aba852ccc1cc2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.sdk.internal.workspace.dto;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.scout.commons.CompareUtility;
import org.eclipse.scout.commons.StringUtility;
import org.eclipse.scout.sdk.extensions.runtime.classes.IRuntimeClasses;
import org.eclipse.scout.sdk.internal.ScoutSdk;
import org.eclipse.scout.sdk.sourcebuilder.SortedMemberKeyFactory;
import org.eclipse.scout.sdk.sourcebuilder.annotation.AnnotationSourceBuilder;
import org.eclipse.scout.sdk.sourcebuilder.comment.CommentSourceBuilderFactory;
import org.eclipse.scout.sdk.sourcebuilder.field.FieldSourceBuilderFactory;
import org.eclipse.scout.sdk.sourcebuilder.field.IFieldSourceBuilder;
import org.eclipse.scout.sdk.sourcebuilder.method.IMethodSourceBuilder;
import org.eclipse.scout.sdk.sourcebuilder.method.MethodBodySourceBuilderFactory;
import org.eclipse.scout.sdk.sourcebuilder.method.MethodSourceBuilder;
import org.eclipse.scout.sdk.sourcebuilder.method.MethodSourceBuilderFactory;
import org.eclipse.scout.sdk.sourcebuilder.type.ITypeSourceBuilder;
import org.eclipse.scout.sdk.sourcebuilder.type.TypeSourceBuilder;
import org.eclipse.scout.sdk.util.NamingUtility;
import org.eclipse.scout.sdk.util.jdt.JdtUtility;
import org.eclipse.scout.sdk.util.signature.IImportValidator;
import org.eclipse.scout.sdk.util.signature.SignatureCache;
import org.eclipse.scout.sdk.util.signature.SignatureUtility;
import org.eclipse.scout.sdk.util.type.IPropertyBean;
import org.eclipse.scout.sdk.util.type.MethodParameter;
import org.eclipse.scout.sdk.util.type.PropertyBeanComparators;
import org.eclipse.scout.sdk.util.type.TypeUtility;
import org.eclipse.scout.sdk.util.typecache.ITypeHierarchy;
import org.eclipse.scout.sdk.workspace.dto.formdata.FormDataAnnotation;
import org.eclipse.scout.sdk.workspace.type.ScoutPropertyBeanFilters;
import org.eclipse.scout.sdk.workspace.type.ScoutTypeUtility;
/**
* <h3>{@link AbstractDtoTypeSourceBuilder}</h3>
*
* @author Andreas Hoegger
* @since 3.10.0 27.08.2013
*/
public abstract class AbstractDtoTypeSourceBuilder extends TypeSourceBuilder {
private IType m_modelType;
private ITypeHierarchy m_localTypeHierarchy;
protected static final String SIG_FOR_IS_METHOD_NAME = Signature.SIG_BOOLEAN;
public AbstractDtoTypeSourceBuilder(IType modelType, String elementName, IProgressMonitor monitor) {
this(modelType, elementName, true, monitor);
}
/**
* @param elementName
*/
public AbstractDtoTypeSourceBuilder(IType modelType, String elementName, boolean setup, IProgressMonitor monitor) {
super(elementName);
m_modelType = modelType;
m_localTypeHierarchy = TypeUtility.getLocalTypeHierarchy(modelType);
if (setup) {
setup(monitor);
}
}
protected void setup(IProgressMonitor monitor) {
setupBuilder();
createContent(monitor);
}
/**
*
*/
protected void setupBuilder() {
// flags
int flags = Flags.AccPublic;
try {
if (Flags.isAbstract(getModelType().getFlags())) {
flags |= Flags.AccAbstract;
}
}
catch (JavaModelException e) {
ScoutSdk.logWarning("could not determ abstract flag of '" + getModelType().getFullyQualifiedName() + "'.", e);
}
setFlags(flags);
try {
setSuperTypeSignature(computeSuperTypeSignature());
}
catch (CoreException e) {
ScoutSdk.logError("could not calculate super type for '" + getElementName() + "'.", e);
}
}
protected void createContent(IProgressMonitor monitor) {
// constructor
IMethodSourceBuilder constructorBuilder = MethodSourceBuilderFactory.createConstructorSourceBuilder(getElementName());
addSortedMethodSourceBuilder(SortedMemberKeyFactory.createMethodConstructorKey(constructorBuilder), constructorBuilder);
// serial version uid
IFieldSourceBuilder serialVersionUidBuilder = FieldSourceBuilderFactory.createSerialVersionUidBuilder();
addSortedFieldSourceBuilder(SortedMemberKeyFactory.createFieldSerialVersionUidKey(serialVersionUidBuilder), serialVersionUidBuilder);
// copy annotations over to the DTO
copyAnnotations(getModelType(), getModelType(), this);
}
/**
* @return
*/
protected abstract String computeSuperTypeSignature() throws CoreException;
protected static void copyAnnotations(IAnnotatable annotationOwner, final IType declaringType, ITypeSourceBuilder sourceBuilder) {
IAnnotation[] annotations;
try {
annotations = annotationOwner.getAnnotations();
}
catch (JavaModelException e1) {
ScoutSdk.logError("Unable to retrieve annotations of element '" + annotationOwner.toString() + "'.", e1);
return;
}
for (IAnnotation a : annotations) {
try {
final IAnnotation annotation = a;
String elementName = annotation.getElementName();
if (!IRuntimeClasses.FormData.equals(elementName) && !IRuntimeClasses.Order.equals(elementName) && !IRuntimeClasses.PageData.equals(elementName) &&
!Signature.getSimpleName(IRuntimeClasses.FormData).equals(elementName) && !Signature.getSimpleName(IRuntimeClasses.Order).equals(elementName) &&
!Signature.getSimpleName(IRuntimeClasses.PageData).equals(elementName)) {
if (!NamingUtility.isFullyQualifiedName(elementName)) {
elementName = TypeUtility.getReferencedTypeFqn(declaringType, elementName, true);
}
final IType annotationDeclarationType = TypeUtility.getType(elementName);
if (isAnnotationDtoRelevant(annotationDeclarationType)) {
AnnotationSourceBuilder asb = new AnnotationSourceBuilder(SignatureCache.createTypeSignature(elementName)) {
@Override
public void createSource(StringBuilder source, String lineDelimiter, IJavaProject ownerProject, IImportValidator validator) throws CoreException {
// check if annotation is accessible
if (!TypeUtility.isOnClasspath(annotationDeclarationType, ownerProject)) {
ScoutSdk.logInfo("DTO relevant annotation '" + annotationDeclarationType.getFullyQualifiedName() + "' is not accessible from DTO project '" + ownerProject.getElementName() + "'. Annotation will be skipped.");
return;
}
// check if there is source available
String copySrc = annotation.getSource();
if (!StringUtility.hasText(copySrc)) {
ScoutSdk.logInfo("DTO relevant annotation '" + annotationDeclarationType.getFullyQualifiedName() + "' in type '" + declaringType.getFullyQualifiedName() + "' has no source code. Annotation will be skipped.");
return;
}
// check if all referenced types are accessible
List<IType> typeOccurenceInAnnotation = ScoutTypeUtility.getTypeOccurenceInAnnotation(annotation, declaringType);
for (IType t : typeOccurenceInAnnotation) {
if (!TypeUtility.isOnClasspath(t, ownerProject)) {
ScoutSdk.logInfo("Type '" + t.getFullyQualifiedName() + "' referenced in DTO relevant annotation '" + annotationDeclarationType.getFullyQualifiedName() + "' in type '" + declaringType.getFullyQualifiedName() + "' is not accessible from DTO project '" + ownerProject.getElementName() + "'. Annotation will be skipped.");
return;
}
}
IType classIdType = TypeUtility.getType(IRuntimeClasses.ClassId);
if (CompareUtility.equals(annotationDeclarationType, classIdType)) {
// classid append formdata
IMemberValuePair[] memberValuePairs = annotation.getMemberValuePairs();
if (memberValuePairs != null && memberValuePairs.length == 1 && memberValuePairs[0].getMemberName().equals("value")) {
copySrc = copySrc.replace(memberValuePairs[0].getValue().toString(), memberValuePairs[0].getValue().toString() + "-formdata");
}
}
// add imports to referenced types
for (IType t : typeOccurenceInAnnotation) {
validator.addImport(t.getFullyQualifiedName());
IType surroundingType = t.getDeclaringType();
while (surroundingType != null) {
validator.addImport(surroundingType.getFullyQualifiedName());
surroundingType = surroundingType.getDeclaringType();
}
}
// add annotation source
source.append(copySrc);
}
};
sourceBuilder.addAnnotationSourceBuilder(asb);
}
}
}
catch (JavaModelException e) {
ScoutSdk.logError("Error copying the annotation '" + a.getElementName() + "' in type '" + declaringType.getFullyQualifiedName() + "' to the form data.", e);
}
}
}
protected static boolean isAnnotationDtoRelevant(IType annotation) {
if (!TypeUtility.exists(annotation)) {
return false;
}
IAnnotation dtoReleventAnnotation = JdtUtility.getAnnotation(annotation, IRuntimeClasses.DtoRelevant);
return TypeUtility.exists(dtoReleventAnnotation);
}
public IType getModelType() {
return m_modelType;
}
public ITypeHierarchy getLocalTypeHierarchy() {
return m_localTypeHierarchy;
}
protected void collectProperties(IProgressMonitor monitor) {
Set<? extends IPropertyBean> beanPropertyDescriptors = TypeUtility.getPropertyBeans(getModelType(), ScoutPropertyBeanFilters.getFormDataPropertyFilter(), PropertyBeanComparators.getNameComparator());
for (IPropertyBean desc : beanPropertyDescriptors) {
try {
if (monitor.isCanceled()) {
return;
}
if (desc.getReadMethod() != null || desc.getWriteMethod() != null) {
if (FormDataAnnotation.isCreate(ScoutTypeUtility.findFormDataAnnotation(desc.getReadMethod())) &&
FormDataAnnotation.isCreate(ScoutTypeUtility.findFormDataAnnotation(desc.getWriteMethod()))) {
String beanName = NamingUtility.ensureValidParameterName(desc.getBeanName());
String lowerCaseBeanName = NamingUtility.ensureStartWithLowerCase(beanName);
final String upperCaseBeanName = NamingUtility.ensureStartWithUpperCase(beanName);
String propName = upperCaseBeanName + "Property";
String resolvedSignature = SignatureUtility.getResolvedSignature(desc.getBeanSignature(), desc.getDeclaringType());
if (!StringUtility.hasText(resolvedSignature)) {
resolvedSignature = SignatureCache.createTypeSignature(Object.class.getName());
}
String unboxedSignature = SignatureUtility.unboxPrimitiveSignature(resolvedSignature);
// property class
TypeSourceBuilder propertyTypeBuilder = new TypeSourceBuilder(propName);
propertyTypeBuilder.setFlags(Flags.AccPublic | Flags.AccStatic);
String superTypeSig = SignatureCache.createTypeSignature(IRuntimeClasses.AbstractPropertyData);
superTypeSig = DtoUtility.ENDING_SEMICOLON_PATTERN.matcher(superTypeSig).replaceAll("<" + unboxedSignature + ">;");
propertyTypeBuilder.setSuperTypeSignature(superTypeSig);
IFieldSourceBuilder serialVersionUidBuilder = FieldSourceBuilderFactory.createSerialVersionUidBuilder();
propertyTypeBuilder.addSortedFieldSourceBuilder(SortedMemberKeyFactory.createFieldSerialVersionUidKey(serialVersionUidBuilder), serialVersionUidBuilder);
IMethodSourceBuilder constructorBuilder = MethodSourceBuilderFactory.createConstructorSourceBuilder(propName);
propertyTypeBuilder.addSortedMethodSourceBuilder(SortedMemberKeyFactory.createMethodConstructorKey(constructorBuilder), constructorBuilder);
addSortedTypeSourceBuilder(SortedMemberKeyFactory.createTypeFormDataPropertyKey(propertyTypeBuilder), propertyTypeBuilder);
// copy annotations over to the DTO
IMethod propertyMethod = desc.getReadMethod();
if (!TypeUtility.exists(propertyMethod)) {
propertyMethod = desc.getWriteMethod();
}
if (TypeUtility.exists(propertyMethod)) {
copyAnnotations(propertyMethod, propertyMethod.getDeclaringType(), propertyTypeBuilder);
}
// getter
IMethodSourceBuilder propertyGetterBuilder = new MethodSourceBuilder("get" + propName);
propertyGetterBuilder.setFlags(Flags.AccPublic);
propertyGetterBuilder.setReturnTypeSignature(Signature.createTypeSignature(propName, false));
propertyGetterBuilder.setMethodBodySourceBuilder(MethodBodySourceBuilderFactory.createSimpleMethodBody("return getPropertyByClass(" + propName + ".class);"));
addSortedMethodSourceBuilder(SortedMemberKeyFactory.createMethodPropertyKey(propertyGetterBuilder), propertyGetterBuilder);
// legacy getter
IMethodSourceBuilder legacyPropertyGetterBuilder = new MethodSourceBuilder((SIG_FOR_IS_METHOD_NAME.equals(resolvedSignature) ? "is" : "get") + upperCaseBeanName);
legacyPropertyGetterBuilder.setCommentSourceBuilder(CommentSourceBuilderFactory.createCustomCommentBuilder("access method for property " + upperCaseBeanName + "."));
legacyPropertyGetterBuilder.setFlags(Flags.AccPublic);
legacyPropertyGetterBuilder.setReturnTypeSignature(resolvedSignature);
legacyPropertyGetterBuilder.setMethodBodySourceBuilder(MethodBodySourceBuilderFactory.createSimpleMethodBody(getLegacyGetterMethodBody(resolvedSignature, propName)));
addSortedMethodSourceBuilder(SortedMemberKeyFactory.createMethodPropertyKey(legacyPropertyGetterBuilder), legacyPropertyGetterBuilder);
// legacy setter
IMethodSourceBuilder legacyPropertySetterBuilder = new MethodSourceBuilder("set" + upperCaseBeanName);
legacyPropertySetterBuilder.setCommentSourceBuilder(CommentSourceBuilderFactory.createCustomCommentBuilder("access method for property " + upperCaseBeanName + "."));
legacyPropertySetterBuilder.setFlags(Flags.AccPublic);
legacyPropertySetterBuilder.setReturnTypeSignature(Signature.SIG_VOID);
legacyPropertySetterBuilder.addParameter(new MethodParameter(lowerCaseBeanName, resolvedSignature));
legacyPropertySetterBuilder.setMethodBodySourceBuilder(MethodBodySourceBuilderFactory.createSimpleMethodBody("get" + propName + "().setValue(" + lowerCaseBeanName + ");"));
addSortedMethodSourceBuilder(SortedMemberKeyFactory.createMethodPropertyKey(legacyPropertySetterBuilder), legacyPropertySetterBuilder);
}
}
}
catch (CoreException e) {
ScoutSdk.logError("could append property to form data '" + getElementName() + "'.", e);
}
}
}
private String getLegacyGetterMethodBody(String propertySignature, String propertyName) {
String nonArraySig = propertySignature;
StringBuilder source = new StringBuilder();
source.append("return ");
if (Signature.SIG_BOOLEAN.equals(nonArraySig)) {
source.append("(get" + propertyName + "().getValue() == null) ? (false) : (get" + propertyName + "().getValue());");
}
else if (Signature.SIG_BYTE.equals(nonArraySig)) {
source.append("(get" + propertyName + "().getValue() == null) ? (0) : (get" + propertyName + "().getValue());");
}
else if (Signature.SIG_CHAR.equals(nonArraySig)) {
source.append("(get" + propertyName + "().getValue() == null) ? ('\u0000') : (get" + propertyName + "().getValue());");
}
else if (Signature.SIG_DOUBLE.equals(nonArraySig)) {
source.append("(get" + propertyName + "().getValue() == null) ? (0.0d) : (get" + propertyName + "().getValue());");
}
else if (Signature.SIG_FLOAT.equals(nonArraySig)) {
source.append("(get" + propertyName + "().getValue() == null) ? (0.0f) : (get" + propertyName + "().getValue());");
}
else if (Signature.SIG_INT.equals(nonArraySig)) {
source.append("(get" + propertyName + "().getValue() == null) ? (0) : (get" + propertyName + "().getValue());");
}
else if (Signature.SIG_LONG.equals(nonArraySig)) {
source.append("(get" + propertyName + "().getValue() == null) ? (0L) : (get" + propertyName + "().getValue());");
}
else if (Signature.SIG_SHORT.equals(nonArraySig)) {
source.append("(get" + propertyName + "().getValue() == null) ? (0) : (get" + propertyName + "().getValue());");
}
else {
source.append("get" + propertyName + "().getValue();");
}
return source.toString();
}
}