/**
  * Sets a handle internally used to create MetaClass implementations. When replacing the handle
  * with a custom version, you should reuse the old handle to keep custom logic and to use the
  * default logic as fall back. WARNING: experimental code, likely to change soon
  *
  * @param handle the handle
  */
 public void setMetaClassCreationHandle(MetaClassCreationHandle handle) {
   if (handle == null)
     throw new IllegalArgumentException("Cannot set MetaClassCreationHandle to null value!");
   ClassInfo.clearModifiedExpandos();
   handle.setDisableCustomMetaClassLookup(
       metaClassCreationHandle.isDisableCustomMetaClassLookup());
   metaClassCreationHandle = handle;
 }
  public MetaClassRegistryImpl(final int loadDefault, final boolean useAccessible) {
    this.useAccessible = useAccessible;

    if (loadDefault == LOAD_DEFAULT) {
      Map<CachedClass, List<MetaMethod>> map = new HashMap<CachedClass, List<MetaMethod>>();

      // let's register the default methods
      registerMethods(null, true, true, map);
      final Class[] additionals = DefaultGroovyMethods.additionals;
      for (int i = 0; i != additionals.length; ++i) {
        createMetaMethodFromClass(map, additionals[i]);
      }

      Class[] pluginDGMs = VMPluginFactory.getPlugin().getPluginDefaultGroovyMethods();
      for (Class plugin : pluginDGMs) {
        registerMethods(plugin, false, true, map);
      }
      registerMethods(DefaultGroovyStaticMethods.class, false, false, map);
      Class[] staticPluginDGMs = VMPluginFactory.getPlugin().getPluginStaticGroovyMethods();
      for (Class plugin : staticPluginDGMs) {
        registerMethods(plugin, false, false, map);
      }

      for (Map.Entry<CachedClass, List<MetaMethod>> e : map.entrySet()) {
        CachedClass cls = e.getKey();
        cls.setNewMopMethods(e.getValue());
      }
    }

    installMetaClassCreationHandle();

    final MetaClass emcMetaClass = metaClassCreationHandle.create(ExpandoMetaClass.class, this);
    emcMetaClass.initialize();
    ClassInfo.getClassInfo(ExpandoMetaClass.class).setStrongMetaClass(emcMetaClass);

    addMetaClassRegistryChangeEventListener(
        new MetaClassRegistryChangeEventListener() {
          public void updateConstantMetaClass(MetaClassRegistryChangeEvent cmcu) {
            synchronized (metaClassInfo) {
              metaClassInfo.add(cmcu.getNewMetaClass());
              DefaultMetaClassInfo.getNewConstantMetaClassVersioning();
              Class c = cmcu.getClassToUpdate();
              DefaultMetaClassInfo.setPrimitiveMeta(c, cmcu.getNewMetaClass() == null);
              Field sdyn;
              try {
                sdyn = c.getDeclaredField(Verifier.STATIC_METACLASS_BOOL);
                sdyn.setBoolean(null, cmcu.getNewMetaClass() != null);
              } catch (Throwable e) {
                // DO NOTHING
              }
            }
          }
        });
  }
  public void setMetaClass(Object obj, MetaClass theMetaClass) {
    Class theClass = obj.getClass();
    final ClassInfo info = ClassInfo.getClassInfo(theClass);
    MetaClass oldMC = null;
    info.lock();
    try {
      oldMC = info.getPerInstanceMetaClass(obj);
      info.setPerInstanceMetaClass(obj, theMetaClass);
    } finally {
      info.unlock();
    }

    fireConstantMetaClassUpdate(obj, theClass, oldMC, theMetaClass);
  }
  /**
   * if oldMc is null, newMc will replace whatever meta class was used before. if oldMc is not null,
   * then newMc will be used only if he stored mc is the same as oldMc
   */
  private void setMetaClass(Class theClass, MetaClass oldMc, MetaClass newMc) {
    final ClassInfo info = ClassInfo.getClassInfo(theClass);

    MetaClass mc = null;
    info.lock();
    try {
      mc = info.getStrongMetaClass();
      info.setStrongMetaClass(newMc);
    } finally {
      info.unlock();
    }
    if ((oldMc == null && mc != newMc) || (oldMc != null && mc != newMc && mc != oldMc)) {
      fireConstantMetaClassUpdate(null, theClass, mc, newMc);
    }
  }
 public MetaClass getMetaClass(Object obj) {
   return ClassInfo.getClassInfo(obj.getClass()).getMetaClass(obj);
 }
 public final MetaClass getMetaClass(Class theClass) {
   return ClassInfo.getClassInfo(theClass).getMetaClass();
 }