blob: 4343060ea819ede7382567d0ad64862180fde218 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 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.operation.form.formdata;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.Flags;
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.CompositeObject;
import org.eclipse.scout.sdk.RuntimeClasses;
import org.eclipse.scout.sdk.internal.ScoutSdk;
import org.eclipse.scout.sdk.util.resources.ResourceUtility;
import org.eclipse.scout.sdk.util.signature.IImportValidator;
import org.eclipse.scout.sdk.util.signature.SignatureUtility;
import org.eclipse.scout.sdk.util.type.IPropertyBean;
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.type.ScoutPropertyBeanFilters;
import org.eclipse.scout.sdk.workspace.type.ScoutTypeUtility;
import org.eclipse.scout.sdk.workspace.type.validationrule.ValidationRuleMethod;
/**
* <h3>{@link SourceBuilderWithProperties}</h3> ...
*
* @author Andreas Hoegger
* @since 1.0.8 21.02.2011
*/
public class SourceBuilderWithProperties extends TypeSourceBuilder {
private static Pattern REGEX_STRING_LITERALS = Pattern.compile("\"+[^\"]+\"", Pattern.DOTALL);
public SourceBuilderWithProperties(final IType type) {
super(ResourceUtility.getLineSeparator(type.getOpenable()));
visitProperties(type);
addValidationRules(type);
}
protected void visitProperties(IType type) {
IPropertyBean[] beanPropertyDescriptors = TypeUtility.getPropertyBeans(type, ScoutPropertyBeanFilters.getFormDataPropertyFilter(), PropertyBeanComparators.getNameComparator());
if (beanPropertyDescriptors != null) {
for (IPropertyBean desc : beanPropertyDescriptors) {
try {
if (desc.getReadMethod() != null || desc.getWriteMethod() != null) {
if (FormDataAnnotation.isCreate(ScoutTypeUtility.findFormDataAnnotation(desc.getReadMethod())) &&
FormDataAnnotation.isCreate(ScoutTypeUtility.findFormDataAnnotation(desc.getWriteMethod()))) {
String beanName = FormDataUtility.getValidMethodParameterName(desc.getBeanName());
String lowerCaseBeanName = FormDataUtility.getBeanName(beanName, false);
String upperCaseBeanName = FormDataUtility.getBeanName(beanName, true);
String propName = upperCaseBeanName + "Property";
String resolvedSignature = SignatureUtility.getResolvedSignature(desc.getBeanSignature(), desc.getDeclaringType());
String unboxedSignature = FormDataUtility.unboxPrimitiveSignature(resolvedSignature);
// property class
TypeSourceBuilder propertyBuilder = new TypeSourceBuilder(NL);
propertyBuilder.setElementName(propName);
String superTypeSig = Signature.createTypeSignature(RuntimeClasses.AbstractPropertyData, true);
superTypeSig = superTypeSig.replaceAll("\\;$", "<" + unboxedSignature + ">;");
propertyBuilder.setSuperTypeSignature(superTypeSig);
addBuilder(propertyBuilder, CATEGORY_TYPE_PROPERTY);
// getter
MethodSourceBuilder getterBuilder = new MethodSourceBuilder(NL);
getterBuilder.setElementName("get" + propName);
getterBuilder.setReturnSignature(Signature.createTypeSignature(propName, false));
getterBuilder.setSimpleBody("return getPropertyByClass(" + propName + ".class);");
addBuilder(getterBuilder, new CompositeObject(CATEGORY_METHOD_PROPERTY, lowerCaseBeanName, 1, getterBuilder));
// legacy getter
MethodSourceBuilder legacyGetter = new MethodSourceBuilder(NL);
legacyGetter.setJavaDoc(" /** " + NL + " * access method for property " + upperCaseBeanName + "." + NL + "*/");
legacyGetter.setElementName((Signature.SIG_BOOLEAN.equals(resolvedSignature) ? "is" : "get") + upperCaseBeanName);
legacyGetter.setReturnSignature(resolvedSignature);
legacyGetter.setSimpleBody(getLegacyGetterMethodBody(resolvedSignature, propName));
addBuilder(legacyGetter, new CompositeObject(CATEGORY_METHOD_PROPERTY, lowerCaseBeanName, 2, legacyGetter));
// legacy setter
MethodSourceBuilder legacySetter = new MethodSourceBuilder(NL);
legacySetter.setJavaDoc(" /** " + NL + " * access method for property " + upperCaseBeanName + "." + NL + "*/");
legacySetter.setElementName("set" + upperCaseBeanName);
legacySetter.addParameter(new MethodParameter(resolvedSignature, lowerCaseBeanName));
legacySetter.setSimpleBody("get" + propName + "().setValue(" + lowerCaseBeanName + ");");
addBuilder(legacySetter, new CompositeObject(CATEGORY_METHOD_PROPERTY, lowerCaseBeanName, 3, legacySetter));
}
}
}
catch (JavaModelException 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();
}
protected void addValidationRules(IType type) {
// validation rules
try {
boolean replaceAnnotationPresent = ScoutTypeUtility.isReplaceAnnotationPresent(type);
ITypeHierarchy hierarchy = TypeUtility.getSuperTypeHierarchy(type);
if (hierarchy == null) {
return;
}
// If the replace annotation is available, we have to check whether the replaced field
// is not associated to a form field data
boolean superTypeHasNoFormFieldData = false;
if (replaceAnnotationPresent) {
IType superType = hierarchy.getSuperclass(type);
superTypeHasNoFormFieldData = ScoutTypeUtility.getFormDataType(superType, hierarchy) == null;
}
List<ValidationRuleMethod> list = ScoutTypeUtility.getValidationRuleMethods(type, hierarchy.getJdtHierarchy());
if (list.size() > 0) {
for (Iterator<ValidationRuleMethod> it = list.iterator(); it.hasNext();) {
ValidationRuleMethod vm = it.next();
if (replaceAnnotationPresent) {
if (superTypeHasNoFormFieldData && vm.isSkipRule()) {
it.remove();
continue;
}
else if (!superTypeHasNoFormFieldData && !type.equals(vm.getImplementedMethod().getDeclaringType())) {
// remove all validation rules that are not overridden by the replacement class
it.remove();
continue;
}
}
else if (vm.isSkipRule()) {
// class does not replace its super class. Hence remove all skipped validation rules
it.remove();
continue;
}
String generatedSourceCode = vm.getRuleGeneratedSourceCode();
if (generatedSourceCode != null) {
if (generatedSourceCode.equals("null")) {
it.remove();
continue;
}
if (generatedSourceCode.equals("false")) {
it.remove();
continue;
}
}
}
if (list.size() > 0) {
ValidationRuleMethodOverrideBuilder builder = new ValidationRuleMethodOverrideBuilder(list, NL);
builder.setJavaDoc(" /** " + NL + " * list of derived validation rules." + NL + "*/");
builder.addAnnotation(new AnnotationSourceBuilder("Ljava.lang.Override;"));
builder.setFlags(Flags.AccProtected);
builder.setElementName("initValidationRules");
builder.addParameter(new MethodParameter(Signature.createTypeSignature("java.util.Map<String,Object>", false), "ruleMap"));
addBuilder(builder, 4);
}
}
}
catch (Throwable t) {
ScoutSdk.logError("could not append validation rules to form field data '" + type.getFullyQualifiedName() + "'.", t);
}
}
private static boolean containsBrackets(String genSource) {
String srcWithoutStrings = REGEX_STRING_LITERALS.matcher(genSource).replaceAll("");
return srcWithoutStrings != null && srcWithoutStrings.contains("(");
}
private static class ValidationRuleMethodOverrideBuilder extends MethodSourceBuilder {
private final List<ValidationRuleMethod> m_methods;
public ValidationRuleMethodOverrideBuilder(List<ValidationRuleMethod> methods, String nl) {
super(nl);
m_methods = methods;
}
@Override
protected String createMethodBody(IImportValidator validator) {
//validator.addImport("java.util.Map");
StringBuilder buf = new StringBuilder();
buf.append("super.initValidationRules(ruleMap);");
for (ValidationRuleMethod vm : m_methods) {
try {
String generatedSourceCode = vm.getRuleGeneratedSourceCode();
//filter
generatedSourceCode = filterGeneratedSourceCode(vm.getImplementedMethod(), generatedSourceCode, validator);
if (!vm.isSkipRule() && (generatedSourceCode == null || containsBrackets(generatedSourceCode))) {
//add javadoc warning
String fqn = vm.getImplementedMethod().getDeclaringType().getFullyQualifiedName('.') + " # " + vm.getImplementedMethod().getElementName();
buf.append("/**");
buf.append(NL);
buf.append(" * XXX not processed ValidationRule(" + vm.getRuleName() + ")");
buf.append(NL);
buf.append(" * generatedSourceCode: ");
buf.append(generatedSourceCode);
buf.append(NL);
buf.append(" * at " + fqn);
buf.append(NL);
buf.append("*/");
continue;
}
//
String ruleDecl;
if (vm.getRuleField() != null) {
validator.addImport(vm.getRuleField().getDeclaringType().getFullyQualifiedName());
ruleDecl = vm.getRuleField().getDeclaringType().getElementName() + "." + vm.getRuleField().getElementName();
}
else {
ruleDecl = "\"" + vm.getRuleName() + "\"";
}
//
buf.append(NL);
if (vm.isSkipRule()) {
buf.append("ruleMap.remove(");
buf.append(ruleDecl);
buf.append(");");
}
else {
buf.append("ruleMap.put(");
buf.append(ruleDecl);
buf.append(", ");
buf.append(generatedSourceCode);
buf.append(");");
}
}
catch (Exception e) {
String fqn = vm.getImplementedMethod().getDeclaringType().getFullyQualifiedName() + "#" + vm.getImplementedMethod().getElementName();
ScoutSdk.logError("could not append rule " + vm.getRuleName() + " from method " + fqn + ".", e);
}
}
return buf.toString();
}
private String filterGeneratedSourceCode(IMethod sourceMethod, String sourceSnippet, IImportValidator targetValidator) throws CoreException {
if (sourceSnippet != null) {
IType[] refTypes = ScoutTypeUtility.getTypeOccurenceInSnippet(sourceMethod, sourceSnippet);
for (IType refType : refTypes) {
//if the type is a form field type it is transformed to the corresponding form data field
ITypeHierarchy h = TypeUtility.getSuperTypeHierarchy(refType);
if (h.contains(TypeUtility.getType(RuntimeClasses.IFormField))) {
String formDataFieldName = FormDataUtility.getBeanName(FormDataUtility.getFieldNameWithoutSuffix(refType.getElementName()), true);
return formDataFieldName + ".class";
}
//other client types are not supported
String fqn = refType.getFullyQualifiedName();
//XXX imo: aho, is there a better way to find out if targetValidator would accept that type in the import section?
//XXX aho: yes, follows soon: if(TypeUtility.isOnClasspath(refType, targetValidator.getTargetProject())){
if (fqn.indexOf(".client.") >= 0) {
return null;
}
targetValidator.addImport(fqn);
}
}
return sourceSnippet;
}
}
}