package com.framsticks.params; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; import java.util.HashMap; import java.util.Map; import com.framsticks.params.annotations.ParamAnnotation; public class ParamCandidate { public class OneTime { protected final String name; T value; /** * @param name */ public OneTime(String name) { this.name = name; } final void set(T value) { if (this.value == null) { this.value = value; return; } if (!this.value.equals(value)) { throw new ConstructionException().msg("already set") .arg("name", name) .arg("in", ParamCandidate.this) .arg("already", this.value) .arg("now", value); } } public final T get() { return value; } public final boolean has() { return value != null; } @Override public String toString() { return value == null ? "" : value.toString(); } } protected final String id; protected final OneTime name = new OneTime<>("name"); protected final OneTime type = new OneTime<>("type"); protected final OneTime field = new OneTime<>("field"); protected final OneTime setter = new OneTime<>("setter"); protected final OneTime getter = new OneTime<>("getter"); /** * @param id */ public ParamCandidate(String id) { this.id = id; } /** * @return the id */ public String getId() { return id; } /** * @return the name */ public String getName() { return name.get(); } /** * @return the type */ public Type getType() { return type.get(); } public Class getRawType() { return getRawClass(type.get()); } void setType(Type type) { this.type.set(type); } /** * @return the field */ public Field getField() { return field.get(); } /** * @return the setter */ public Method getSetter() { return setter.get(); } /** * @return the getter */ public Method getGetter() { return getter.get(); } void validate() throws ConstructionException { try { if (isPublic(field)) { if (getter.has()) { throw new ConstructionException().msg("getter and public field coexist"); } return; } if (isPublic(field)) { if (setter.has()) { throw new ConstructionException().msg("setter and field coexist"); } } if (!getter.has() && !field.has()) { throw new ConstructionException().msg("missing getter or field"); } } catch (ConstructionException e) { throw e.arg("in", this); } } boolean isFinal() { if (Collection.class.isAssignableFrom(getRawType())) { return false; } if (setter.has()) { return false; } if (field.has()) { return Modifier.isFinal(field.get().getModifiers()); } return true; } boolean isReadOnly() { if (Collection.class.isAssignableFrom(getRawType())) { return false; } if (isPublic(setter)) { return false; } if (isPublic(field)) { return Modifier.isFinal(field.get().getModifiers()); } return true; } void add(Member member, String name) { this.name.set(name); if (member instanceof Field) { this.field.set((Field) member); setType(field.get().getGenericType()); return; } if (member instanceof Method) { Method m = (Method) member; Type[] ps = m.getGenericParameterTypes(); if (ps.length == 0) { getter.set(m); setType(m.getGenericReturnType()); return; } if (ps.length == 1) { setter.set(m); setType(ps[0]); return; } throw new ConstructionException().msg("invalid number of arguments").arg("method", m).arg("in", this); } throw new ConstructionException().msg("invalid kind of member").arg("member", member).arg("in", this); } public boolean isPrimitive() { return getRawType().isPrimitive(); } public int getFlags() { int f = 0; if (isReadOnly()) { f |= Flags.READONLY; } return f; } @Override public String toString() { return id + "(" + type.toString() + ")"; } public static boolean isPublic(Member member) { return Modifier.isPublic(member.getModifiers()); } public static boolean isPublic(OneTime v) { return v.has() ? isPublic(v.get()) : false; } public static void filterParamsCandidates(Map params, M[] members) { for (M m : members) { ParamAnnotation pa = m.getAnnotation(ParamAnnotation.class); if (pa == null) { continue; } // if (!isPublic(m)) { // throw new ConstructionException().msg("field is not public").arg("field", m); // } String id = FramsClassBuilder.getId(pa, m); ParamCandidate pc = null; if (params.containsKey(id)) { pc = params.get(id); } else { pc = new ParamCandidate(id); params.put(id, pc); } pc.add(m, FramsClassBuilder.getName(pa, m)); } } public static Map getAllCandidates(Class javaClass) throws ConstructionException { Map candidates = new HashMap<>(); while (javaClass != null) { filterParamsCandidates(candidates, javaClass.getDeclaredFields()); filterParamsCandidates(candidates, javaClass.getDeclaredMethods()); javaClass = javaClass.getSuperclass(); } for (ParamCandidate pc : candidates.values()) { pc.validate(); } return candidates; } public static Class getRawClass(final Type type) { if (Class.class.isInstance(type)) { return Class.class.cast(type); } if (ParameterizedType.class.isInstance(type)) { final ParameterizedType parameterizedType = ParameterizedType.class.cast(type); return getRawClass(parameterizedType.getRawType()); } else if (GenericArrayType.class.isInstance(type)) { GenericArrayType genericArrayType = GenericArrayType.class.cast(type); Class c = getRawClass(genericArrayType.getGenericComponentType()); return Array.newInstance(c, 0).getClass(); } else { return null; } } };