[86] | 1 | package com.framsticks.params; |
---|
| 2 | |
---|
| 3 | import java.io.InputStream; |
---|
| 4 | import java.lang.reflect.Field; |
---|
| 5 | import java.lang.reflect.Member; |
---|
| 6 | import java.lang.reflect.Method; |
---|
| 7 | import java.lang.reflect.ParameterizedType; |
---|
| 8 | import java.lang.reflect.Type; |
---|
[87] | 9 | import java.util.ArrayList; |
---|
| 10 | import java.util.Collections; |
---|
| 11 | import java.util.HashMap; |
---|
| 12 | import java.util.LinkedList; |
---|
[86] | 13 | import java.util.List; |
---|
| 14 | import java.util.Map; |
---|
| 15 | |
---|
| 16 | import org.apache.log4j.Logger; |
---|
| 17 | |
---|
[87] | 18 | import com.framsticks.params.annotations.AutoAppendAnnotation; |
---|
[86] | 19 | import com.framsticks.params.annotations.FramsClassAnnotation; |
---|
| 20 | import com.framsticks.params.annotations.ParamAnnotation; |
---|
| 21 | import com.framsticks.parsers.FileSource; |
---|
| 22 | import com.framsticks.parsers.Loaders; |
---|
[88] | 23 | import com.framsticks.util.Builder; |
---|
| 24 | import com.framsticks.util.FramsticksException; |
---|
[86] | 25 | import com.framsticks.util.lang.Strings; |
---|
| 26 | |
---|
[87] | 27 | @FramsClassAnnotation(id = "class", name = "class") |
---|
[88] | 28 | public class FramsClassBuilder implements Builder<FramsClass> { |
---|
[86] | 29 | private static final Logger log = |
---|
| 30 | Logger.getLogger(FramsClassBuilder.class); |
---|
| 31 | |
---|
| 32 | public static String getName(FramsClassAnnotation fca, Class<?> javaClass) { |
---|
| 33 | return fca.name().equals("") ? javaClass.getSimpleName() : fca.name(); |
---|
| 34 | } |
---|
| 35 | |
---|
| 36 | public static String getId(FramsClassAnnotation fca, Class<?> javaClass) { |
---|
| 37 | return fca.id().equals("") ? javaClass.getSimpleName() : fca.id(); |
---|
| 38 | } |
---|
| 39 | |
---|
| 40 | public static String getParamTypeForNativeType(Type type) { |
---|
| 41 | if (type instanceof ParameterizedType) { |
---|
| 42 | ParameterizedType p = (ParameterizedType) type; |
---|
| 43 | Type rawType = p.getRawType(); |
---|
| 44 | Type containedType = null; |
---|
| 45 | //TODO make implementation here |
---|
| 46 | boolean map = false; |
---|
| 47 | if (rawType.equals(Map.class)) { |
---|
| 48 | containedType = p.getActualTypeArguments()[1]; |
---|
| 49 | map = true; |
---|
| 50 | } else if (rawType.equals(List.class)) { |
---|
| 51 | containedType = p.getActualTypeArguments()[0]; |
---|
| 52 | } |
---|
| 53 | if (!(containedType instanceof Class)) { |
---|
| 54 | return null; |
---|
| 55 | } |
---|
| 56 | Class<?> containedClass = (Class<?>) containedType; |
---|
| 57 | StringBuilder b = new StringBuilder(); |
---|
| 58 | b.append("l "); |
---|
| 59 | FramsClassAnnotation fca = containedClass.getAnnotation(FramsClassAnnotation.class); |
---|
| 60 | if (fca == null) { |
---|
| 61 | log.error("the class is not annotated: " + containedClass); |
---|
| 62 | return null; |
---|
| 63 | } |
---|
| 64 | b.append(getName(fca, containedClass)); |
---|
| 65 | if (map) { |
---|
| 66 | b.append(" name"); |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | return b.toString(); |
---|
| 70 | } |
---|
| 71 | |
---|
| 72 | if (type instanceof Class) { |
---|
[87] | 73 | |
---|
| 74 | Class<?> cl = (Class<?>) type; |
---|
| 75 | |
---|
| 76 | // TODO: future support for enum |
---|
| 77 | // if (cl.isEnum()) { |
---|
| 78 | // Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) cl; |
---|
| 79 | // Enum<?>[] enums = enumType.getEnumConstants(); |
---|
| 80 | // StringBuilder b = new StringBuilder(); |
---|
| 81 | |
---|
| 82 | // b.append("d 0 ").append(enums.length - 1).append(" 0 "); |
---|
| 83 | // for (Enum<?> e : enums) { |
---|
| 84 | // b.append("~").append(e.name()); |
---|
| 85 | // } |
---|
| 86 | // return b.toString(); |
---|
| 87 | // } |
---|
| 88 | if (cl.equals(Integer.class) || cl.equals(int.class)) { |
---|
| 89 | return "d"; |
---|
| 90 | } |
---|
| 91 | if (cl.equals(String.class)) { |
---|
| 92 | return "s"; |
---|
| 93 | } |
---|
| 94 | if (cl.equals(Double.class) || cl.equals(double.class)) { |
---|
| 95 | return "f"; |
---|
| 96 | } |
---|
| 97 | if (cl.equals(Boolean.class) || cl.equals(boolean.class)) { |
---|
| 98 | return "d 0 1"; |
---|
| 99 | } |
---|
| 100 | if (cl.equals(Object.class)) { |
---|
| 101 | return "x"; |
---|
| 102 | } |
---|
| 103 | |
---|
[86] | 104 | return "o " + ((Class<?>) type).getCanonicalName(); |
---|
| 105 | } |
---|
[87] | 106 | |
---|
| 107 | throw new ConstructionException().msg("failed to find framsticks for native type").arg("type", type); |
---|
[86] | 108 | } |
---|
| 109 | |
---|
| 110 | public static final String GENERATE_HELP_PREFIX = "automatically generated from: "; |
---|
| 111 | |
---|
| 112 | public static FramsClass readFromStream(InputStream stream) { |
---|
| 113 | return Loaders.loadFramsClass(new FileSource(stream)); |
---|
| 114 | } |
---|
| 115 | |
---|
| 116 | // public static Class<? extends Param> getParamType(@Nonnull Class<?> c) { |
---|
| 117 | // if (c.equals(Integer.class)) { |
---|
| 118 | // return DecimalParam.class; |
---|
| 119 | // } |
---|
| 120 | // if (c.equals(Double.class)) { |
---|
| 121 | // return FloatParam.class; |
---|
| 122 | // } |
---|
| 123 | // if (c.equals(String.class)) { |
---|
| 124 | // return StringParam.class; |
---|
| 125 | // } |
---|
| 126 | // if (c.equals(Object.class)) { |
---|
| 127 | // return UniversalParam.class; |
---|
| 128 | // } |
---|
| 129 | // return null; |
---|
| 130 | // } |
---|
| 131 | |
---|
| 132 | public static String extractIdOf(Member member) { |
---|
| 133 | if (member instanceof Field) { |
---|
| 134 | return member.getName(); |
---|
| 135 | } |
---|
| 136 | if (member instanceof Method) { |
---|
| 137 | Method m = (Method) member; |
---|
| 138 | String n = m.getName(); |
---|
| 139 | int argsNum = m.getParameterTypes().length; |
---|
| 140 | if (argsNum == 0) { |
---|
| 141 | return n.startsWith("get") ? Strings.uncapitalize(n.substring(3)) : n; |
---|
| 142 | } |
---|
| 143 | if (argsNum == 1) { |
---|
| 144 | return n.startsWith("set") ? Strings.uncapitalize(n.substring(3)) : n; |
---|
| 145 | } |
---|
| 146 | log.error("invalid number of arguments"); |
---|
| 147 | return null; |
---|
| 148 | } |
---|
| 149 | log.error("invalid kind of member"); |
---|
| 150 | return null; |
---|
| 151 | } |
---|
| 152 | public static String getName(ParamAnnotation annotation, Member member) { |
---|
| 153 | return annotation.name().equals("") ? Strings.capitalize(extractIdOf(member)) : annotation.name(); |
---|
| 154 | } |
---|
| 155 | |
---|
| 156 | public static String getId(ParamAnnotation annotation, Member member) { |
---|
| 157 | return annotation.id().equals("") ? extractIdOf(member) : annotation.id(); |
---|
| 158 | } |
---|
| 159 | |
---|
| 160 | public static ParamBuilder fill(ParamBuilder builder, Member member, ParamAnnotation annotation) { |
---|
| 161 | return builder |
---|
| 162 | .id(getId(annotation, member)) |
---|
| 163 | .name(getName(annotation, member)); |
---|
| 164 | |
---|
| 165 | } |
---|
| 166 | |
---|
[88] | 167 | public static final Map<Class<?>, FramsClass> synchronizedCacheForBasedOnForJavaClass = Collections.synchronizedMap(new HashMap<Class<?>, FramsClass>()); |
---|
[86] | 168 | |
---|
[87] | 169 | public FramsClass forClass(Class<?> javaClass) throws ConstructionException { |
---|
| 170 | FramsClass result = synchronizedCacheForBasedOnForJavaClass.get(javaClass); |
---|
| 171 | if (result != null) { |
---|
| 172 | return result; |
---|
| 173 | } |
---|
| 174 | |
---|
[88] | 175 | log.debug("building for class " + javaClass); |
---|
[87] | 176 | |
---|
[86] | 177 | FramsClassAnnotation fca = javaClass.getAnnotation(FramsClassAnnotation.class); |
---|
| 178 | if (fca == null) { |
---|
[87] | 179 | throw new ConstructionException().msg("java class is not annotated with FramsClassAnnotation").arg("java", javaClass); |
---|
[86] | 180 | } |
---|
| 181 | |
---|
[87] | 182 | id(getId(fca, javaClass)); |
---|
| 183 | name(getName(fca, javaClass)); |
---|
[86] | 184 | |
---|
| 185 | Map<String, ParamCandidate> candidates = ParamCandidate.getAllCandidates(javaClass); |
---|
| 186 | |
---|
| 187 | for (ParamCandidate pc : candidates.values()) { |
---|
[88] | 188 | String type = getParamTypeForNativeType(pc.getType()); |
---|
| 189 | if (type == null) { |
---|
| 190 | throw new FramsticksException().msg("failed to find type for param candidate").arg("candidate", pc); |
---|
| 191 | } |
---|
| 192 | param(Param.build().id(pc.getId()).name(pc.getName()).type(type).flags(pc.getFlags())); |
---|
[86] | 193 | } |
---|
| 194 | |
---|
[87] | 195 | result = finish(); |
---|
| 196 | |
---|
| 197 | synchronizedCacheForBasedOnForJavaClass.put(javaClass, result); |
---|
| 198 | |
---|
| 199 | return result; |
---|
[86] | 200 | } |
---|
[87] | 201 | |
---|
| 202 | |
---|
| 203 | protected String id; |
---|
| 204 | |
---|
| 205 | protected String name; |
---|
| 206 | |
---|
| 207 | protected String description; |
---|
| 208 | |
---|
| 209 | protected final List<Param> params = new LinkedList<>(); |
---|
| 210 | |
---|
| 211 | protected List<Group> groups = new ArrayList<Group>(); |
---|
| 212 | |
---|
| 213 | @ParamAnnotation |
---|
| 214 | public FramsClassBuilder id(String id) { |
---|
| 215 | this.id = id; |
---|
| 216 | return this; |
---|
| 217 | } |
---|
| 218 | |
---|
| 219 | @ParamAnnotation |
---|
| 220 | public FramsClassBuilder name(String name) { |
---|
| 221 | this.name = name; |
---|
| 222 | return this; |
---|
| 223 | } |
---|
| 224 | |
---|
| 225 | public FramsClassBuilder idAndName(String v) { |
---|
| 226 | this.id = v; |
---|
| 227 | this.name = v; |
---|
| 228 | return this; |
---|
| 229 | } |
---|
| 230 | |
---|
| 231 | @ParamAnnotation(id = "desc") |
---|
| 232 | public FramsClassBuilder description(String description) { |
---|
| 233 | this.description = description; |
---|
| 234 | return this; |
---|
| 235 | } |
---|
| 236 | |
---|
| 237 | public FramsClassBuilder() { |
---|
| 238 | } |
---|
| 239 | |
---|
| 240 | public FramsClass finish() { |
---|
[88] | 241 | return new FramsClass(this); |
---|
[87] | 242 | } |
---|
| 243 | |
---|
| 244 | public FramsClassBuilder append(Param param) { |
---|
| 245 | params.add(param); |
---|
| 246 | return this; |
---|
| 247 | } |
---|
| 248 | |
---|
| 249 | @AutoAppendAnnotation |
---|
| 250 | public FramsClassBuilder param(ParamBuilder builder) { |
---|
| 251 | return append(builder.finish()); |
---|
| 252 | } |
---|
| 253 | |
---|
| 254 | @AutoAppendAnnotation |
---|
[88] | 255 | public FramsClassBuilder group(GroupBuilder builder) { |
---|
| 256 | return group(builder.finish()); |
---|
| 257 | } |
---|
| 258 | |
---|
| 259 | @AutoAppendAnnotation |
---|
[87] | 260 | public FramsClassBuilder group(Group group) { |
---|
| 261 | groups.add(group); |
---|
| 262 | return this; |
---|
| 263 | } |
---|
| 264 | |
---|
| 265 | /** |
---|
| 266 | * @return the id |
---|
| 267 | */ |
---|
| 268 | @ParamAnnotation |
---|
| 269 | public String getId() { |
---|
| 270 | return id; |
---|
| 271 | } |
---|
| 272 | |
---|
| 273 | /** |
---|
| 274 | * @return the name |
---|
| 275 | */ |
---|
| 276 | @ParamAnnotation |
---|
| 277 | public String getName() { |
---|
| 278 | return name; |
---|
| 279 | } |
---|
| 280 | |
---|
| 281 | /** |
---|
| 282 | * @return the description |
---|
| 283 | */ |
---|
| 284 | @ParamAnnotation(id = "desc") |
---|
| 285 | public String getDescription() { |
---|
| 286 | return description; |
---|
| 287 | } |
---|
| 288 | |
---|
| 289 | public FramsClassBuilder group(String group) { |
---|
| 290 | return group(new Group(group)); |
---|
| 291 | } |
---|
| 292 | |
---|
| 293 | public ParamBuilder param(String id) { |
---|
| 294 | return new ParamBuilder(this).id(id); |
---|
| 295 | } |
---|
| 296 | |
---|
[86] | 297 | } |
---|