EMMA Coverage Report (generated Tue Apr 17 08:51:20 BST 2007)
[all classes][org.jtoolkit.essence.app.pojo.impl]

COVERAGE SUMMARY FOR SOURCE FILE [DataValueClass.java]

nameclass, %method, %block, %line, %
DataValueClass.java100% (2/2)74%  (46/62)64%  (1698/2659)68%  (315.8/465)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DataValueClass100% (1/1)73%  (44/60)64%  (1688/2649)68%  (312.8/462)
asArray (Class, String): Object 0%   (0/1)0%   (0/26)0%   (0/5)
createViaReflection (Class, Object): Object 0%   (0/1)0%   (0/86)0%   (0/22)
getArgumentNumber (Class, InvocationTargetException): int 0%   (0/1)0%   (0/45)0%   (0/9)
getFieldNames (Class): Set 0%   (0/1)0%   (0/10)0%   (0/2)
parseArray (Object): String [] 0%   (0/1)0%   (0/21)0%   (0/4)
project (Class, Object, Object []): Object 0%   (0/1)0%   (0/8)0%   (0/2)
project2 (Object, Object []): Object 0%   (0/1)0%   (0/37)0%   (0/6)
setField (Object [], String, Object): void 0%   (0/1)0%   (0/53)0%   (0/7)
setNew (Object, String, Object, Object []): Object 0%   (0/1)0%   (0/11)0%   (0/2)
setNew2 (Object, String, Object, Object []): Object 0%   (0/1)0%   (0/43)0%   (0/7)
throwCannotBeNull (String, String): Object 0%   (0/1)0%   (0/16)0%   (0/1)
throwCannotBeSet (String): void 0%   (0/1)0%   (0/17)0%   (0/1)
throwCannotParse (Object, Class, Throwable): Object 0%   (0/1)0%   (0/17)0%   (0/1)
throwFailedToSet (String, Object, InstantiationException, Object []): Object 0%   (0/1)0%   (0/54)0%   (0/6)
throwNotAGetter (String): String 0%   (0/1)0%   (0/12)0%   (0/1)
throwUnmatched (String, List, Set, Exception): Object 0%   (0/1)0%   (0/19)0%   (0/1)
returnNull (Class): Object 100% (1/1)29%  (5/17)60%  (1.2/2)
build (Object [], PojoContext): Object 100% (1/1)40%  (117/291)49%  (26/53)
cast (Class, Object, PojoContext): Object 100% (1/1)57%  (174/304)59%  (26.7/45)
getInferedKey (boolean): DataValueClass 100% (1/1)62%  (72/117)79%  (15/19)
getField (Object, String): Object 100% (1/1)66%  (43/65)70%  (7/10)
build (Map, PojoContext): Object 100% (1/1)71%  (32/45)85%  (4.2/5)
asClass (Class, String, PojoContext): Object 100% (1/1)73%  (72/99)83%  (9.1/11)
acquire (Class): DataValueClass 100% (1/1)81%  (22/27)83%  (5/6)
isGetter (Method): boolean 100% (1/1)82%  (53/65)86%  (13.7/16)
asArray2 (Object): Object [] 100% (1/1)83%  (57/69)73%  (11.6/16)
parseMap (Object): Map 100% (1/1)86%  (59/69)89%  (9.8/11)
hashCode (Object): int 100% (1/1)86%  (37/43)80%  (8/10)
equalsCheckNull (Object, Object): boolean 100% (1/1)87%  (13/15)89%  (2.7/3)
getNameFromGetter (String): String 100% (1/1)88%  (56/64)75%  (9/12)
equals (Object, Object): boolean 100% (1/1)90%  (55/61)86%  (12/14)
isAssignableFrom (Class, Class): boolean 100% (1/1)97%  (63/65)98%  (11.7/12)
<static initializer> 100% (1/1)100% (182/182)100% (25/25)
DataValueClass (Class): void 100% (1/1)100% (59/59)100% (13/13)
DataValueClass (Class, String): void 100% (1/1)100% (43/43)100% (11/11)
acquire (Class, String): DataValueClass 100% (1/1)100% (26/26)100% (5/5)
asArray (Object): Object [] 100% (1/1)100% (6/6)100% (1/1)
asMap (Object): Map 100% (1/1)100% (8/8)100% (2/2)
asMap2 (Object): Map 100% (1/1)100% (24/24)100% (3/3)
build (Class, Map, PojoContext): Object 100% (1/1)100% (6/6)100% (1/1)
build (Class, Object [], PojoContext): Object 100% (1/1)100% (6/6)100% (1/1)
buildArray (List, Class, PojoContext): Object [] 100% (1/1)100% (54/54)100% (8/8)
cast (Class, Object): Object 100% (1/1)100% (5/5)100% (1/1)
forName (String): Class 100% (1/1)100% (33/33)100% (9/9)
getConstructor (Class, Map): Constructor 100% (1/1)100% (36/36)100% (9/9)
getFields (): String [] 100% (1/1)100% (3/3)100% (1/1)
getFields (Class): Map 100% (1/1)100% (48/48)100% (10/10)
getGetters (Class): Map 100% (1/1)100% (53/53)100% (11/11)
getMandatoryField (Map): Set 100% (1/1)100% (33/33)100% (7/7)
getType (): Class 100% (1/1)100% (3/3)100% (1/1)
isBaseType (Class): boolean 100% (1/1)100% (12/12)100% (1/1)
isNotAssignableFrom (Class): boolean 100% (1/1)100% (9/9)100% (1/1)
isNotSerializable (): boolean 100% (1/1)100% (3/3)100% (1/1)
isNotSerializable (Class): boolean 100% (1/1)100% (19/19)100% (1/1)
isPrimative (Class): boolean 100% (1/1)100% (4/4)100% (1/1)
isTopClass (Class): boolean 100% (1/1)100% (12/12)100% (1/1)
toString (): String 100% (1/1)100% (4/4)100% (1/1)
toString (Object): String 100% (1/1)100% (39/39)100% (9/9)
trimChars (String, char, char): String 100% (1/1)100% (23/23)100% (3/3)
writeData (DataOutput): void 100% (1/1)100% (5/5)100% (2/2)
     
