/** Creates a new instance of the specified type. */ protected Object newInstance(Class<?> type) throws Exception { // find the most specific constructor that can take the last value if (_lvalue != null) { boolean inner = ReflectionUtil.isInner(type); _lvalue.getClass(); Constructor cctor = null; Class<?> cptype = null; for (Constructor ctor : type.getConstructors()) { Class<?>[] ptypes = ctor.getParameterTypes(); if (inner ? (ptypes.length != 2 || !ptypes[0].isInstance(_outer)) : (ptypes.length != 1)) { continue; } Class<?> ptype = ptypes[ptypes.length - 1]; if (ptype.isInstance(_lvalue) && (cctor == null || cptype.isAssignableFrom(ptype))) { cctor = ctor; cptype = ptype; } } if (cctor != null) { return inner ? cctor.newInstance(_outer, _lvalue) : cctor.newInstance(_lvalue); } } // fall back on default constructor return ReflectionUtil.newInstance(type, _outer); }
/** Determines whether the supplied value is legal for this property. */ protected boolean isLegalValue(Class<?> type, Object value) { if (type.isPrimitive()) { if (value == null) { return false; } type = ClassUtil.objectEquivalentOf(type); } return (value == null) ? getAnnotation().nullable() : type.isInstance(value); }
/** Adds the subtypes listed in the provided annotation to the supplied list. */ protected static void addSubtypes(EditorTypes annotation, Collection<Class<?>> types) { for (Class<?> sclazz : annotation.value()) { // handle the case where the annotation includes the annotated class if (sclazz.getAnnotation(EditorTypes.class) == annotation) { types.add(sclazz); } else { addSubtypes(sclazz, types); } } }
/** Returns the component type of this (array or collection) type. */ public Class<?> getComponentType() { Class<?> type = getType(); if (type.isArray()) { return type.getComponentType(); } else if (Collection.class.isAssignableFrom(type)) { return getArgumentType(Collection.class); } return null; }
/** * Returns the arguments of the provided generic class or interface. * * @param type the class to search. * @param args the class arguments. * @param clazz the generic class or interface of interest. * @return the arguments of the generic class or interface, or <code>null</code> if not found. */ protected static Type[] getTypeArguments(Class<?> type, Type[] args, Class<?> clazz) { if (type == clazz) { return args; } TypeVariable[] params = type.getTypeParameters(); for (Type iface : type.getGenericInterfaces()) { Type[] result = getTypeArguments(iface, params, args, clazz); if (result != null) { return result; } } return getTypeArguments(type.getGenericSuperclass(), params, args, clazz); }
/** Returns the message bundle to use when translating the supplied class's properties. */ public static String getMessageBundle(Class<?> clazz) { while (clazz.isArray()) { clazz = clazz.getComponentType(); } if (clazz.isPrimitive()) { return EditorMessageBundle.DEFAULT; } String bundle = _bundles.get(clazz); if (bundle == null) { _bundles.put(clazz, bundle = findMessageBundle(clazz)); } return bundle; }
/** Returns the generic component type of this (array or collection) type. */ public Type getGenericComponentType() { Class<?> type = getType(); Type gtype = getGenericType(); if (gtype instanceof GenericArrayType) { return ((GenericArrayType) gtype).getGenericComponentType(); } else if (type.isArray()) { return type.getComponentType(); } else if (Collection.class.isAssignableFrom(type)) { return getGenericArgumentType(Collection.class); } return null; }
/** * Adds the subtypes of the specified class to the provided list. If the class has an {@link * EditorTypes} annotation, the classes therein will be added; otherwise, the class itself will be * added. */ protected static void addSubtypes(Class<?> clazz, Collection<Class<?>> types) { EditorTypes annotation = clazz.getAnnotation(EditorTypes.class); if (annotation == null) { types.add(clazz); } else { addSubtypes(annotation, types); } }
/** * Returns an array containing the available subtypes of the specified type, first looking to * subtypes listed in the annotation, then attempting to find a method using reflection. */ protected Class<?>[] getSubtypes(Class<?> type) { ArrayList<Class<?>> types = new ArrayList<Class<?>>(); // start with the null class, if allowed boolean nullable = getAnnotation().nullable(); if (nullable) { types.add(null); } // look for a subtype annotation and add its types EditorTypes ownAnnotation = getAnnotation(EditorTypes.class); EditorTypes annotation = (ownAnnotation == null) ? type.getAnnotation(EditorTypes.class) : ownAnnotation; if (annotation != null) { addSubtypes(annotation, types); } // get the config key and add the config types String key = (annotation == null) ? type.getName() : annotation.key(); if (StringUtil.isBlank(key)) { if (annotation == ownAnnotation) { Member member = getMember(); key = member.getDeclaringClass().getName() + "." + member.getName(); } else { key = type.getName(); } } Class<?>[] ctypes = _configTypes.get(key); if (ctypes != null) { Collections.addAll(types, ctypes); } // if we don't have at least one non-null class, add the type itself if (types.size() == (nullable ? 1 : 0)) { types.add(type); } // convert to array, return return types.toArray(new Class<?>[types.size()]); }
/** Finds the editor message bundle for the supplied class. */ protected static String findMessageBundle(Class<?> clazz) { EditorMessageBundle annotation = clazz.getAnnotation(EditorMessageBundle.class); if (annotation != null) { return annotation.value(); } Class<?> eclazz = clazz.getEnclosingClass(); if (eclazz != null) { return getMessageBundle(eclazz); } String name = clazz.getName(); int idx; while ((idx = name.lastIndexOf('.')) != -1) { name = name.substring(0, idx); Package pkg = Package.getPackage(name); if (pkg != null) { annotation = pkg.getAnnotation(EditorMessageBundle.class); if (annotation != null) { return annotation.value(); } } } return EditorMessageBundle.DEFAULT; }
/** Creates and returns the list of properties for the supplied class. */ protected static Property[] createProperties(Class<?> clazz) { // get the list of properties ArrayList<Property> properties = new ArrayList<Property>(); createProperties(clazz, properties); // sort the properties by weight Collections.sort(properties, WEIGHT_COMP); // if the class has a property order attribute, move the listed properties // to the beginning in the desired order PropertyOrder order = clazz.getAnnotation(PropertyOrder.class); if (order != null) { int index = 0; for (String name : order.value()) { int oindex = index; for (int ii = index, nn = properties.size(); ii < nn; ii++) { Property property = properties.get(ii); if (property.getName().equals(name)) { properties.remove(ii); properties.add(index++, property); break; } } if (index == oindex) { log.warning( "Missing property in order annotation [class=" + clazz.getName() + ", property=" + name + "]."); } } } // return an array with the results return properties.toArray(new Property[properties.size()]); }
static { // load the types from the configuration Config config = new Config("/rsrc/config/editor/type"); for (Iterator<String> it = config.keys(); it.hasNext(); ) { String key = it.next(); ArrayList<Class<?>> classes = new ArrayList<Class<?>>(); for (String cname : config.getValue(key, ArrayUtil.EMPTY_STRING)) { try { addSubtypes(Class.forName(cname), classes); } catch (ClassNotFoundException e) { log.warning("Missing type config class.", "class", cname, e); } } _configTypes.put(key, classes.toArray(new Class<?>[classes.size()])); } }
/** * Returns the editor types annotation on the property, if it exists, or on the specified type, if * that exists, or <code>null</code> if neither one exists. */ protected EditorTypes getEditorTypes(Class<?> type) { EditorTypes annotation = getAnnotation(EditorTypes.class); return (annotation == null) ? type.getAnnotation(EditorTypes.class) : annotation; }
/** Retrieves all {@link Editable} properties of the specified class. */ protected static void createProperties(Class<?> clazz, ArrayList<Property> properties) { // prepend the superclass properties Class<?> sclazz = clazz.getSuperclass(); if (sclazz != null) { Collections.addAll(properties, getProperties(sclazz)); } // find the getters and setters HashMap<String, Method> unpaired = new HashMap<String, Method>(); for (Method method : clazz.getDeclaredMethods()) { if (!method.isAnnotationPresent(Editable.class)) { continue; } String name = method.getName(); boolean getter; if (name.startsWith("set")) { name = name.substring(3); getter = false; } else { if (name.startsWith("get")) { name = name.substring(3); } else if (name.startsWith("is")) { name = name.substring(2); } else { log.warning( "Invalid method name for editable property [class=" + clazz + ", name=" + name + "]."); continue; } getter = true; } Method omethod = unpaired.remove(name); if (omethod != null) { Method gmethod = getter ? method : omethod; Method smethod = getter ? omethod : method; Class<?> rtype = gmethod.getReturnType(); Class<?>[] ptypes = smethod.getParameterTypes(); if (ptypes.length != 1 || ptypes[0] != rtype) { log.warning( "Mismatched types on getter/setter [class=" + clazz + ", getter=" + gmethod + ", setter=" + smethod + "]."); continue; } properties.add(new MethodProperty(gmethod, smethod)); } else { unpaired.put(name, method); } } if (!unpaired.isEmpty()) { log.warning( "Found unmatched getters/setters [class=" + clazz + ", methods=" + unpaired.values() + "]."); } // add all editable fields for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Editable.class)) { properties.add(new FieldProperty(field)); } } }