/** * Copyright (c) 2009 Farata Systems http://www.faratasystems.com * * Licensed under The MIT License * Re-distributions of files must retain the above copyright notice. * * @license http://www.opensource.org/licenses/mit-license.php The MIT License * */ package com.farata.dto2extjs.asap.reflect; import java.util.Iterator; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.HashSet; import com.farata.dto2extjs.annotations.JSClass; import com.farata.dto2extjs.annotations.JSIgnore; import com.farata.dto2extjs.asap.types.JSTypeReflector; import com.sun.mirror.declaration.InterfaceDeclaration; import com.sun.mirror.declaration.MemberDeclaration; import com.sun.mirror.declaration.MethodDeclaration; import com.sun.mirror.declaration.Modifier; import com.sun.mirror.declaration.ParameterDeclaration; import com.sun.mirror.declaration.TypeDeclaration; import com.sun.mirror.type.InterfaceType; import com.sun.mirror.type.TypeMirror; import com.sun.mirror.util.Declarations; public class JSInterfaceDeclarationReflector extends JSTypeDeclarationReflector { public JSInterfaceDeclarationReflector(final InterfaceDeclaration interfaceDeclaration, final JSTypeReflector typeReflector) { super(interfaceDeclaration, typeReflector); } protected TypeDeclarationVisitor createVisitor() { return new TypeDeclarationVisitor() { protected TypeDeclarationKind getTypeKind() { return TypeDeclarationKind.INTERFACE; } @Override protected void preprocess() { final TypeDeclaration type = source; final Map<String, IJSPropertyDefinition> properties = _properties; final Set<MemberDeclaration> propertyMembers = _propertyMembers; final Set<MemberDeclaration> propertyDeclaringMembers = _propertyDeclaringMembers; properties.clear(); propertyMembers.clear(); propertyDeclaringMembers.clear(); final Map<String, MethodDeclaration> getters = new TreeMap<String, MethodDeclaration>(); final Map<String, Collection<MethodDeclaration>> setters = new TreeMap<String, Collection<MethodDeclaration>>(); for (final MethodDeclaration method : type.getMethods() ) { final String name = method.getSimpleName(); final int nameLength = name.length(); final Collection<Modifier> modifiers = method.getModifiers(); if ( modifiers.contains(Modifier.STATIC) || !modifiers.contains(Modifier.PUBLIC) ) continue; final Collection<ParameterDeclaration> params = method.getParameters(); final TypeMirror returnType = method.getReturnType(); if ( params.size() == 0 && (nameLength > 3 && name.startsWith("get") && !_types.isVoid(returnType) || nameLength > 2 && name.startsWith("is") && _types.isBoolean(returnType) ) ) getters.put(name, method); else if ( name.length() > 3 && name.startsWith("set") && params.size() == 1 && _types.isVoid( returnType ) ) { Collection<MethodDeclaration> settersByName = setters.get(name); if (null == settersByName) { settersByName = new HashSet<MethodDeclaration>(); setters.put(name, settersByName); } settersByName.add(method); } } // Iterate getters, then find corresponding setter // Create read-write property if matching pair found or read-only property for (final Iterator<Map.Entry<String, MethodDeclaration>> i = getters.entrySet().iterator(); i.hasNext(); ) { final Map.Entry<String, MethodDeclaration> entry = i.next(); final String getterName = entry.getKey(); final MethodDeclaration getter = entry.getValue(); final int prefixLength; if ( getterName.startsWith("get") ) prefixLength = 3; else if ( getterName.startsWith("is") ) prefixLength = 2; else prefixLength = 0; if ( prefixLength > 0) { final String setterName = "set" + getterName.substring(prefixLength); final Collection<MethodDeclaration> settersByName = setters.get(setterName); MethodDeclaration setter = null; if (null != settersByName) { boolean oneFound = false; for (final MethodDeclaration nextSetter : settersByName) { if ( nextSetter.getAnnotation(JSIgnore.class) != null ) continue; if (oneFound) { // If more then one setter found then should be an error break; } else { final TypeMirror argumentType = nextSetter.getParameters().iterator().next().getType(); if ( getter.getReturnType().equals(argumentType) ) { setter = nextSetter; oneFound = true; } } } } if (null != setter) setters.remove(setterName); if ( getter.getAnnotation(JSIgnore.class) != null ) continue; final JSMethodDeclarationKind declareGetter = checkOverride(getter); final JSMethodDeclarationKind declareSetter = setter != null ? checkOverride(setter) : JSMethodDeclarationKind.SKIP; if (declareGetter != JSMethodDeclarationKind.SKIP || declareSetter != JSMethodDeclarationKind.SKIP) { final String propertyName = Character.toLowerCase( getterName.charAt(prefixLength) ) + getterName.substring(prefixLength + 1); properties.put(propertyName, new JSPropertyDefinition( getter, propertyName, getter.getReturnType(), declareGetter, declareSetter, JSMethodDeclarationKind.SKIP == declareGetter || JSMethodDeclarationKind.SKIP == declareSetter || getter.getModifiers().contains(Modifier.ABSTRACT) || setter.getModifiers().contains(Modifier.ABSTRACT) ) ); } propertyMembers.add( getter ); if (null != setter) propertyMembers.add( setter ); propertyDeclaringMembers.add( getter ); } } // Iterate setters without matching getters // and create write-only properties for (final Iterator<Map.Entry<String, Collection<MethodDeclaration>>> i = setters.entrySet().iterator(); i.hasNext(); ) { final Map.Entry<String, Collection<MethodDeclaration>> entry = i.next(); final String name = entry.getKey(); final Collection<MethodDeclaration> settersByName = entry.getValue(); boolean oneFound = false; for (final MethodDeclaration setter : settersByName) { if ( setter.getAnnotation(JSIgnore.class) != null ) continue; if (oneFound) { // If more then one setter found then should be an error break; } else { final JSMethodDeclarationKind declareSetter = checkOverride(setter); if (JSMethodDeclarationKind.SKIP != declareSetter) { final String propertyName = Character.toLowerCase( name.charAt(3) ) + name.substring(4); properties.put(propertyName, new JSPropertyDefinition( setter, propertyName, setter.getParameters().iterator().next().getType(), JSMethodDeclarationKind.SKIP, declareSetter, true ) ); } propertyMembers.add( setter ); propertyDeclaringMembers.add( setter ); oneFound = true; } } } } protected JSMethodDeclarationKind checkOverride(final MethodDeclaration method) { final TypeDeclaration type = method.getDeclaringType(); for (final InterfaceType superInterface : type.getSuperinterfaces() ) { final TypeDeclaration superDeclaration = superInterface.getDeclaration(); if ( wasDefinedInInterface(method, superDeclaration) ) return JSMethodDeclarationKind.SKIP; } return JSMethodDeclarationKind.DECLARE; } protected boolean wasDefinedInInterface(final MethodDeclaration method, final TypeDeclaration intf) { final Declarations declarations = apt.getDeclarationUtils(); for (final MethodDeclaration other : intf.getMethods() ) { if ( null != other.getAnnotation(JSIgnore.class) ) continue; if ( declarations.overrides(method, other) ) return true; } for (final InterfaceType superInterface : intf.getSuperinterfaces() ) { final TypeDeclaration superDeclaration = superInterface.getDeclaration(); if ( null == superDeclaration.getAnnotation(JSClass.class) || null != superDeclaration.getAnnotation(JSIgnore.class) ) continue; if ( wasDefinedInInterface(method, superDeclaration) ) return true; } return false; } }; } }