/* * 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.runtime; import groovy.lang.Closure; import groovy.lang.MetaMethod; import org.codehaus.groovy.reflection.CachedConstructor; import org.codehaus.groovy.reflection.ReflectionCache; import java.io.IOException; import java.util.Arrays; import java.util.List; /** * Represents a method on an object using a closure which can be invoked * at any time * * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> */ public class MethodClosure extends Closure { public static final String NEW = "new"; public static final String ANY_INSTANCE_METHOD_EXISTS = "anyInstanceMethodExists"; public static boolean ALLOW_RESOLVE = false; private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; private final String method; private final boolean anyInstanceMethodExists; // whether the method closure is related to instance method public MethodClosure(Object owner, String method) { super(owner); this.method = method; final Class clazz = owner.getClass() == Class.class ? (Class) owner : owner.getClass(); this.maximumNumberOfParameters = 0; this.parameterTypes = EMPTY_CLASS_ARRAY; List<MetaMethod> methods = InvokerHelper.getMetaClass(clazz).respondsTo(owner, method); int instanceMethodCnt = 0; for (MetaMethod m : methods) { Class[] newParameterTypes = this.makeParameterTypes(owner, m); this.setParameterTypesAndNumber(newParameterTypes); if (!m.isStatic()) { instanceMethodCnt++; } } this.anyInstanceMethodExists = instanceMethodCnt > 0; if (NEW.equals(method)) { if (clazz.isArray()) { Class[] sizeTypes = new Class[ArrayTypeUtils.dimension(clazz)]; Arrays.fill(sizeTypes, int.class); this.setParameterTypesAndNumber(sizeTypes); } else { for (CachedConstructor c : ReflectionCache.getCachedClass(clazz).getConstructors()) { Class[] newParameterTypes = c.getNativeParameterTypes(); this.setParameterTypesAndNumber(newParameterTypes); } } } } private void setParameterTypesAndNumber(Class[] newParameterTypes) { if (!(newParameterTypes.length > this.maximumNumberOfParameters)) { return; } this.maximumNumberOfParameters = newParameterTypes.length; this.parameterTypes = newParameterTypes; } /* * Create a new array of parameter type. * * If the owner is a class instance(e.g. String) and the method is instance method, * we expand the original array of parameter type by inserting the owner at the first place of the expanded array */ private Class[] makeParameterTypes(Object owner, MetaMethod m) { Class[] newParameterTypes; if (owner instanceof Class && !m.isStatic()) { Class[] nativeParameterTypes = m.getNativeParameterTypes(); newParameterTypes = new Class[nativeParameterTypes.length + 1]; System.arraycopy(nativeParameterTypes, 0, newParameterTypes, 1, nativeParameterTypes.length); newParameterTypes[0] = (Class) owner; } else { newParameterTypes = m.getNativeParameterTypes(); } return newParameterTypes; } public String getMethod() { return method; } // TODO confirm: The "doCall" method seems to be never called..., because MetaClassImpl.invokeMethod will intercept calls and return the result protected Object doCall(Object arguments) { return InvokerHelper.invokeMethod(getOwner(), method, arguments); } private Object readResolve() { if (ALLOW_RESOLVE) { return this; } throw new UnsupportedOperationException(); } private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { if (ALLOW_RESOLVE) { stream.defaultReadObject(); } throw new UnsupportedOperationException(); } public Object getProperty(String property) { if ("method".equals(property)) { return getMethod(); } else if (ANY_INSTANCE_METHOD_EXISTS.equals(property)) { return this.anyInstanceMethodExists; } else return super.getProperty(property); } }