class DataValueClass$1100% (1/1)100% (2/2)100% (10/10)100% (3/3)
DataValueClass$1 (): void 100% (1/1)100% (3/3)100% (1/1)
convert (DataInput): DataValueClass 100% (1/1)100% (7/7)100% (2/2)

1package org.jtoolkit.essence.app.pojo.impl;
2 
3import org.apache.commons.logging.Log;
4import static org.apache.commons.logging.LogFactory.getLog;
5import org.jetbrains.annotations.NotNull;
6import org.jetbrains.annotations.Nullable;
7import org.jtoolkit.essence.app.pojo.DataValue;
8import org.jtoolkit.essence.app.pojo.Datable;
9import org.jtoolkit.essence.app.pojo.DatableUtils;
10import org.jtoolkit.essence.concurrency.Immutable;
11import org.jtoolkit.essence.data.Mapping;
12import org.jtoolkit.essence.utils.Named;
13import org.jtoolkit.essence.utils.Pair;
14import org.jtoolkit.essence.utils.StringUtils;
15import static org.jtoolkit.essence.utils.StringUtils.*;
16import org.jtoolkit.essence.utils.impl.MapArray;
17 
18import java.io.DataInput;
19import java.io.DataOutput;
20import java.io.IOException;
21import java.io.Serializable;
22import java.lang.reflect.*;
23import java.math.BigDecimal;
24import java.math.BigInteger;
25import java.util.*;
26import static java.util.Arrays.asList;
27import java.util.concurrent.ConcurrentHashMap;
28 
29/*
30   Copyright 2006 Peter Lawrey
31 
32   Licensed under the Apache License, Version 2.0 (the "License");
33   you may not use this file except in compliance with the License.
34   You may obtain a copy of the License at
35 
36       http://www.apache.org/licenses/LICENSE-2.0
37 
38   Unless required by applicable law or agreed to in writing, software
39   distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
40   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41   See the License for the specific language governing permissions and
42   limitations under the License.
43*/
44 
45/**
46 * Helper class for building and decomposing data value objects.
47 *
48 * @author Peter Lawrey
49 */
50@Immutable
51@SuppressWarnings({"unchecked", "RedundantCast", "ClassWithTooManyMethods", "OverlyComplexClass", "OverlyCoupledClass"})
52public class DataValueClass<T> implements Datable {
53    private static final Log LOG = getLog(DataValueClass.class);
54    private static final Map<Object, DataValueClass> pojoClasses = new ConcurrentHashMap<Object, DataValueClass>(256, 0.5f, 16);
55    public static final Class[] BASE_CLASSES = {
56            Boolean.class, Byte.class, Character.class, Short.class, Integer.class, Float.class, Double.class, Long.class,
57            String.class, Class.class, DataValueClass.class, BigInteger.class, BigDecimal.class, Date.class
58    };
59    private static final String[] CLASS_SEP = ",.,$,.data.".split(",");
60    private static final Map<Class, Class> PRIMATIVE_MATCH = new LinkedHashMap<Class, Class>();
61    private static final String[] NO_STRINGS = {};
62    private static final Class[] STRING_CLASS = {String.class};
63 
64    static {
65        PRIMATIVE_MATCH.put(long.class, Long.class);
66        PRIMATIVE_MATCH.put(int.class, Integer.class);
67        PRIMATIVE_MATCH.put(double.class, Double.class);
68        PRIMATIVE_MATCH.put(short.class, Short.class);
69        PRIMATIVE_MATCH.put(byte.class, Byte.class);
70        PRIMATIVE_MATCH.put(float.class, Float.class);
71        PRIMATIVE_MATCH.put(boolean.class, Boolean.class);
72        PRIMATIVE_MATCH.put(char.class, Character.class);
73    }
74 
75    private static final Map<String, Class> CLASS_LOOKUP = new ConcurrentHashMap<String, Class>();
76 
77    static {
78        for (Class clazz : PRIMATIVE_MATCH.keySet())
79            CLASS_LOOKUP.put(clazz.getName(), clazz);
80        CLASS_LOOKUP.put(void.class.getName(), void.class);
81    }
82 
83    private static final Set<Class> BASE_CLASS_SET = new LinkedHashSet<Class>();
84 
85    static {
86        BASE_CLASS_SET.addAll(asList(BASE_CLASSES));
87        BASE_CLASS_SET.addAll(PRIMATIVE_MATCH.keySet());
88    }
89 
90    private final Class<T> type;
91    private final String[] fields;
92    private final Map<String, Integer> fieldOrder;
93    @Nullable private final Map<String, Field> fieldMap;
94    @Nullable private final Map<String, Method> getterMap;
95    @Nullable private final Constructor<T> cons;
96    private final Set<String> mandatoryFields;
97    private final boolean notSerializable;
98    private static final String FIELD = "Field ";
99 
100    @NotNull public static <T> DataValueClass<T> acquire(@NotNull Class<T> clazz) {
101        if (clazz == void.class)
102            throw new IllegalArgumentException("There is no data value class for void.");
103 
104        DataValueClass<T> ret = pojoClasses.get(clazz);
105        if (ret == null)
106            pojoClasses.put(clazz, ret = new DataValueClass(clazz));
107        return ret;
108    }
109 
110    @NotNull private static <T> DataValueClass<T> acquire(@NotNull Class<T> clazz, @NotNull String name) {
111        Pair key = new Pair(clazz, name);
112        DataValueClass<T> ret = pojoClasses.get(key);
113        if (ret == null)
114            pojoClasses.put(key, ret = new DataValueClass(clazz, name));
115        return ret;
116    }
117 
118    @NotNull public static <T> Object[] asArray(@NotNull T dataValue) {
119        return acquire((Class<T>) dataValue.getClass()).asArray2(dataValue);
120    }
121 
122    @NotNull private Object[] asArray2(@NotNull T dataValue) {
123        Object[] ret = new Object[fieldOrder.size()];
124        for (String field : fieldOrder.keySet()) {
125            int order = fieldOrder.get(field);
126            try {
127                Method getter = getterMap.get(field);
128                if (getter != null) {
129                    ret[order] = getter.invoke(dataValue);
130                    continue;
131                }
132                Field f = fieldMap.get(field);
133                ret[order] = f.get(dataValue);
134            } catch (InvocationTargetException e) {
135                ret[order] = e.getCause();
136            } catch (Exception e) {
137                ret[order] = e;
138            }
139        }
140        return ret;
141    }
142 
143    @NotNull public static <T> Map<String, Object> asMap(@NotNull T dataValue) {
144        DataValueClass<T> dataValueClass = acquire((Class<T>) dataValue.getClass());
145        return dataValueClass.asMap2(dataValue);
146    }
147 
148    @NotNull public Map<String, Object> asMap2(@NotNull T dataValue) {
149        if (fieldMap == null)
150            return new MapArray<String, Object>(fieldOrder, new Object[]{dataValue});
151        return new MapArray<String, Object>(fieldOrder, asArray2(dataValue));
152    }
153 
154    @NotNull
155    public T build(@NotNull Map<String, ? extends Object> map, @NotNull PojoContext pojoContext) throws InstantiationException, IllegalArgumentException {
156        if (fields.length == 0) throw new IllegalArgumentException("Not fields for " + type);
157        Object[] objs = new Object[fields.length];
158        for (int i = 0; i < fields.length; i++)
159            objs[i] = map.get(fields[i]);
160        return build(objs, pojoContext);
161    }
162 
163    @NotNull
164    public T build(@NotNull Object[] values, @NotNull PojoContext pojoContext) throws InstantiationException, IllegalArgumentException {
165        // plain data type.
166        if (fieldMap == null)
167            return cast(type, values[0], pojoContext);
168        if (cons != null) {
169            Object[] values2 = new Object[values.length];
170            for (int i = 0; i < fields.length; i++) {
171                String fieldName = fields[i];
172                Field field = fieldMap.get(fieldName);
173                Class<?> type = field.getType();
174                Object value = values[i];
175                values2[i] = cast(type, value, pojoContext);
176                if (isPrimative(type) && values2[i] == null)
177                    return (T) throwCannotBeNull(type.getName(), fieldName);
178            }
179            try {
180                return cons.newInstance(values2);
181            } catch (IllegalArgumentException e) {
182                LOG.error(Thread.currentThread().getName() + ": Attempting to call " + cons + " with " + asList(values2), e);
183                throw e;
184            } catch (IllegalAccessException e) {
185                InternalError error = new InternalError(type.getName() + ": Unable to use the constructor " + cons);
186                error.initCause(e);
187                throw error;
188            } catch (InvocationTargetException e) {
189                return (T) throwCannotBeNull(type.getName(), fields[getArgumentNumber(type, e)]);
190            }
191        }
192        T obj;
193        try {
194            obj = type.newInstance();
195        } catch (IllegalAccessException e) {
196            IllegalArgumentException e2 = new IllegalArgumentException(e.toString());
197            e2.initCause(e.getCause());
198            throw e2;
199        }
200        try {
201            int manditoryFieldCount = 0;
202            for (int i = 0; i < fields.length; i++) {
203                String key = fields[i];
204                Object value = values[i];
205 
206                Field f = fieldMap.get(key);
207                if (f == null)
208                    throw new IllegalArgumentException(type + ": Unable to find field " + key);
209                if (value != null && mandatoryFields.contains(key))
210                    manditoryFieldCount++;
211                f.set(obj, value);
212            }
213            if (manditoryFieldCount < mandatoryFields.size()) {
214                Set<String> mFields = new LinkedHashSet<String>(mandatoryFields);
215                for (String key : fieldOrder.keySet()) {
216                    int idx = fieldOrder.get(key);
217                    Object value = values[idx];
218                    if (value != null)
219                        mFields.remove(key);
220                }
221                throw new IllegalArgumentException(type + ": Manditory fieldMap not set=" + mFields + ", map=" + new MapArray<String, Object>(fieldOrder, values));
222            }
223        } catch (IllegalAccessException e) {
224            InternalError error = new InternalError(type + ": Unable to set fieldMap.");
225            error.initCause(e);
226            throw error;
227        }
228        return obj;
229    }
230 
231    static int getArgumentNumber(Class<?> type, InvocationTargetException e) {
232        Throwable cause = e.getCause();
233        if (cause instanceof IllegalArgumentException) {
234            String mesg = cause.getMessage();
235            if (mesg.startsWith("Argument ") && mesg.endsWith(" must not be null")) {
236                String[] words = mesg.split(" ");
237                try {
238                    return Integer.parseInt(words[1]);
239                } catch (NumberFormatException ignored) {
240                    throw (IllegalArgumentException) cause;
241                }
242            }
243        }
244        throw new IllegalStateException(type.getName() + ": Failed to build.", e.getCause());
245    }
246 
247    @NotNull
248    public static <T> T build(@NotNull Class<T> class2, @NotNull Map<String, Object> map, @NotNull PojoContext pojoContext) throws InstantiationException {
249        return acquire(class2).build(map, pojoContext);
250    }
251 
252    @NotNull
253    public static <T> T build(@NotNull Class<T> class2, @NotNull Object[] values, @NotNull PojoContext pojoContext) throws InstantiationException {
254        return acquire(class2).build(values, pojoContext);
255    }
256 
257    @NotNull
258    public static <T> T[] buildArray(@NotNull List<String[]> strings, Class<T> clazz, @NotNull PojoContext pojoContext) throws InstantiationException {
259        List<T> ret = new ArrayList<T>();
260        if (strings.size() > 1) {
261            String[] heading = strings.get(0);
262            Map<String, Integer> keys = MapArray.asKeys(heading);
263            DataValueClass<T> ph = acquire(clazz);
264            for (String[] row : strings.subList(1, strings.size())) {
265                ret.add(ph.build(new MapArray(keys, row), pojoContext));
266            }
267        }
268        return ret.toArray((T[]) Array.newInstance(clazz, ret.size()));
269    }
270 
271    @Nullable
272    public static <T> T cast(@NotNull Class<? extends T> clazz, @Nullable Object obj) throws UnknownFormatConversionException {
273        return cast(clazz, obj, PojoContext.EMPTY);
274    }
275 
276    @SuppressWarnings({"MethodWithMultipleReturnPoints", "OverlyComplexMethod", "OverlyCoupledMethod", "OverlyLongMethod"})
277    @Nullable
278    public static <T> T cast(@NotNull Class<? extends T> clazz, @Nullable Object obj, @NotNull PojoContext pojoContext) throws UnknownFormatConversionException {
279        if (obj == null || "null".equals(obj)) return returnNull(clazz);
280        if (clazz == Object.class) return (T) obj;
281        // occurs very often.
282        if (clazz == Map.class && obj instanceof Map) return (T) obj;
283 
284        // if a primative update to the object type.
285        Class clazz2 = PRIMATIVE_MATCH.get(clazz);
286        if (clazz2 == null) clazz2 = clazz;
287 
288        // check the object's class.
289        Class objClass = obj.getClass();
290        if (objClass == String.class) obj = expand((String) obj, pojoContext.properties);
291        if (clazz2 == objClass) return (T) obj;
292 
293        if (obj instanceof Number) {
294            if (clazz2 == Number.class) //noinspection CastConflictsWithInstanceof
295                return (T) obj;
296            if (clazz2 == Long.class) return (T) Long.valueOf(((Number) obj).longValue());
297            if (clazz2 == Integer.class) return (T) Integer.valueOf(((Number) obj).intValue());
298            if (clazz2 == Double.class) return (T) Double.valueOf(((Number) obj).doubleValue());
299            if (clazz2 == Float.class) return (T) Float.valueOf(((Number) obj).floatValue());
300            if (clazz2 == Short.class) return (T) Short.valueOf(((Number) obj).shortValue());
301            if (clazz2 == Byte.class) return (T) Byte.valueOf(((Number) obj).byteValue());
302        }
303        if (objClass != String.class) {
304            if (LOG.isDebugEnabled())
305                LOG.debug(Thread.currentThread().getName() + ": Casting " + obj + " from " + obj.getClass() + " to " + clazz2);
306            if (isAssignableFrom(clazz2, objClass)) return (T) obj;
307        }
308        String asStr = obj.toString();
309        if (clazz2 == String.class) return (T) asStr;
310        if (clazz2 == String[].class) return (T) (Object) parseArray(asStr);
311        if (clazz2 == List.class) return (T) asList(parseArray(asStr));
312        if (clazz2 == Set.class) return (T) new LinkedHashSet<String>(asList(parseArray(asStr)));
313        if (clazz2 == Map.class) return (T) parseMap(asStr);
314        if (clazz2 == Named.Source.class) return (T) new Named.MapSource<String>(parseMap(asStr));
315 
316        // empty string means null for all other types.
317        if ("".equals(obj)) return returnNull(clazz);
318        if (clazz2 == Long.class) return (T) Long.valueOf(Long.parseLong(asStr));
319        if (clazz2 == Integer.class) return (T) Integer.valueOf(Integer.parseInt(asStr));
320        if (clazz2 == Double.class) return (T) Double.valueOf(asStr);
321        if (clazz2 == Short.class) return (T) Short.valueOf(Short.parseShort(asStr));
322        if (clazz2 == Byte.class) return (T) Byte.valueOf(Byte.parseByte(asStr));
323        if (clazz2 == Float.class) return (T) Float.valueOf(asStr);
324        if (clazz2 == Boolean.class) return (T) StringUtils.parseBoolean(asStr);
325        if (clazz2 == BigInteger.class) return (T) new BigInteger(asStr);
326        if (clazz2 == BigDecimal.class || clazz2 == Number.class) return (T) new BigDecimal(asStr);
327        if (clazz2 == Character.class) return (T) Character.valueOf(asStr.charAt(0));
328        if (isAssignableFrom(Date.class, clazz2)) return (T) parseDate(asStr);
329        if (isAssignableFrom(Enum.class, clazz2)) return (T) Enum.valueOf((Class<Enum>) clazz2, toUpperCase(asStr));
330        if (clazz2 == Class.class || clazz2 == DataValueClass.class) return (T) asClass(clazz2, asStr, pojoContext);
331        Class<?> componentType = clazz2.getComponentType();
332        if (componentType != null) return (T) asArray(componentType, asStr);
333        return createViaReflection(clazz, obj);
334    }
335 
336    private static <T> T createViaReflection(Class<T> type, Object object) {
337        // look for a static method valueOf(String)
338        try {
339            Method valueOfString = type.getMethod("valueOf", STRING_CLASS);
340            if ((valueOfString.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
341                valueOfString.setAccessible(true);
342                return (T) valueOfString.invoke(null, object.toString());
343            }
344        } catch (NoSuchMethodException ignored) {
345            // ignored
346        } catch (IllegalAccessException e) {
347            return throwCannotParse(object, type, e);
348        } catch (InvocationTargetException e) {
349            throwCannotParse(object, type, e.getCause());
350        }
351 
352        // look for constructor(String)
353        try {
354            Constructor consString = type.getDeclaredConstructor(STRING_CLASS);
355            consString.setAccessible(true);
356            return (T) consString.newInstance(object.toString());
357        } catch (NoSuchMethodException ignored) {
358            // ignored
359        } catch (IllegalAccessException e) {
360            throwCannotParse(object, type, e);
361        } catch (InvocationTargetException e) {
362            throwCannotParse(object, type, e.getCause());
363        } catch (InstantiationException e) {
364            throwCannotParse(object, type, e);
365        }
366        return throwCannotParse(object, type, null);
367    }
368 
369    private static <T> T throwCannotParse(Object object, Class<T> type, Throwable e) {
370        throw new IllegalArgumentException("Cannot parse " + object + " as " + type, e);
371    }
372 
373    private static <T> T asArray(Class<?> componentType, String asStr) {
374        String[] values = parseArray(asStr);
375        Object array = Array.newInstance(componentType, values.length);
376        for (int i = 0; i < values.length; i++)
377            Array.set(array, i, cast(componentType, values[i]));
378        return (T) array;
379    }
380 
381    private static Object asClass(Class clazz2, String asStr, PojoContext pojoContext) {
382        Class pClass = CLASS_LOOKUP.get(asStr);
383        if (pClass != NoClassFound.class && pClass != null) return pClass;
384 
385        // strip any "class " or "interface " prefix.
386        int pos = asStr.indexOf(' ');
387        if (pos >= 0) asStr = asStr.substring(pos + 1);
388        for (String packageName : pojoContext.getPackagePath()) {
389            for (String classSep : CLASS_SEP) {
390                String name = packageName + classSep + asStr;
391                Class<?> aClass = forName(name);
392                if (aClass != null)
393                    return clazz2 == DataValueClass.class ? acquire(aClass) : aClass;
394            }
395        }
396        throw new IllegalArgumentException("Class " + asStr + " not found in " + asList(pojoContext.getPackagePath()));
397    }
398 
399    @Nullable @SuppressWarnings("SameReturnValue")
400    private static <T> T returnNull(Class<T> clazz) {
401        if (isPrimative(clazz)) throw new IllegalArgumentException("Cannot cast null to " + clazz);
402        return null;
403    }
404 
405    private static String[] parseArray(Object object) {
406        String text = object.toString();
407        if (text.length() == 0 || "[]".equals(text)) return NO_STRINGS;
408        text = trimChars(text, '[', ']');
409        return text.split(", ?");
410    }
411 
412    private static String trimChars(String text, char start, char end) {
413        if (text.charAt(0) == start && text.charAt(text.length() - 1) == end)
414            text = text.substring(1, text.length() - 1);
415        return text;
416    }
417 
418    public static Map<String, String> parseMap(Object object) {
419        String text = object.toString();
420        if (text.length() == 0 || "{}".equals(text)) return Collections.emptyMap();
421        text = trimChars(text, '{', '}');
422        Map<String, String> ret = new LinkedHashMap<String, String>();
423        String[] keyValues = text.split(", ?");
424        for (String keyValue : keyValues) {
425            String[] parts = keyValue.split("=", 2);
426            if (parts.length == 1)
427                ret.put(parts[0], "");
428            else
429                ret.put(parts[0], parts[1]);
430        }
431        return ret;
432    }
433 
434    public static <T> boolean equals(@NotNull T obj1, @Nullable Object obj2) {
435        //noinspection ObjectEquality
436        if (obj1 == obj2) return true;
437        if (obj2 == null) return false;
438        if (obj2.getClass() != obj1.getClass()) return false;
439 
440        DataValueClass<T> dvc = acquire((Class<T>) obj1.getClass());
441        try {
442            for (Field f : dvc.fieldMap.values()) {
443                Object val1 = f.get(obj1);
444                Object val2 = f.get(obj2);
445                if (val1 != val2 && (val1 == null || !val1.equals(val2)))
446                    return false;
447            }
448        } catch (IllegalAccessException e) {
449            throw new AssertionError(e);
450        }
451        return true;
452    }
453 
454    public static boolean equalsCheckNull(@Nullable Object obj1, @Nullable Object obj2) {
455        //noinspection ObjectEquality
456        if (obj1 == obj2) return true;
457        //noinspection SimplifiableIfStatement
458        if (obj1 == null || obj2 == null) return false;
459        return obj1.equals(obj2);
460    }
461 
462    static Object throwCannotBeNull(String objectName, String name) {
463        throw new IllegalArgumentException(objectName + ": Failed to build as field " + name + " cannot be null.");
464    }
465 
466    static Object throwUnmatched(String objectName, List<String> matched, Set<String> unmatched, Exception e) {
467        throw new IllegalStateException(objectName + ": Matched " + matched + " but was unable to match " + unmatched, e);
468    }
469 
470    @SuppressWarnings({"MarkerInterface"}) interface NoClassFound {
471    }
472 
473    @Nullable private static Class forName(@NotNull String name) {
474        Class clazz = CLASS_LOOKUP.get(name);
475        if (clazz != null)
476            return clazz == NoClassFound.class ? null : clazz;
477        Class clazz2;
478        try {
479            clazz2 = Class.forName(name);
480        } catch (ClassNotFoundException ignored) {
481            clazz2 = NoClassFound.class;
482        }
483        CLASS_LOOKUP.put(name, clazz2);
484        return clazz2 == NoClassFound.class ? null : clazz2;
485    }
486 
487    @NotNull public String[] getFields() {
488        return fields;
489    }
490 
491    @Nullable public <K> DataValueClass<K> getInferedKey(boolean firstField) {
492        if (fieldMap == null)
493            throw new IllegalStateException("FieldMap is null, cannot get key for " + type);
494        String name = type.getName();
495        if (name.endsWith("$Key") || isPrimative(type))
496            throw new IllegalStateException("Cannot get a key type for " + name);
497        Class clazz = forName(name + "$Key");
498        if (clazz == null)
499            clazz = forName(name + "Key");
500        if (clazz != null)
501            return acquire((Class<K>) clazz);
502        if (firstField) {
503            // cannot determine the key type of an interface.
504            if (type.isInterface() || type == Object.class)
505                return null;
506 
507            String key = fields[0];
508            Field field = fieldMap.get(key);
509            if (field == null)
510                throw new IllegalArgumentException("Key " + key + " is not an attribute of " + type);
511            return (DataValueClass<K>) acquire(field.getType(), key);
512        } else {
513            return (DataValueClass<K>) acquire(Object.class);
514        }
515    }
516 
517    @NotNull public Class<T> getType() {
518        return type;
519    }
520 
521    /**
522     * This is similar to Class.isNotAssignableFrom except it also handled primatives.
523     */
524    public boolean isNotAssignableFrom(@NotNull Class class2) {
525        return !isAssignableFrom(type, class2);
526    }
527 
528    private static final Map<Pair<Class, Class>, Boolean> IS_ASSIGNABLE_CACHE = new ConcurrentHashMap<Pair<Class, Class>, Boolean>(256, 0.5f, 16);
529 
530    public static boolean isAssignableFrom(@NotNull Class class1, @NotNull Class class2) {
531        if (class1 == class2 || class1 == Object.class) return true;
532        Pair<Class, Class> key = new Pair<Class, Class>(class1, class2);
533        Boolean ret = IS_ASSIGNABLE_CACHE.get(key);
534        if (ret != null) return ret;
535        Class pclass1 = PRIMATIVE_MATCH.get(class1);
536        if (pclass1 != null)
537            return pclass1 == class2;
538        Class pclass2 = PRIMATIVE_MATCH.get(class2);
539        if (pclass2 != null)
540            return pclass2 == class1;
541        IS_ASSIGNABLE_CACHE.put(key, ret = class1.isAssignableFrom(class2));
542        return ret;
543    }
544 
545    public static boolean isBaseType(@NotNull Class type) {
546        return BASE_CLASS_SET.contains(type) || isAssignableFrom(Enum.class, type);
547    }
548 
549    @NotNull
550    public static <T> T setNew(@NotNull T pojo, @NotNull String field, @NotNull Object value, Object... others) throws IllegalArgumentException {
551        DataValueClass<T> dataValueClass = (DataValueClass<T>) acquire(pojo.getClass());
552        return dataValueClass.setNew2(pojo, field, value, others);
553    }
554 
555    @NotNull
556    private T setNew2(@NotNull T dataValue, @NotNull String field, @NotNull Object value, Object... others) throws IllegalArgumentException {
557        Object[] asArray = asArray2(dataValue);
558        setField(asArray, field, value);
559        for (int i = 0; i < others.length - 1; i += 2)
560            setField(asArray, others[i].toString(), others[i + 1]);
561        try {
562            return build(asArray, PojoContext.EMPTY);
563        } catch (InstantiationException e) {
564            return (T) throwFailedToSet(field, value, e, others);
565        }
566    }
567 
568    private static Object throwFailedToSet(String field, Object value, InstantiationException e, Object... others) {
569        StringBuilder sb = new StringBuilder();
570        if (field != null)
571            sb.append(field).append('=').append(value);
572        for (int i = 0; i < others.length - 1; i += 2)
573            sb.append(", ").append(others[i]).append('=').append(others[i + 1]);
574        throw new IllegalArgumentException("Failed to set " + sb, e.getCause());
575    }
576 
577    @NotNull
578    public static <T2, T> T project(@NotNull Class<T> clazz, @NotNull T2 pojo, Object... keyValues) throws IllegalArgumentException {
579        DataValueClass<T> dataValueClass = acquire(clazz);
580        return dataValueClass.project2(pojo, keyValues);
581    }
582 
583    @NotNull private <T2> T project2(@NotNull T2 dataValue, Object... keyValues) throws IllegalArgumentException {
584        Map<String, Object> map = asMap(dataValue);
585        for (int i = 0; i < keyValues.length - 1; i += 2)
586            map.put(keyValues[i].toString(), keyValues[i + 1]);
587        try {
588            return build(map, PojoContext.EMPTY);
589        } catch (InstantiationException e) {
590            return (T) throwFailedToSet(null, null, e, keyValues);
591        }
592    }
593 
594    private void setField(Object[] asArray, String field, Object value) {
595        Integer fieldNum = fieldOrder.get(field);
596        if (fieldNum == null) throw new IllegalArgumentException(FIELD + field + " not a field of " + type);
597        if (fieldMap == null) throwCannotBeSet(field);
598        else {
599            Field f = fieldMap.get(field);
600            if (f == null) throwCannotBeSet(field);
601            else
602                asArray[fieldNum] = cast(f.getType(), value);
603        }
604    }
605 
606    private void throwCannotBeSet(String field) {
607        throw new IllegalArgumentException(FIELD + field + " cannot be set for " + type);
608    }
609 
610    public static <T> int hashCode(@NotNull T t) {
611        DataValueClass<T> dvc = acquire((Class<T>) t.getClass());
612        int ret = 0;
613        try {
614            for (Field f : dvc.fieldMap.values()) {
615                Object obj = f.get(t);
616                ret = ret * 37 + (obj == null ? 0 : obj.hashCode());
617            }
618        } catch (IllegalAccessException e) {
619            throw new AssertionError(e);
620        }
621        return ret;
622    }
623 
624    public static <T> String toString(@NotNull T t) {
625        StringBuilder sb = new StringBuilder(32);
626        String name = t.getClass().getName();
627        int pos = name.lastIndexOf('$');
628        if (pos < 0)
629            pos = name.lastIndexOf('.');
630        sb.append(name.substring(pos + 1));
631        sb.append(' ');
632        sb.append(asMap(t));
633        return sb.toString();
634    }
635 
636    public String toString() {
637        return type.getName();
638    }
639 
640    private DataValueClass(Class<T> clazz) {
641        type = clazz;
642        getterMap = getGetters(clazz);
643        fieldMap = getFields(clazz);
644        cons = getConstructor(clazz, fieldMap);
645        Set<String> fieldsSet = new LinkedHashSet<String>();
646        fieldsSet.addAll(fieldMap.keySet()); // the first field must be the first field in the code for getInferedKey().
647        fieldsSet.addAll(getterMap.keySet()); // the getter field order is unsafe so put them last.
648        fields = fieldsSet.toArray(new String[fieldsSet.size()]);
649        fieldOrder = MapArray.asKeys(fields);
650        mandatoryFields = getMandatoryField(fieldMap);
651        notSerializable = isNotSerializable(clazz);
652    }
653 
654    private DataValueClass(Class<T> clazz, String name) {
655        type = clazz;
656        getterMap = null;
657        fieldMap = null;
658        cons = null;
659        fields = new String[]{name};
660        fieldOrder = MapArray.asKeys(fields);
661        mandatoryFields = new HashSet<String>();
662        mandatoryFields.add(name);
663        notSerializable = isNotSerializable(clazz);
664    }
665 
666    @Nullable private static <T> Constructor<T> getConstructor(Class<T> clazz, Map<String, Field> fieldMap) {
667        Class[] classes = new Class[fieldMap.size()];
668        int i = 0;
669        for (Field f : fieldMap.values())
670            classes[i++] = f.getType();
671        try {
672            Constructor<T> cons = clazz.getDeclaredConstructor(classes);
673            cons.setAccessible(true);
674            return cons;
675        } catch (NoSuchMethodException ignored) {
676            return null;
677        }
678    }
679 
680    private static Set<String> getMandatoryField(Map<String, Field> fields) {
681        Set<String> ret = new LinkedHashSet<String>();
682        for (Field f : fields.values()) {
683            Class<?> type = f.getType();
684            if (isPrimative(type) || f.getAnnotation(NotNull.class) != null)
685                ret.add(f.getName());
686        }
687        return ret;
688    }
689 
690    @NotNull static Map<String, Field> getFields(Class clazz) {
691        Map<String, Field> fields;
692        Class superclass = clazz.getSuperclass();
693        if (isTopClass(superclass))
694            fields = getFields(superclass);
695        else
696            fields = new LinkedHashMap<String, Field>();
697        for (Field f : clazz.getDeclaredFields()) {
698            // drop static or transient.
699            if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0)
700                continue;
701            f.setAccessible(true);
702            fields.put(f.getName(), f);
703        }
704        return fields;
705    }
706 
707    private static boolean isTopClass(Class superclass) {
708        return superclass != null && superclass != Object.class && superclass != DataValue.class;
709    }
710 
711    @NotNull private static Map<String, Method> getGetters(Class<?> clazz) {
712        Map<String, Method> getters;
713        Class superclass = clazz.getSuperclass();
714        if (isTopClass(superclass))
715            getters = getGetters(superclass);
716        else
717            getters = new LinkedHashMap<String, Method>();
718        for (Method method : clazz.getDeclaredMethods()) {
719            if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC | Modifier.TRANSIENT)) != Modifier.PUBLIC)
720                continue;
721            if (isGetter(method)) {
722                method.setAccessible(true);
723                getters.put(getNameFromGetter(method.getName()), method);
724            }
725        }
726        return getters;
727    }
728 
729    private static boolean isGetter(Method method) {
730        Class<?> returnType = method.getReturnType();
731        if (returnType == void.class)
732            return false;
733        Class<?>[] classes = method.getParameterTypes();
734        if (classes.length > 0)
735            return false;
736        String name = method.getName();
737        if (name.startsWith("get"))
738            return name.length() > 3;
739        // otherwise must be a boolean getter.
740        if (returnType != Boolean.class && returnType != boolean.class)
741            return false;
742        if (name.startsWith("has"))
743            return name.length() > 3;
744        //noinspection SimplifiableIfStatement
745        if (name.startsWith("is"))
746            return name.length() > 2;
747        return false;
748    }
749 
750    private static String getNameFromGetter(String name) {
751        String name2;
752        if (name.startsWith("get") || name.startsWith("has")) {
753            name2 = name.substring(3);
754        } else if (name.startsWith("is")) {
755            name2 = name.substring(2);
756        } else {
757            return throwNotAGetter(name);
758        }
759        if (name2.length() < 1)
760            return throwNotAGetter(name);
761        if (Character.isUpperCase(name2.charAt(0))) {
762            // if two upper chars leave unchanged.
763            if (name2.length() > 1 && Character.isUpperCase(name2.charAt(1))) {
764                return name2;
765            }
766            return Character.toLowerCase(name2.charAt(0)) + name2.substring(1);
767        }
768        return name2;
769    }
770 
771    private static String throwNotAGetter(String name) {
772        throw new IllegalArgumentException("Not a getter method " + name);
773    }
774 
775    public static boolean isPrimative(Class clazz) {
776        return PRIMATIVE_MATCH.containsKey(clazz);
777    }
778 
779    public static Set<String> getFieldNames(Class clazz) {
780        DataValueClass dvClass = acquire(clazz);
781        return new LinkedHashSet(Arrays.asList(dvClass.getFields()));
782    }
783 
784    public static Object getField(@NotNull Object value, @NotNull String name) throws NoSuchElementException, IllegalAccessException, InvocationTargetException {
785        DataValueClass<?> dvClass = acquire(value.getClass());
786        Method method = dvClass.getterMap.get(name);
787        if (method != null)
788            return method.invoke(value);
789        Field field = (Field) dvClass.fieldMap.get(name);
790        if (field == null)
791            throw new NoSuchElementException("Cannot find field " + name + " for " + value.getClass());
792        try {
793            return field.get(value);
794        } catch (IllegalAccessException ignored) {
795            throw new NoSuchElementException("Cannot access field " + name + " for " + value.getClass());
796        }
797    }
798 
799    static {
800        DatableUtils.registerBuilder(DataValueClass.class, new Mapping<DataInput, DataValueClass>() {
801            public DataValueClass convert(DataInput in) throws IOException {
802                Class type = (Class) DatableUtils.readObject(in);
803                return acquire(type);
804            }
805        });
806    }
807 
808    public void writeData(@NotNull DataOutput out) throws IOException {
809        DatableUtils.writeObject(out, type);
810    }
811 
812    public boolean isNotSerializable() {
813        return notSerializable;
814    }
815 
816    private boolean isNotSerializable(Class<T> clazz) {
817        return !isAssignableFrom(Serializable.class, clazz) && !isAssignableFrom(Datable.class, clazz) && !isAssignableFrom(DataValue.class, clazz) && clazz.getComponentType() == null;
818    }
819}

[all classes][org.jtoolkit.essence.app.pojo.impl]
EMMA 2.0.5312 (C) Vladimir Roubtsov