001    /*
002     * Copyright 2005 John G. Wilson
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     *
016     */
017    
018    package groovy.lang;
019    
020    import java.lang.reflect.Method;
021    import java.util.List;
022    import java.util.logging.Logger;
023    
024    import org.codehaus.groovy.ast.ClassNode;
025    import org.codehaus.groovy.runtime.MetaClassHelper;
026    
027    /**
028     * Base class for meta class implementations. 
029     * The meta class is used to invoke methods or to get 
030     * fields/properties. For proper initialization of this class 
031     * it is not enough to only call the constructor, the
032     * initialize() must be called too. The invoke methods should
033     * check that initialize() was called. Adding methods is
034     * valid unless initilise method was called. Therefore 
035     * addNewStaticMethod and addNewInstanceMethod should check that
036     * that initilise awas not called before.
037     * 
038     * 
039     * @author John Wilson
040     *
041     */
042    
043    public abstract class MetaClass {
044        protected static final Logger log = Logger.getLogger(MetaClass.class.getName());
045        protected static boolean useReflection = false;
046        public static final Object NO_METHOD_FOUND = new Object();
047        protected final Class theClass;
048        private boolean isGroovyObject;
049        
050        public static boolean isUseReflection() {
051            return MetaClass.useReflection;
052        }
053    
054        /**
055         * Allows reflection to be enabled in situations where bytecode generation
056         * of method invocations causes issues.
057         *
058         * @param useReflection
059         */
060        public static void setUseReflection(boolean useReflection) {
061            MetaClass.useReflection = useReflection;
062        }
063        
064        protected MetaClass(final Class theClass) {
065            this.theClass = theClass;
066            isGroovyObject = GroovyObject.class.isAssignableFrom(theClass);
067        }
068        
069        public boolean isGroovyObject(){
070            return isGroovyObject;
071        }
072        
073        public Object invokeMissingMethod(Object instance, String methodName, Object[] arguments) {
074            GroovyObject pogo = (GroovyObject) instance;
075            return pogo.invokeMethod(methodName,arguments);
076        }
077        
078        public Object invokeMethod(Object object, String methodName, Object arguments) {
079            if (arguments == null) {
080                return invokeMethod(object, methodName, MetaClassHelper.EMPTY_ARRAY);
081            }
082            if (arguments instanceof Tuple) {
083                Tuple tuple = (Tuple) arguments;
084                return invokeMethod(object, methodName, tuple.toArray());
085            }
086            if (arguments instanceof Object[]) {
087                return invokeMethod(object, methodName, (Object[])arguments);
088            }
089            else {
090                return invokeMethod(object, methodName, new Object[]{arguments});
091            }
092        }
093        
094        public Object invokeMethod(Class sender, Object receiver, String methodName, Object[] arguments, boolean isCallToSuper, boolean fromInsideClass){
095            return invokeMethod(receiver,methodName,arguments);
096        }
097        
098        public Object getProperty(Class sender, Object receiver, String messageName, boolean useSuper, boolean fromInsideClass) {
099            return getProperty(receiver,messageName);
100        }
101        
102        public void setProperty(Class sender, Object receiver, String messageName, Object messageValue, boolean useSuper, boolean fromInsideClass) {
103            setProperty(receiver,messageName,messageValue);
104        }
105        
106        public Object getAttribute(Class sender, Object receiver, String messageName, boolean useSuper) {
107            return getAttribute(receiver,messageName);
108        }
109        
110        public void setAttribute(Class sender, Object receiver, String messageName, Object messageValue, boolean useSuper, boolean fromInsideClass) {
111            setAttribute(receiver,messageName,messageValue);
112        }
113        
114        public abstract Object invokeConstructor(Object[] arguments);
115        public abstract Object invokeMethod(Object object, String methodName, Object[] arguments);
116        public abstract Object invokeStaticMethod(Object object, String methodName, Object[] arguments);
117        public abstract Object getProperty(Object object, String property);
118        public abstract void setProperty(Object object, String property, Object newValue);
119        public abstract Object getAttribute(Object object, String attribute);
120        public abstract void setAttribute(Object object, String attribute, Object newValue);
121        /**
122         * adds a new instance method to this meta class. Instance
123         * methods are able to overwrite the original methods of the
124         * class. Calling this method should not be done after 
125         * initlise was called.
126         * @param method the method to be added
127         */
128        public abstract void addNewInstanceMethod(Method method);
129        /**
130         * adds a new static method to this meta class. This is only
131         * possible as long as initilise was not called.
132         * @param method the method to be added
133         */
134        public abstract void addNewStaticMethod(Method method);
135        /**
136         * complete the initlialisation process. After this method
137         * is called no methods should be added to the meta class.
138         * Invocation of methods or access to fields/proeprties is
139         * forbidden unless this method is called. This method 
140         * should contain any initialisation code, taking a longer
141         * time to complete. An example is the creation of the 
142         * Reflector. It is suggested to synchronize this 
143         * method.
144         */
145        public abstract void initialize();
146        
147        public abstract List getProperties();
148        public abstract ClassNode getClassNode();
149        public abstract List getMetaMethods();
150        
151        public abstract List getMethods();
152        
153        /**
154         * Warning, this method will be removed until 1.0
155         * @deprecated
156         */
157        public Object invokeConstructorAt(Class at, Object[] arguments) {
158            return invokeConstructor(arguments);
159        }
160    
161        /**
162         * Warning, this method will be removed until 1.0
163         * @deprecated
164         */
165        public abstract MetaMethod pickMethod(String methodName, Class[] arguments);
166        
167        /**
168         * Warning, this method will be removed until 1.0
169         * @deprecated
170         */
171        protected abstract MetaMethod retrieveMethod(String methodName, Class[] arguments);
172    
173    }