/*
* Copyright © 2010-2012 Philipp Eichhorn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.eclipse.handlers;
import static lombok.ast.AST.*;
import static lombok.core.util.ErrorMessages.*;
import static lombok.eclipse.handlers.Eclipse.ensureAllClassScopeMethodWereBuild;
import java.util.*;
import lombok.*;
import lombok.ast.Argument;
import lombok.ast.Expression;
import lombok.core.AnnotationValues;
import lombok.core.handlers.ListenerSupportHandler;
import lombok.core.util.As;
import lombok.core.util.Each;
import lombok.eclipse.DeferUntilBuildFieldsAndMethods;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.ast.EclipseType;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.mangosdk.spi.ProviderFor;
/**
* Handles the {@link ListenerSupport} annotation for eclipse using the {@link PatchListenerSupport}.
*/
@ProviderFor(EclipseAnnotationHandler.class)
@DeferUntilBuildFieldsAndMethods
public class HandleListenerSupport extends EclipseAnnotationHandler<ListenerSupport> {
private final EclipseListenerSupportHandler handler = new EclipseListenerSupportHandler();
@Override
public void handle(final AnnotationValues<ListenerSupport> annotation, final Annotation source, final EclipseNode annotationNode) {
EclipseType type = EclipseType.typeOf(annotationNode, source);
if (type.isAnnotation() || type.isInterface()) {
annotationNode.addError(canBeUsedOnClassAndEnumOnly(ListenerSupport.class));
return;
}
List<Object> listenerInterfaces = annotation.getActualExpressions("value");
if (listenerInterfaces.isEmpty()) {
annotationNode.addError(String.format("@%s has no effect since no interface types were specified.", ListenerSupport.class.getName()));
return;
}
for (Object listenerInterface : listenerInterfaces) {
if (listenerInterface instanceof ClassLiteralAccess) {
TypeBinding binding = ((ClassLiteralAccess) listenerInterface).type.resolveType(type.get().initializerScope);
if (binding == null) continue;
if (!binding.isInterface()) {
annotationNode.addWarning(String.format("@%s works only with interfaces. %s was skipped", ListenerSupport.class.getName(), As.string(binding.readableName())));
continue;
}
handler.addListenerField(type, binding);
handler.addAddListenerMethod(type, binding);
handler.addRemoveListenerMethod(type, binding);
addFireListenerMethods(type, binding);
}
}
type.editor().rebuild();
}
private void addFireListenerMethods(final EclipseType type, final TypeBinding interfaze) {
List<MethodBinding> methods = getInterfaceMethods(interfaze);
for (MethodBinding method : methods) {
handler.addFireListenerMethod(type, interfaze, method);
}
}
private List<MethodBinding> getInterfaceMethods(final TypeBinding binding) {
List<MethodBinding> methods = new ArrayList<MethodBinding>();
getInterfaceMethods(binding, methods, new HashSet<String>());
return methods;
}
private void getInterfaceMethods(final TypeBinding binding, final List<MethodBinding> methods, final Set<String> banList) {
if (binding == null) return;
ensureAllClassScopeMethodWereBuild(binding);
if (binding instanceof ReferenceBinding) {
ReferenceBinding rb = (ReferenceBinding) binding;
MethodBinding[] availableMethods = rb.availableMethods();
for (MethodBinding mb : Each.elementIn(availableMethods)) {
String sig = As.string(mb.readableName());
if (!banList.add(sig)) continue;
methods.add(mb);
}
ReferenceBinding[] interfaces = rb.superInterfaces();
for (ReferenceBinding iface : Each.elementIn(interfaces)) {
getInterfaceMethods(iface, methods, banList);
}
}
}
private static class EclipseListenerSupportHandler extends ListenerSupportHandler<EclipseType> {
@Override
protected void createParamsAndArgs(final Object method, final List<Argument> params, final List<Expression<?>> args) {
MethodBinding methodBinding = (MethodBinding) method;
int argCounter = 0;
for (TypeBinding parameter : Each.elementIn(methodBinding.parameters)) {
String arg = "arg" + argCounter++;
params.add(Arg(Type(parameter), arg));
args.add(Name(arg));
}
}
@Override
protected String name(final Object object) {
if (object instanceof MethodBinding) {
return As.string(((MethodBinding) object).selector);
} else {
return As.string(((Binding) object).shortReadableName());
}
}
@Override
protected Object type(final Object object) {
return object;
}
}
}