package ognl.helperfunction; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSKeyValueCoding.ValueAccessor; import com.webobjects.foundation.NSKeyValueCoding._BooleanFieldBinding; import com.webobjects.foundation.NSKeyValueCoding._BooleanMethodBinding; import com.webobjects.foundation.NSKeyValueCoding._FieldBinding; import com.webobjects.foundation.NSKeyValueCoding._KeyBinding; import com.webobjects.foundation.NSKeyValueCoding._MethodBinding; import com.webobjects.foundation.NSKeyValueCoding._NumberFieldBinding; import com.webobjects.foundation.NSKeyValueCoding._NumberMethodBinding; import com.webobjects.foundation.NSKeyValueCoding._ReflectionKeyBindingCreation.Callback; import com.webobjects.foundation.NSLog; import com.webobjects.foundation.NSMutableDictionary; import com.webobjects.foundation.NSMutableSet; import com.webobjects.foundation._NSReflectionUtilities; import com.webobjects.foundation._NSThreadsafeMutableDictionary; import com.webobjects.foundation._NSThreadsafeMutableSet; import com.webobjects.foundation._NSUtilities; /** * WOHelperFunctionClassKeyValueCoding is basically just like NSKVC except that * it operates ONLY on Classes, and not on Objects. This is required if you have * a keypath with evaluates to null, and you need to figure out the type of the * Helper to pass the null value to. * * @author mschrag */ public class WOHelperFunctionClassKeyValueCoding { public static class _ReflectionKeyBindingCreation { public static _KeyBinding _NotAvailableIndicator = new _KeyBinding(null, null); private static final _NSThreadsafeMutableDictionary _bindingStorageMapTable = new _NSThreadsafeMutableDictionary(new NSMutableDictionary(256)); public static final int _ValueForKeyLookupOrder[] = { 0, 1, 3, 2, 4 }; public static final int _StoredValueForKeyLookupOrder[] = { 1, 3, 2, 4, 0 }; private static class _BindingStorage { _KeyBinding _keyGetBindings[]; _KeyBinding _keySetBindings[]; public _BindingStorage() { _keyGetBindings = new _KeyBinding[4]; _keySetBindings = new _KeyBinding[4]; } } public static _KeyBinding _fieldKeyBinding(Class objectClass, String key, String fieldName) { ValueAccessor valueAccessor = ValueAccessor._valueAccessorForClass(objectClass); boolean publicFieldOnly = valueAccessor == null; Field field = _NSReflectionUtilities._fieldForClass(objectClass, fieldName, publicFieldOnly); if (field != null) { Class valueClass = _NSUtilities.classObjectForClass(field.getType()); if (_NSUtilities._isClassANumber(valueClass)) { return new _NumberFieldBinding(objectClass, key, field, valueClass, valueAccessor); } if (_NSUtilities._isClassABoolean(valueClass)) { return new _BooleanFieldBinding(objectClass, key, field, valueAccessor); } return new _FieldBinding(objectClass, key, field, valueAccessor); } return null; } private static final Class _noArgumentTypes[] = new Class[0]; // MS: slurped in from _NSReflectionUtilities because the original won't return a typed keypath for an abstract method, which // means you don't get type information when you bind a helper function to an interface method public static Method _methodForClass(Class objectClass, String methodName, Class argumentTypes[], boolean publicMethodOnly) { Method method = null; if (publicMethodOnly) { try { method = objectClass.getMethod(methodName, argumentTypes == null ? _noArgumentTypes : argumentTypes); } catch (NoSuchMethodException exception) { NSLog._conditionallyLogPrivateException(exception); } catch (SecurityException exception) { NSLog._conditionallyLogPrivateException(exception); method = null; } } else { do { if (objectClass == _NSUtilities._ObjectClass || method != null) { break; } try { method = objectClass.getDeclaredMethod(methodName, argumentTypes == null ? _noArgumentTypes : argumentTypes); } catch (NoSuchMethodException exception) { NSLog._conditionallyLogPrivateException(exception); } catch (SecurityException exception) { NSLog._conditionallyLogPrivateException(exception); method = null; } if (method == null) { objectClass = objectClass.getSuperclass(); } } while (true); } if (method != null) { int modifiers = method.getModifiers(); if (Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers)/* || Modifier.isAbstract(modifiers)*/) { return null; } } return method; } public static _KeyBinding _methodKeyGetBinding(Class objectClass, String key, String methodName) { ValueAccessor valueAccessor = ValueAccessor._valueAccessorForClass(objectClass); boolean publicMethodOnly = valueAccessor == null; Method method = WOHelperFunctionClassKeyValueCoding._ReflectionKeyBindingCreation._methodForClass(objectClass, methodName, null, publicMethodOnly); if (method != null) { Class valueClass = _NSUtilities.classObjectForClass(method.getReturnType()); if (_NSUtilities._isClassANumber(valueClass)) { return new _NumberMethodBinding(objectClass, key, method, valueClass, valueAccessor); } if (_NSUtilities._isClassABoolean(valueClass)) { return new _BooleanMethodBinding(objectClass, key, method, valueAccessor); } return new _MethodBinding(objectClass, key, method, valueAccessor); } return null; } public static _KeyBinding _methodKeySetBinding(Class objectClass, String key, String methodName) { ValueAccessor valueAccessor = ValueAccessor._valueAccessorForClass(objectClass); boolean publicMethodOnly = valueAccessor == null; Method method = _NSReflectionUtilities._methodWithOneArgumentOfUnknownType(objectClass, methodName, key, publicMethodOnly, true, null, true); if (method != null) { Class valueClass = _NSUtilities.classObjectForClass(method.getParameterTypes()[0]); if (_NSUtilities._isClassANumber(valueClass)) { return new _NumberMethodBinding(objectClass, key, method, valueClass, valueAccessor); } if (_NSUtilities._isClassABoolean(valueClass)) { return new _BooleanMethodBinding(objectClass, key, method, valueAccessor); } return new _MethodBinding(objectClass, key, method, valueAccessor); } return null; } private static _KeyBinding _createKeyBindingForKey(Class objectClass, String key, int lookupOrder[], boolean trueForSetAndFalseForGet) { if (key == null || key.length() == 0) { return null; } boolean canAccessFieldsDirectlyTestPerformed = false; boolean canAccessFieldsDirectly = false; _KeyBinding lookupBinding = new _KeyBinding(objectClass, key); _BindingStorage bindingStorage = (_BindingStorage) _bindingStorageMapTable.objectForKey(lookupBinding); if (bindingStorage == null) { bindingStorage = new _BindingStorage(); _bindingStorageMapTable.setObjectForKey(bindingStorage, lookupBinding); } // MS: We just can't support callbacks without the original object // ... I think this is PROBABLY OK for our purposes. Callback keyBindingCreationCallbackObject = null; // (object instanceof Callback) ? (Callback) object : null; _KeyBinding keyBindings[] = trueForSetAndFalseForGet ? bindingStorage._keySetBindings : bindingStorage._keyGetBindings; for (int i = 0; i < lookupOrder.length; i++) { int lookup = lookupOrder[i]; _KeyBinding keyBinding = lookup < 0 || lookup > 3 ? null : keyBindings[lookup]; if (keyBinding == null) { switch (lookup) { case 0: StringBuilder methodNameBuffer = new StringBuilder(key.length() + 3); methodNameBuffer.append(trueForSetAndFalseForGet ? "set" : "get"); methodNameBuffer.append(Character.toUpperCase(key.charAt(0))); methodNameBuffer.append(key.substring(1)); String methodName = methodNameBuffer.toString(); if (trueForSetAndFalseForGet) { keyBinding = keyBindingCreationCallbackObject == null ? _methodKeySetBinding(objectClass, key, methodName) : keyBindingCreationCallbackObject._methodKeySetBinding(key, methodName); } else { keyBinding = keyBindingCreationCallbackObject == null ? _methodKeyGetBinding(objectClass, key, methodName) : keyBindingCreationCallbackObject._methodKeyGetBinding(key, methodName); if (keyBinding == null) { keyBinding = keyBindingCreationCallbackObject == null ? _methodKeyGetBinding(objectClass, key, key) : keyBindingCreationCallbackObject._methodKeyGetBinding(key, key); } if (keyBinding == null) { methodNameBuffer.setLength(0); methodNameBuffer.append("is"); methodNameBuffer.append(Character.toUpperCase(key.charAt(0))); methodNameBuffer.append(key.substring(1)); methodName = methodNameBuffer.toString(); keyBinding = keyBindingCreationCallbackObject == null ? _methodKeyGetBinding(objectClass, key, methodName) : keyBindingCreationCallbackObject._methodKeyGetBinding(key, methodName); } } break; case 1: StringBuilder underbarMethodNameBuffer = new StringBuilder(key.length() + 4); underbarMethodNameBuffer.append(trueForSetAndFalseForGet ? "_set" : "_get"); underbarMethodNameBuffer.append(Character.toUpperCase(key.charAt(0))); underbarMethodNameBuffer.append(key.substring(1)); String underbarMethodName = underbarMethodNameBuffer.toString(); if (trueForSetAndFalseForGet) { keyBinding = keyBindingCreationCallbackObject == null ? _methodKeySetBinding(objectClass, key, underbarMethodName) : keyBindingCreationCallbackObject._methodKeySetBinding(key, underbarMethodName); } else { keyBinding = keyBindingCreationCallbackObject == null ? _methodKeyGetBinding(objectClass, key, underbarMethodName) : keyBindingCreationCallbackObject._methodKeyGetBinding(key, underbarMethodName); if (keyBinding == null) { underbarMethodNameBuffer.setLength(0); underbarMethodNameBuffer.append('_'); underbarMethodNameBuffer.append(key); underbarMethodName = underbarMethodNameBuffer.toString(); keyBinding = keyBindingCreationCallbackObject == null ? _methodKeyGetBinding(objectClass, key, underbarMethodName) : keyBindingCreationCallbackObject._methodKeyGetBinding(key, underbarMethodName); } if (keyBinding == null) { underbarMethodNameBuffer.setLength(0); underbarMethodNameBuffer.append("_is"); underbarMethodNameBuffer.append(Character.toUpperCase(key.charAt(0))); underbarMethodNameBuffer.append(key.substring(1)); underbarMethodName = underbarMethodNameBuffer.toString(); keyBinding = keyBindingCreationCallbackObject == null ? _methodKeyGetBinding(objectClass, key, underbarMethodName) : keyBindingCreationCallbackObject._methodKeyGetBinding(key, underbarMethodName); } } break; case 2: if (!canAccessFieldsDirectlyTestPerformed) { canAccessFieldsDirectlyTestPerformed = true; canAccessFieldsDirectly = NSKeyValueCoding._ReflectionKeyBindingCreation._canAccessFieldsDirectlyForClass(objectClass); } if (canAccessFieldsDirectly) { keyBinding = keyBindingCreationCallbackObject == null ? _fieldKeyBinding(objectClass, key, key) : keyBindingCreationCallbackObject._fieldKeyBinding(key, key); if (keyBinding == null) { StringBuilder fieldNameBuffer = new StringBuilder(key.length() + 2); fieldNameBuffer.append("is"); fieldNameBuffer.append(Character.toUpperCase(key.charAt(0))); fieldNameBuffer.append(key.substring(1)); String fieldName = fieldNameBuffer.toString(); keyBinding = keyBindingCreationCallbackObject == null ? _fieldKeyBinding(objectClass, key, fieldName) : keyBindingCreationCallbackObject._fieldKeyBinding(key, fieldName); } } break; case 3: if (!canAccessFieldsDirectlyTestPerformed) { canAccessFieldsDirectlyTestPerformed = true; canAccessFieldsDirectly = NSKeyValueCoding._ReflectionKeyBindingCreation._canAccessFieldsDirectlyForClass(objectClass); } if (canAccessFieldsDirectly) { StringBuilder underbarFieldNameBuffer = new StringBuilder(key.length() + 3); underbarFieldNameBuffer.append('_'); underbarFieldNameBuffer.append(key); String underbarFieldName = underbarFieldNameBuffer.toString(); keyBinding = keyBindingCreationCallbackObject == null ? _fieldKeyBinding(objectClass, key, underbarFieldName) : keyBindingCreationCallbackObject._fieldKeyBinding(key, underbarFieldName); if (keyBinding == null) { underbarFieldNameBuffer.setLength(0); underbarFieldNameBuffer.append("_is"); underbarFieldNameBuffer.append(Character.toUpperCase(key.charAt(0))); underbarFieldNameBuffer.append(key.substring(1)); underbarFieldName = underbarFieldNameBuffer.toString(); keyBinding = keyBindingCreationCallbackObject == null ? _fieldKeyBinding(objectClass, key, underbarFieldName) : keyBindingCreationCallbackObject._fieldKeyBinding(key, underbarFieldName); } } break; case 4: keyBinding = keyBindingCreationCallbackObject == null ? null : keyBindingCreationCallbackObject._otherStorageBinding(key); break; } if (keyBinding == null) { keyBinding = _NotAvailableIndicator; } if (lookup == 2 || lookup == 3) { bindingStorage._keySetBindings[lookup] = bindingStorage._keyGetBindings[lookup] = keyBinding; } else if (lookup == 0 || lookup == 1) { keyBindings[lookup] = keyBinding; } } if (keyBinding != null && keyBinding != _NotAvailableIndicator) { return keyBinding; } } return null; } public static _KeyBinding _createKeyGetBindingForKey(Class objectClass, String key, int lookupOrder[]) { return _createKeyBindingForKey(objectClass, key, lookupOrder, false); } public static _KeyBinding _createKeySetBindingForKey(Class objectClass, String key, int lookupOrder[]) { return _createKeyBindingForKey(objectClass, key, lookupOrder, true); } } public static class DefaultImplementation { private static final _NSThreadsafeMutableSet _keyGetBindings = new _NSThreadsafeMutableSet(new NSMutableSet(256)); private static final _NSThreadsafeMutableSet _keySetBindings = new _NSThreadsafeMutableSet(new NSMutableSet(256)); public static void _flushCaches() { _keyGetBindings.removeAllObjects(); _keySetBindings.removeAllObjects(); } public static _KeyBinding _keyGetBindingForKey(Class objectClass, String key) { _KeyBinding keyBinding = (_KeyBinding) _keyGetBindings.member(new _KeyBinding(objectClass, key)); if (keyBinding == null) { //keyBinding = (object instanceof _KeyBindingCreation) ? ((_KeyBindingCreation) object)._createKeyGetBindingForKey(key) : _createKeyGetBindingForKey(objectClass, key); keyBinding = _createKeyGetBindingForKey(objectClass, key); if (keyBinding == null) { keyBinding = new _KeyBinding(objectClass, key); } _keyGetBindings.addObject(keyBinding); } return keyBinding; } public static _KeyBinding _keySetBindingForKey(Class objectClass, String key) { _KeyBinding keyBinding = (_KeyBinding) _keySetBindings.member(new _KeyBinding(objectClass, key)); if (keyBinding == null) { //keyBinding = (object instanceof _KeyBindingCreation) ? ((_KeyBindingCreation) object)._createKeySetBindingForKey(key) : _createKeySetBindingForKey(objectClass, key); keyBinding = _createKeySetBindingForKey(objectClass, key); if (keyBinding == null) { keyBinding = new _KeyBinding(objectClass, key); } _keySetBindings.addObject(keyBinding); } return keyBinding; } public static _KeyBinding _createKeyGetBindingForKey(Class objectClass, String key) { _KeyBinding keyBinding = _ReflectionKeyBindingCreation._createKeyGetBindingForKey(objectClass, key, _ReflectionKeyBindingCreation._ValueForKeyLookupOrder); return keyBinding; } public static _KeyBinding _createKeySetBindingForKey(Class objectClass, String key) { return _ReflectionKeyBindingCreation._createKeySetBindingForKey(objectClass, key, _ReflectionKeyBindingCreation._ValueForKeyLookupOrder); } public static _KeyBinding keyGetBindingForKeyPath(Class objectClass, String keyPath) { if (keyPath == null) { return null; } int index = keyPath.indexOf('.'); if (index < 0) { return WOHelperFunctionClassKeyValueCoding.DefaultImplementation._keyGetBindingForKey(objectClass, keyPath); } String key = keyPath.substring(0, index); _KeyBinding keyBinding = WOHelperFunctionClassKeyValueCoding.DefaultImplementation._keyGetBindingForKey(objectClass, key); return keyBinding != null ? WOHelperFunctionClassKeyValueCoding.DefaultImplementation.keyGetBindingForKeyPath(keyBinding.valueType(), keyPath.substring(index + 1)) : null; } DefaultImplementation() { throw new IllegalStateException("Cannot instantiate an instance of class " + getClass().getName()); } } }