protected void forceNumberArgumentsToParameterTypes(
      Object[] args, Class[] paramTypes, int[] typeFlagsByParamIndex) {
    final int paramTypesLen = paramTypes.length;
    final int argsLen = args.length;
    for (int argIdx = 0; argIdx < argsLen; argIdx++) {
      final int paramTypeIdx = argIdx < paramTypesLen ? argIdx : paramTypesLen - 1;
      final int typeFlags = typeFlagsByParamIndex[paramTypeIdx];

      // Forcing the number type can only be interesting if there are numerical parameter types on
      // that index,
      // and the unwrapping was not to an exact numerical type.
      if ((typeFlags & TypeFlags.WIDENED_NUMERICAL_UNWRAPPING_HINT) != 0) {
        final Object arg = args[argIdx];
        // If arg isn't a number, we can't do any conversions anyway, regardless of the param type.
        if (arg instanceof Number) {
          final Class targetType = paramTypes[paramTypeIdx];
          final Number convertedArg =
              BeansWrapper.forceUnwrappedNumberToType((Number) arg, targetType, bugfixed);
          if (convertedArg != null) {
            args[argIdx] = convertedArg;
          }
        }
      }
    }
  }
 @Override
 TemplateModel invokeMethod(BeansWrapper bw, Object obj, Object[] args)
     throws TemplateModelException, InvocationTargetException, IllegalAccessException {
   return bw.invokeMethod(obj, (Method) member, args);
 }
  private TemplateModel getInternal(String key)
      throws TemplateModelException, ClassNotFoundException {
    {
      TemplateModel model = (TemplateModel) cache.get(key);
      if (model != null) return model;
    }

    final ClassIntrospector classIntrospector;
    int classIntrospectorClearingCounter;
    final Object sharedLock = wrapper.getSharedIntrospectionLock();
    synchronized (sharedLock) {
      TemplateModel model = (TemplateModel) cache.get(key);
      if (model != null) return model;

      while (model == null && classIntrospectionsInProgress.contains(key)) {
        // Another thread is already introspecting this class;
        // waiting for its result.
        try {
          sharedLock.wait();
          model = (TemplateModel) cache.get(key);
        } catch (InterruptedException e) {
          throw new RuntimeException("Class inrospection data lookup aborded: " + e);
        }
      }
      if (model != null) return model;

      // This will be the thread that introspects this class.
      classIntrospectionsInProgress.add(key);

      // While the classIntrospector should not be changed from another thread, badly written apps
      // can do that,
      // and it's cheap to get the classIntrospector from inside the lock here:
      classIntrospector = wrapper.getClassIntrospector();
      classIntrospectorClearingCounter = classIntrospector.getClearingCounter();
    }
    try {
      final Class clazz = ClassUtil.forName(key);

      // This is called so that we trigger the
      // class-reloading detector. If clazz is a reloaded class,
      // the wrapper will in turn call our clearCache method.
      // TODO: Why do we check it now and only now?
      classIntrospector.get(clazz);

      TemplateModel model = createModel(clazz);
      // Warning: model will be null if the class is not good for the subclass.
      // For example, EnumModels#createModel returns null if clazz is not an enum.

      if (model != null) {
        synchronized (sharedLock) {
          // Save it into the cache, but only if nothing relevant has changed while we were outside
          // the lock:
          if (classIntrospector == wrapper.getClassIntrospector()
              && classIntrospectorClearingCounter == classIntrospector.getClearingCounter()) {
            cache.put(key, model);
          }
        }
      }
      return model;
    } finally {
      synchronized (sharedLock) {
        classIntrospectionsInProgress.remove(key);
        sharedLock.notifyAll();
      }
    }
  }
 void removeFromCache(Class clazz) {
   synchronized (wrapper.getSharedIntrospectionLock()) {
     cache.remove(clazz.getName());
   }
 }
 void clearCache() {
   synchronized (wrapper.getSharedIntrospectionLock()) {
     cache.clear();
   }
 }