1 /*
2 * Copyright 2003-2007 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.codehaus.groovy.runtime.metaclass;
18
19 import groovy.lang;
20
21 import org.codehaus.groovy.reflection;
22 import org.codehaus.groovy.runtime.InvokerHelper;
23 import org.codehaus.groovy.runtime.MetaClassHelper;
24 import org.codehaus.groovy.runtime.callsite.CallSite;
25 import org.codehaus.groovy.runtime.callsite.PogoMetaClassSite;
26 import org.codehaus.groovy.runtime.wrappers.Wrapper;
27 import org.codehaus.groovy.util.FastArray;
28
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.Method;
31 import java.util;
32
33 /**
34 * A meta class for closures generated by the Groovy compiler. These classes
35 * have special characteristics this MetaClass uses. One of these is that a
36 * generated Closure has only additional doCall methods, all other methods
37 * are in the Closure class as well. To use this fact this MetaClass uses
38 * a MetaClass for Closure as static field And delegates calls to this
39 * MetaClass if needed. This allows a lean implementation for this MetaClass.
40 * Multiple generated closures will then use the same MetaClass for Closure.
41 * For static dispatching this class uses the MetaClass of Class, again
42 * all instances of this class will share that MetaClass. The Class MetaClass
43 * is initialized lazy, because most operations do not need this MetaClass.
44 * <p/>
45 * The Closure and Class MetaClasses are not replaceable.
46 * <p/>
47 * This MetaClass is for internal usage only!
48 *
49 * @author Jochen Theodorou
50 * @since 1.5
51 */
52 public final class ClosureMetaClass extends MetaClassImpl {
53 private boolean initialized;
54 private final FastArray closureMethods = new FastArray(3);
55 private Map attributes = new HashMap();
56 private MethodChooser chooser;
57 private volatile boolean attributeInitDone = false;
58
59 private static final MetaClassImpl CLOSURE_METACLASS;
60 private static MetaClassImpl classMetaClass;
61 private static final Object[] EMPTY_ARGUMENTS = {};
62 private static final String CLOSURE_CALL_METHOD = "call";
63 private static final String CLOSURE_DO_CALL_METHOD = "doCall";
64 private static final String CLOSURE_CURRY_METHOD = "curry";
65
66 static {
67 CLOSURE_METACLASS = new MetaClassImpl(Closure.class);
68 CLOSURE_METACLASS.initialize();
69 }
70
71 private static synchronized MetaClass getStaticMetaClass() {
72 if (classMetaClass == null) {
73 classMetaClass = new MetaClassImpl(Class.class);
74 classMetaClass.initialize();
75 }
76 return classMetaClass;
77 }
78
79 private interface MethodChooser {
80 Object chooseMethod(Class[] arguments, boolean coerce);
81 }
82
83 private static class StandardClosureChooser implements MethodChooser {
84 private final MetaMethod doCall0;
85 private final MetaMethod doCall1;
86
87 StandardClosureChooser(MetaMethod m0, MetaMethod m1) {
88 doCall0 = m0;
89 doCall1 = m1;
90 }
91
92 public Object chooseMethod(Class[] arguments, boolean coerce) {
93 if (arguments.length == 0) return doCall0;
94 if (arguments.length == 1) return doCall1;
95 return null;
96 }
97 }
98
99 private static class NormalMethodChooser implements MethodChooser {
100 private final FastArray methods;
101 final Class theClass;
102
103 NormalMethodChooser(Class theClass, FastArray methods) {
104 this.theClass = theClass;
105 this.methods = methods;
106 }
107
108 public Object chooseMethod(Class[] arguments, boolean coerce) {
109 if (arguments.length == 0) {
110 return MetaClassHelper.chooseEmptyMethodParams(methods);
111 } else if (arguments.length == 1 && arguments[0] == null) {
112 return MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
113 } else {
114 List matchingMethods = new ArrayList();
115
116 final int len = methods.size();
117 final Object[] data = methods.getArray();
118 for (int i = 0; i != len; ++i) {
119 Object method = data[i];
120
121 // making this false helps find matches
122 if (((ParameterTypes) method).isValidMethod(arguments)) {
123 matchingMethods.add(method);
124 }
125 }
126 if (matchingMethods.isEmpty()) {
127 return null;
128 } else if (matchingMethods.size() == 1) {
129 return matchingMethods.get(0);
130 }
131 return chooseMostSpecificParams(CLOSURE_DO_CALL_METHOD, matchingMethods, arguments);
132 }
133 }
134
135 private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
136 long matchesDistance = -1;
137 LinkedList matches = new LinkedList();
138 for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
139 Object method = iter.next();
140 final ParameterTypes parameterTypes = (ParameterTypes) method;
141 Class[] paramTypes = parameterTypes.getNativeParameterTypes();
142 if (!MetaClassHelper.parametersAreCompatible(arguments, paramTypes)) continue;
143 long dist = MetaClassHelper.calculateParameterDistance(arguments, parameterTypes);
144 if (dist == 0) return method;
145 if (matches.isEmpty()) {
146 matches.add(method);
147 matchesDistance = dist;
148 } else if (dist < matchesDistance) {
149 matchesDistance = dist;
150 matches.clear();
151 matches.add(method);
152 } else if (dist == matchesDistance) {
153 matches.add(method);
154 }
155
156 }
157 if (matches.size() == 1) {
158 return matches.getFirst();
159 }
160 if (matches.isEmpty()) {
161 return null;
162 }
163
164 //more than one matching method found --> ambigous!
165 String msg = "Ambiguous method overloading for method ";
166 msg += theClass.getName() + "#" + name;
167 msg += ".\nCannot resolve which method to invoke for ";
168 msg += InvokerHelper.toString(arguments);
169 msg += " due to overlapping prototypes between:";
170 for (Iterator iter = matches.iterator(); iter.hasNext();) {
171 CachedClass[] types = ((ParameterTypes) iter.next()).getParameterTypes();
172 msg += "\n\t" + InvokerHelper.toString(types);
173 }
174 throw new GroovyRuntimeException(msg);
175 }
176 }
177
178
179 public ClosureMetaClass(MetaClassRegistry registry, Class theClass) {
180 super(registry, theClass);
181 }
182
183 public MetaProperty getMetaProperty(String name) {
184 return CLOSURE_METACLASS.getMetaProperty(name);
185 }
186
187 private void unwrap(Object[] arguments) {
188 for (int i = 0; i != arguments.length; i++) {
189 if (arguments[i] instanceof Wrapper) {
190 arguments[i] = ((Wrapper) arguments[i]).unwrap();
191 }
192 }
193 }
194
195 private MetaMethod pickClosureMethod(Class[] argClasses) {
196 Object answer = chooser.chooseMethod(argClasses, false);
197 return (MetaMethod) answer;
198 }
199
200 private MetaMethod getDelegateMethod(Closure closure, Object delegate, String methodName, Class[] argClasses) {
201 if (delegate == closure || delegate == null) return null;
202 MetaClass delegateMetaClass;
203 if (delegate instanceof Class) {
204 delegateMetaClass = registry.getMetaClass((Class)delegate);
205 return delegateMetaClass.getStaticMetaMethod(methodName, argClasses);
206 }
207 else {
208 delegateMetaClass = lookupObjectMetaClass(delegate);
209 MetaMethod method = delegateMetaClass.pickMethod(methodName, argClasses);
210 if (method != null) {
211 return method;
212 }
213
214 if (delegateMetaClass instanceof ExpandoMetaClass) {
215 method = ((ExpandoMetaClass)delegateMetaClass).findMixinMethod(methodName, argClasses);
216
217 if (method != null) {
218 onMixinMethodFound(method);
219 return method;
220 }
221 }
222
223 if (delegateMetaClass instanceof MetaClassImpl) {
224 method = MetaClassImpl.findMethodInClassHierarchy(getTheClass(), methodName, argClasses, this);
225 if(method != null) {
226 onSuperMethodFoundInHierarchy(method);
227 return method;
228 }
229 }
230
231 return method;
232 }
233 }
234
235 public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
236 checkInitalised();
237 if (object == null) {
238 throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
239 }
240
241 final Object[] arguments = makeArguments(originalArguments, methodName);
242 final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
243 unwrap(arguments);
244
245 MetaMethod method;
246 final Closure closure = (Closure) object;
247
248 if (CLOSURE_DO_CALL_METHOD.equals(methodName) || CLOSURE_CALL_METHOD.equals(methodName)) {
249 method = pickClosureMethod(argClasses);
250 if (method==null && arguments.length==1 && arguments[0] instanceof List) {
251 Object[] newArguments = ((List) arguments[0]).toArray();
252 Class[] newArgClasses = MetaClassHelper.convertToTypeArray(newArguments);
253 method = pickClosureMethod(newArgClasses);
254 if (method!=null) {
255 method = new TransformMetaMethod(method) {
256 public Object invoke(Object object, Object[] arguments) {
257 Object firstArgument = arguments[0];
258 List list = (List) firstArgument;
259 arguments = list.toArray();
260 return super.invoke(object, arguments);
261 }
262 };
263 }
264 }
265 if (method==null) throw new MissingMethodException(methodName, theClass, arguments, false);
266 } else if (CLOSURE_CURRY_METHOD.equals(methodName)) {
267 return closure.curry(arguments);
268 } else {
269 method = CLOSURE_METACLASS.pickMethod(methodName, argClasses);
270 }
271
272 if (method != null) return method.doMethodInvoke(object, arguments);
273
274 MissingMethodException last = null;
275 Object callObject = object;
276 if (method == null) {
277 final Object owner = closure.getOwner();
278 final Object delegate = closure.getDelegate();
279 final Object thisObject = closure.getThisObject();
280 final int resolveStrategy = closure.getResolveStrategy();
281 boolean invokeOnDelegate = false;
282 boolean invokeOnOwner = false;
283 boolean ownerFirst = true;
284
285 switch (resolveStrategy) {
286 case Closure.TO_SELF:
287 break;
288 case Closure.DELEGATE_ONLY:
289 method = getDelegateMethod(closure, delegate, methodName, argClasses);
290 callObject = delegate;
291 if (method == null) {
292 invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject);
293 }
294 break;
295 case Closure.OWNER_ONLY:
296 method = getDelegateMethod(closure, owner, methodName, argClasses);
297 callObject = owner;
298 if (method == null) {
299 invokeOnOwner = owner != closure && (owner instanceof GroovyObject);
300 }
301
302 break;
303 case Closure.DELEGATE_FIRST:
304 method = getDelegateMethod(closure, delegate, methodName, argClasses);
305 callObject = delegate;
306 if (method == null) {
307 method = getDelegateMethod(closure, owner, methodName, argClasses);
308 callObject = owner;
309 }
310 if (method == null) {
311 invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject);
312 invokeOnOwner = owner != closure && (owner instanceof GroovyObject);
313 ownerFirst = false;
314 }
315 break;
316 default: // owner first
317 // owner first means we start with the outer most owner that is not a generated closure
318 // this owner is equal to the this object, so we check that one first.
319 method = getDelegateMethod(closure, thisObject, methodName, argClasses);
320 callObject = thisObject;
321 if (method == null) {
322 //try finding a delegate that has that method... we start from
323 // outside building a stack and try each delegate
324 LinkedList list = new LinkedList();
325 for (Object current = closure; current!=thisObject; ) {
326 Closure currentClosure = (Closure) current;
327 if (currentClosure.getDelegate()!=null) list.add(current);
328 current=currentClosure.getOwner();
329 }
330
331 while (!list.isEmpty() && method==null) {
332 Closure closureWithDelegate = (Closure) list.removeLast();
333 Object currentDelegate = closureWithDelegate.getDelegate();
334 method = getDelegateMethod(closureWithDelegate,currentDelegate,methodName,argClasses);
335 callObject = currentDelegate;
336 }
337 }
338 if (method == null) {
339 invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject);
340 invokeOnOwner = owner != closure && (owner instanceof GroovyObject);
341 }
342 }
343 if (method == null && (invokeOnOwner || invokeOnDelegate)) {
344 try {
345 if (ownerFirst) {
346 return invokeOnDelegationObjects(invokeOnOwner, owner, invokeOnDelegate, delegate, methodName, arguments);
347 } else {
348 return invokeOnDelegationObjects(invokeOnDelegate, delegate, invokeOnOwner, owner, methodName, arguments);
349 }
350 } catch (MissingMethodException mme) {
351 last = mme;
352 }
353 }
354 }
355
356 if (method != null) {
357 MetaClass metaClass = registry.getMetaClass(callObject.getClass());
358 if(metaClass instanceof ProxyMetaClass) {
359 return metaClass.invokeMethod(callObject, methodName, arguments);
360 } else {
361 return method.doMethodInvoke(callObject, arguments);
362 }
363 } else {
364 // if no method was found, try to find a closure defined as a field of the class and run it
365 Object value = null;
366 try {
367 value = this.getProperty(object, methodName);
368 } catch (MissingPropertyException mpe) {
369 // ignore
370 }
371 if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this
372 Closure cl = (Closure) value;
373 MetaClass delegateMetaClass = cl.getMetaClass();
374 return delegateMetaClass.invokeMethod(cl.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
375 }
376 }
377
378 if (last != null) throw last;
379 throw new MissingMethodException(methodName, theClass, arguments, false);
380 }
381
382 private Object[] makeArguments(Object[] arguments, String methodName) {
383 if (arguments == null) return EMPTY_ARGUMENTS;
384 return arguments;
385 }
386
387 private static Throwable unwrap(GroovyRuntimeException gre) {
388 Throwable th = gre;
389 if (th.getCause() != null && th.getCause() != gre) th = th.getCause();
390 if (th != gre && (th instanceof GroovyRuntimeException)) return unwrap((GroovyRuntimeException) th);
391 return th;
392 }
393
394 private Object invokeOnDelegationObjects(
395 boolean invoke1, Object o1,
396 boolean invoke2, Object o2,
397 String methodName, Object[] args) {
398 MissingMethodException first = null;
399 if (invoke1) {
400 GroovyObject go = (GroovyObject) o1;
401 try {
402 return go.invokeMethod(methodName, args);
403 } catch (MissingMethodException mme) {
404 first = mme;
405 } catch (GroovyRuntimeException gre) {
406 Throwable th = unwrap(gre);
407 if ((th instanceof MissingMethodException)
408 && (methodName.equals(((MissingMethodException)th).getMethod())))
409 {
410 first = (MissingMethodException) th;
411 } else {
412 throw gre;
413 }
414 }
415 }
416 if (invoke2 && (!invoke1 || o1 != o2)) {
417 GroovyObject go = (GroovyObject) o2;
418 try {
419 return go.invokeMethod(methodName, args);
420 } catch (MissingMethodException mme) {
421 // patch needed here too, but we need a test case to trip it first
422 if (first == null) first = mme;
423 } catch (GroovyRuntimeException gre) {
424 Throwable th = unwrap(gre);
425 if (th instanceof MissingMethodException) {
426 first = (MissingMethodException) th;
427 } else {
428 throw gre;
429 }
430 }
431 }
432 throw first;
433 }
434
435 private synchronized void initAttributes() {
436 if (!attributes.isEmpty()) return;
437 attributes.put("!", null); // just a dummy for later
438 CachedField[] fieldArray = theCachedClass.getFields();
439 for (int i = 0; i < fieldArray.length; i++) {
440 attributes.put(fieldArray[i].getName(), fieldArray[i]);
441 }
442 attributeInitDone = !attributes.isEmpty();
443 }
444
445 public synchronized void initialize() {
446 if (!isInitialized()) {
447 CachedMethod[] methodArray = theCachedClass.getMethods();
448 synchronized (theCachedClass) {
449 for (int i = 0; i < methodArray.length; i++) {
450 final CachedMethod cachedMethod = methodArray[i];
451 if (!cachedMethod.getName().equals(CLOSURE_DO_CALL_METHOD)) continue;
452 MetaMethod method = cachedMethod;
453 closureMethods.add(method);
454 }
455 }
456 assignMethodChooser();
457
458 initialized = true;
459 }
460 }
461
462 private void assignMethodChooser() {
463 if (closureMethods.size() == 1) {
464 final MetaMethod doCall = (MetaMethod) closureMethods.get(0);
465 final CachedClass[] c = doCall.getParameterTypes();
466 int length = c.length;
467 if (length == 0) {
468 // no arg method
469 chooser = new MethodChooser() {
470 public Object chooseMethod(Class[] arguments, boolean coerce) {
471 if (arguments.length == 0) return doCall;
472 return null;
473 }
474 };
475 } else {
476 if (length == 1 && c[0].getTheClass() == Object.class) {
477 // Object fits all, so simple dispatch rule here
478 chooser = new MethodChooser() {
479 public Object chooseMethod(Class[] arguments, boolean coerce) {
480 // <2, because foo() is same as foo(null)
481 if (arguments.length < 2) return doCall;
482 return null;
483 }
484 };
485 } else {
486 boolean allObject = true;
487 for (int i = 0; i < c.length - 1; i++) {
488 if (c[i].getTheClass() != Object.class) {
489 allObject = false;
490 break;
491 }
492 }
493 if (allObject && c[c.length - 1].getTheClass() == Object.class) {
494 // all arguments are object, so test only if argument number is correct
495 chooser = new MethodChooser() {
496 public Object chooseMethod(Class[] arguments, boolean coerce) {
497 if (arguments.length == c.length) return doCall;
498 return null;
499 }
500 };
501 } else {
502 if (allObject && c[c.length - 1].getTheClass() == Object[].class) {
503 // all arguments are Object but last, which is a vargs argument, that
504 // will fit all, so jsut test if the number of argument is equal or
505 // more than the parameters we have.
506 final int minimumLength = c.length - 2;
507 chooser = new MethodChooser() {
508 public Object chooseMethod(Class[] arguments, boolean coerce) {
509 if (arguments.length > minimumLength) return doCall;
510 return null;
511 }
512 };
513 } else {
514 // general case for single method
515 chooser = new MethodChooser() {
516 public Object chooseMethod(Class[] arguments, boolean coerce) {
517 if (doCall.isValidMethod(arguments)) {
518 return doCall;
519 }
520 return null;
521 }
522 };
523 }
524 }
525 }
526 }
527 } else if (closureMethods.size() == 2) {
528 MetaMethod m0 = null, m1 = null;
529 for (int i = 0; i != closureMethods.size(); ++i) {
530 MetaMethod m = (MetaMethod) closureMethods.get(i);
531 CachedClass[] c = m.getParameterTypes();
532 if (c.length == 0) {
533 m0 = m;
534 } else {
535 if (c.length == 1 && c[0].getTheClass() == Object.class) {
536 m1 = m;
537 }
538 }
539 }
540 if (m0 != null && m1 != null) {
541 // standard closure (2 methods because "it" is with default null)
542 chooser = new StandardClosureChooser(m0, m1);
543 }
544 }
545 if (chooser == null) {
546 // standard chooser for cases if it is not a single method and if it is
547 // not the standard closure.
548 chooser = new NormalMethodChooser(theClass, closureMethods);
549 }
550 }
551
552 private MetaClass lookupObjectMetaClass(Object object) {
553 if (object instanceof GroovyObject) {
554 GroovyObject go = (GroovyObject) object;
555 return go.getMetaClass();
556 }
557 Class ownerClass = object.getClass();
558 if (ownerClass == Class.class) {
559 ownerClass = (Class) object;
560 return registry.getMetaClass(ownerClass);
561 }
562 MetaClass metaClass = InvokerHelper.getMetaClass(object);
563 return metaClass;
564 }
565
566 @Override
567 public List<MetaMethod> getMethods() {
568 List<MetaMethod> answer = CLOSURE_METACLASS.getMetaMethods();
569 answer.addAll(closureMethods.toList());
570 return answer;
571 }
572
573 @Override
574 public List<MetaMethod> getMetaMethods() {
575 return CLOSURE_METACLASS.getMetaMethods();
576 }
577
578 @Override
579 public List<MetaProperty> getProperties() {
580 return CLOSURE_METACLASS.getProperties();
581 }
582
583 @Override
584 public MetaMethod pickMethod(String name, Class[] argTypes) {
585 if (argTypes == null) argTypes = MetaClassHelper.EMPTY_CLASS_ARRAY;
586 if (name.equals(CLOSURE_CALL_METHOD) || name.equals(CLOSURE_DO_CALL_METHOD)) {
587 return pickClosureMethod(argTypes);
588 }
589 return CLOSURE_METACLASS.getMetaMethod(name, argTypes);
590 }
591
592 public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
593 return null;
594 }
595
596 protected boolean isInitialized() {
597 return initialized;
598 }
599
600 public MetaMethod getStaticMetaMethod(String name, Object[] args) {
601 return CLOSURE_METACLASS.getStaticMetaMethod(name, args);
602 }
603
604 public MetaMethod getStaticMetaMethod(String name, Class[] argTypes) {
605 return CLOSURE_METACLASS.getStaticMetaMethod(name, argTypes);
606 }
607
608 public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
609 if (object instanceof Class) {
610 return getStaticMetaClass().getProperty(sender, object, name, useSuper, fromInsideClass);
611 } else {
612 return CLOSURE_METACLASS.getProperty(sender, object, name, useSuper, fromInsideClass);
613 }
614 }
615
616 @Override
617 public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) {
618 if (object instanceof Class) {
619 return getStaticMetaClass().getAttribute(sender, object, attribute, useSuper);
620 } else {
621 if (!attributeInitDone) initAttributes();
622 CachedField mfp = (CachedField) attributes.get(attribute);
623 if (mfp == null) {
624 return CLOSURE_METACLASS.getAttribute(sender, object, attribute, useSuper);
625 } else {
626 return mfp.getProperty(object);
627 }
628 }
629 }
630
631 @Override
632 public void setAttribute(Class sender, Object object, String attribute,
633 Object newValue, boolean useSuper, boolean fromInsideClass) {
634 if (object instanceof Class) {
635 getStaticMetaClass().setAttribute(sender, object, attribute, newValue, useSuper, fromInsideClass);
636 } else {
637 if (!attributeInitDone) initAttributes();
638 CachedField mfp = (CachedField) attributes.get(attribute);
639 if (mfp == null) {
640 CLOSURE_METACLASS.setAttribute(sender, object, attribute, newValue, useSuper, fromInsideClass);
641 } else {
642 mfp.setProperty(object, newValue);
643 }
644 }
645 }
646
647 public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
648 return getStaticMetaClass().invokeMethod(Class.class, object, methodName, arguments, false, false);
649 }
650
651 public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
652 if (object instanceof Class) {
653 getStaticMetaClass().setProperty(sender, object, name, newValue, useSuper, fromInsideClass);
654 } else {
655 CLOSURE_METACLASS.setProperty(sender, object, name, newValue, useSuper, fromInsideClass);
656 }
657 }
658
659 public MetaMethod getMethodWithoutCaching(int index, Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
660 throw new UnsupportedOperationException();
661 }
662
663 public void setProperties(Object bean, Map map) {
664 throw new UnsupportedOperationException();
665 }
666
667 public void addMetaBeanProperty(MetaBeanProperty mp) {
668 throw new UnsupportedOperationException();
669 }
670
671 public void addMetaMethod(MetaMethod method) {
672 throw new UnsupportedOperationException();
673 }
674
675 public void addNewInstanceMethod(Method method) {
676 throw new UnsupportedOperationException();
677 }
678
679 public void addNewStaticMethod(Method method) {
680 throw new UnsupportedOperationException();
681 }
682
683 public Constructor retrieveConstructor(Class[] arguments) {
684 throw new UnsupportedOperationException();
685 }
686
687 public CallSite createPojoCallSite(CallSite site, Object receiver, Object[] args) {
688 throw new UnsupportedOperationException();
689 }
690
691 public CallSite createPogoCallSite(CallSite site, Object[] args) {
692 return new PogoMetaClassSite(site, this);
693 }
694
695 public CallSite createPogoCallCurrentSite(CallSite site, Class sender, Object[] args) {
696 return new PogoMetaClassSite(site, this);
697 }
698 }