/* * 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.reflection; import groovy.lang.DelegatingMetaClass; import groovy.lang.ExpandoMetaClass; import groovy.lang.GroovyRuntimeException; import groovy.lang.GroovySystem; import groovy.lang.MetaClass; import groovy.lang.MetaMethod; import groovy.lang.MetaProperty; import org.codehaus.groovy.runtime.HandleMetaClass; import org.codehaus.groovy.runtime.MetaClassHelper; import org.codehaus.groovy.runtime.metaclass.MixedInMetaClass; import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod; import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaProperty; import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod; import org.codehaus.groovy.util.ManagedConcurrentMap; import org.codehaus.groovy.util.ReferenceBundle; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; public class MixinInMetaClass extends ManagedConcurrentMap { final ExpandoMetaClass emc; final CachedClass mixinClass; final CachedConstructor constructor; private static final ReferenceBundle softBundle = ReferenceBundle.getSoftBundle(); public MixinInMetaClass(ExpandoMetaClass emc, CachedClass mixinClass) { super(softBundle); this.emc = emc; this.mixinClass = mixinClass; constructor = findDefaultConstructor(mixinClass); emc.addMixinClass(this); } private static CachedConstructor findDefaultConstructor(CachedClass mixinClass) { for (CachedConstructor constr : mixinClass.getConstructors()) { if (!Modifier.isPublic(constr.getModifiers())) continue; CachedClass[] classes = constr.getParameterTypes(); if (classes.length == 0) return constr; } throw new GroovyRuntimeException("No default constructor for class " + mixinClass.getName() + "! Can't be mixed in."); } public synchronized Object getMixinInstance(Object object) { Object mixinInstance = get(object); if (mixinInstance == null) { mixinInstance = constructor.invoke(MetaClassHelper.EMPTY_ARRAY); new MixedInMetaClass(mixinInstance, object); put(object, mixinInstance); } return mixinInstance; } public synchronized void setMixinInstance(Object object, Object mixinInstance) { if (mixinInstance == null) { remove(object); } else { put(object, mixinInstance); } } public CachedClass getInstanceClass() { return emc.getTheCachedClass(); } public CachedClass getMixinClass() { return mixinClass; } public static void mixinClassesToMetaClass(MetaClass self, List<Class> categoryClasses) { final Class selfClass = self.getTheClass(); if (self instanceof HandleMetaClass) { self = (MetaClass) ((HandleMetaClass) self).replaceDelegate(); } if (!(self instanceof ExpandoMetaClass)) { if (self instanceof DelegatingMetaClass && ((DelegatingMetaClass) self).getAdaptee() instanceof ExpandoMetaClass) { self = ((DelegatingMetaClass) self).getAdaptee(); } else { throw new GroovyRuntimeException("Can't mixin methods to meta class: " + self); } } ExpandoMetaClass mc = (ExpandoMetaClass) self; List<MetaMethod> arr = new ArrayList<MetaMethod>(); for (Class categoryClass : categoryClasses) { final CachedClass cachedCategoryClass = ReflectionCache.getCachedClass(categoryClass); final MixinInMetaClass mixin = new MixinInMetaClass(mc, cachedCategoryClass); final MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(categoryClass); final List<MetaProperty> propList = metaClass.getProperties(); for (MetaProperty prop : propList) if (self.getMetaProperty(prop.getName()) == null) { mc.registerBeanProperty(prop.getName(), new MixinInstanceMetaProperty(prop, mixin)); } for (MetaProperty prop : cachedCategoryClass.getFields()) if (self.getMetaProperty(prop.getName()) == null) { mc.registerBeanProperty(prop.getName(), new MixinInstanceMetaProperty(prop, mixin)); } for (MetaMethod method : metaClass.getMethods()) { final int mod = method.getModifiers(); if (!Modifier.isPublic(mod)) continue; if (method instanceof CachedMethod && ((CachedMethod) method).getCachedMethod().isSynthetic()) continue; if (Modifier.isStatic(mod)) { if (method instanceof CachedMethod) staticMethod(self, arr, (CachedMethod) method); } else if (method.getDeclaringClass().getTheClass() != Object.class || method.getName().equals("toString")) { // if (self.pickMethod(method.getName(), method.getNativeParameterTypes()) == null) { final MixinInstanceMetaMethod metaMethod = new MixinInstanceMetaMethod(method, mixin); arr.add(metaMethod); // } } } } for (Object res : arr) { final MetaMethod metaMethod = (MetaMethod) res; if (metaMethod.getDeclaringClass().isAssignableFrom(selfClass)) mc.registerInstanceMethod(metaMethod); else { mc.registerSubclassInstanceMethod(metaMethod); } } } private static void staticMethod(final MetaClass self, List<MetaMethod> arr, final CachedMethod method) { CachedClass[] paramTypes = method.getParameterTypes(); if (paramTypes.length == 0) return; NewInstanceMetaMethod metaMethod; if (paramTypes[0].isAssignableFrom(self.getTheClass())) { if (paramTypes[0].getTheClass() == self.getTheClass()) metaMethod = new NewInstanceMetaMethod(method); else metaMethod = new NewInstanceMetaMethod(method) { public CachedClass getDeclaringClass() { return ReflectionCache.getCachedClass(self.getTheClass()); } }; arr.add(metaMethod); } else { if (self.getTheClass().isAssignableFrom(paramTypes[0].getTheClass())) { metaMethod = new NewInstanceMetaMethod(method); arr.add(metaMethod); } } } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof MixinInMetaClass)) return false; if (!super.equals(o)) return false; MixinInMetaClass that = (MixinInMetaClass) o; if (mixinClass != null ? !mixinClass.equals(that.mixinClass) : that.mixinClass != null) return false; return true; } public int hashCode() { int result = super.hashCode(); result = 31 * result + (emc != null ? emc.hashCode() : 0); result = 31 * result + (mixinClass != null ? mixinClass.hashCode() : 0); result = 31 * result + (constructor != null ? constructor.hashCode() : 0); return result; } }