/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.codehaus.groovy.transform.stc; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.ReturnStatement; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * The default type checking handler is used by the standard type checker and doesn't handle * any of the type checking errors by default. This just means that whenever a type checking * error is detected, there's no additional information available to the type checker that * could help it. * * The default handler is also capable of handling a collection of delegate handlers. If a list * of delegates is set, then the type checker will try all the delegates until one is capable * of handling an error. * * @author Cedric Champeau * @since 2.1.0 */ public class DefaultTypeCheckingExtension extends TypeCheckingExtension { protected final List<TypeCheckingExtension> handlers = new LinkedList<TypeCheckingExtension>(); public DefaultTypeCheckingExtension(final StaticTypeCheckingVisitor typeCheckingVisitor) { super(typeCheckingVisitor); } public void addHandler(TypeCheckingExtension handler) { handlers.add(handler); } public void removeHandler(TypeCheckingExtension handler) { handlers.remove(handler); } public boolean handleUnresolvedVariableExpression(VariableExpression vexp) { for (TypeCheckingExtension handler : handlers) { if (handler.handleUnresolvedVariableExpression(vexp)) return true; } return false; } public boolean handleUnresolvedProperty(final PropertyExpression pexp) { for (TypeCheckingExtension handler : handlers) { if (handler.handleUnresolvedProperty(pexp)) return true; } return false; } public boolean handleUnresolvedAttribute(final AttributeExpression aexp) { for (TypeCheckingExtension handler : handlers) { if (handler.handleUnresolvedAttribute(aexp)) return true; } return false; } @Override public boolean handleIncompatibleAssignment(final ClassNode lhsType, final ClassNode rhsType, final Expression assignmentExpression) { for (TypeCheckingExtension handler : handlers) { if (handler.handleIncompatibleAssignment(lhsType, rhsType, assignmentExpression)) return true; } return false; } @Override public boolean handleIncompatibleReturnType(ReturnStatement returnStatement, ClassNode inferredReturnType) { for (TypeCheckingExtension handler : handlers) { if (handler.handleIncompatibleReturnType(returnStatement, inferredReturnType)) return true; } return false; } @Override public List<MethodNode> handleAmbiguousMethods(final List<MethodNode> nodes, final Expression origin) { List<MethodNode> result = nodes; Iterator<TypeCheckingExtension> it = handlers.iterator(); while (result.size()>1 && it.hasNext()) { result = it.next().handleAmbiguousMethods(result, origin); } return result; } public List<MethodNode> handleMissingMethod(final ClassNode receiver, final String name, final ArgumentListExpression argumentList, final ClassNode[] argumentTypes, final MethodCall call) { List<MethodNode> result = new LinkedList<MethodNode>(); for (TypeCheckingExtension handler : handlers) { List<MethodNode> handlerResult = handler.handleMissingMethod(receiver, name, argumentList, argumentTypes, call); for (MethodNode mn : handlerResult) { if (mn.getDeclaringClass()==null) { mn.setDeclaringClass(ClassHelper.OBJECT_TYPE); } } result.addAll(handlerResult); } return result; } @Override public void afterVisitMethod(final MethodNode node) { for (TypeCheckingExtension handler : handlers) { handler.afterVisitMethod(node); } } @Override public boolean beforeVisitMethod(final MethodNode node) { for (TypeCheckingExtension handler : handlers) { if (handler.beforeVisitMethod(node)) return true; } return false; } @Override public void afterVisitClass(final ClassNode node) { for (TypeCheckingExtension handler : handlers) { handler.afterVisitClass(node); } } @Override public boolean beforeVisitClass(final ClassNode node) { for (TypeCheckingExtension handler : handlers) { if (handler.beforeVisitClass(node)) return true; } return false; } @Override public void afterMethodCall(final MethodCall call) { for (TypeCheckingExtension handler : handlers) { handler.afterMethodCall(call); } } @Override public boolean beforeMethodCall(final MethodCall call) { for (TypeCheckingExtension handler : handlers) { if (handler.beforeMethodCall(call)) return true; } return false; } @Override public void onMethodSelection(final Expression expression, final MethodNode target) { for (TypeCheckingExtension handler : handlers) { handler.onMethodSelection(expression, target); } } @Override public void setup() { ArrayList<TypeCheckingExtension> copy = new ArrayList<TypeCheckingExtension>(handlers); // we're using a copy here because new extensions can be added during the "setup" phase for (TypeCheckingExtension handler : copy) { handler.setup(); } } @Override public void finish() { for (TypeCheckingExtension handler : handlers) { handler.finish(); } } }