}
return object;
}
java.util.Set<String> unknownFields = new HashSet<String>(json.keySet());
for (Iterator<CodableFieldInfo> fields = classInfo.values().iterator(); fields.hasNext();) {
CodableFieldInfo field = fields.next();
if (field.isWriteOnly()) {
continue;
}
String fieldName = field.getName();
unknownFields.remove(fieldName);
Class type = field.getType();
Object value = json.opt(fieldName);
if (value == null) {
field.set(object, json.getLineNumberInfo(), value, LineNumberInfo.MissingInfo);
continue;
}
LineNumberInfo keyInfo = json.getKeyLineNumber(fieldName);
LineNumberInfo valInfo = json.getValLineNumber(fieldName);
if (JSONCodable.class.isAssignableFrom(type)) {
Object oValue = value;
try {
value = type.newInstance();
} catch (InstantiationException ex) {
CodecExceptionLineNumber celn = translateInstantiationException(type, valInfo);
throw celn;
} catch (IllegalAccessException ex) {
throw new CodecExceptionLineNumber("Could not access the type or the constructor of " +
type.getName(), valInfo);
}
try {
((JSONCodable) value).fromJSONObject(new JSONObject(oValue.toString()));
} catch (Exception ex) {
throw new CodecExceptionLineNumber(ex, valInfo);
}
} else if (field.isArray()) {
value = decodeArrayInternal(type, value, valInfo, warnings);
} else if (field.isNative()) {
if (value.getClass() != type) {
if (value.getClass() == Integer.class || value.getClass() == int.class) {
// upconvert integer values to long if the field requires long
if (type == Long.class || type == long.class || type == AtomicLong.class) {
value = new Long(((Integer) value).longValue());
}
// upconvert integer values to double if the field requires double
if (type == Double.class || type == double.class) {
value = new Double(((Integer) value));
}
// downconvert integer to short if the field requires short
if (type == Short.class || type == short.class) {
value = new Short(((Integer) value).shortValue());
}
}
// downconvert double to float if the field requires a float
if ((value.getClass() == double.class || value.getClass() == Double.class) &&
(type == float.class || type == Float.class)) {
value = new Float(((Double) value));
}
// upconvert long values to double if the field requires double
if ((value.getClass() == long.class || value.getClass() == Long.class) &&
(type == double.class || type == Double.class)) {
value = new Double(((Integer) value));
}
// upconvert float values to double if the field requires double
if ((value.getClass() == float.class || value.getClass() == Float.class) &&
(type == double.class || type == Double.class)) {
value = new Double(((Float) value));
}
if (value.getClass() == String.class) {
try {
// convert String values to int if the field requires int
if (type == Integer.class || type == int.class || type == AtomicInteger.class) {
value = Integer.parseInt((String) value);
}
// convert String values to long if the field requires long
if (type == long.class || type == Long.class || type == AtomicLong.class) {
value = Long.parseLong((String) value);
}
// convert String values to double if the field requires double
if (type == double.class || type == Double.class) {
value = Double.parseDouble((String) value);
}
// convert String values to boolean if the field requires boolean
if (type == boolean.class || type == Boolean.class || type == AtomicBoolean.class) {
value = Boolean.parseBoolean((String) value);
}
} catch (NumberFormatException ex) {
if (type == Integer.class || type == int.class || type == AtomicInteger.class) {
throw new CodecExceptionLineNumber("cannot convert the string to an integer", valInfo);
} else if (type == long.class || type == Long.class || type == AtomicLong.class) {
throw new CodecExceptionLineNumber("cannot convert the string to a long", valInfo);
} else if (type == double.class || type == Double.class) {
throw new CodecExceptionLineNumber("cannot convert the string to a double", valInfo);
} else {
throw new IllegalStateException("unhandled case in the NumberFormatException");
}
}
}
if (type == AtomicInteger.class) {
value = new AtomicInteger((Integer) value);
} else if (type == AtomicLong.class) {
value = new AtomicLong((Long) value);
} else if (type == AtomicBoolean.class) {
value = new AtomicBoolean((Boolean) value);
}
}
// this space left intentionally blank
} else if (field.isMap()) {
Map map;
try {
map = (Map) type.newInstance();
} catch (Exception ex) {
throw new CodecExceptionLineNumber(ex, keyInfo);
}
JSONObject jmap = (JSONObject) value;
Class vc = (Class) field.getGenericTypes()[1];
boolean va = field.isMapValueArray();
for (Iterator<String> iter = jmap.keys(); iter.hasNext();) {
String key = iter.next();
if (field.isInterned()) {
key = key.intern();
}
map.put(key, va ? decodeArrayInternal(vc, jmap.get(key), jmap.getKeyLineNumber(key), warnings)
: decodeObjectInternal(vc, jmap.get(key), jmap.getKeyLineNumber(key), warnings));
}
value = map;
} else if (field.isCollection()) {
Collection col;
JSONArray jarr;
try {
col = (Collection) type.newInstance();
} catch (Exception ex) {
throw new CodecExceptionLineNumber(ex, keyInfo);
}
try {
jarr = (JSONArray) value;
} catch (Exception ex) {
throw new CodecExceptionLineNumber(ex, valInfo);
}
Class vc = field.getCollectionClass();
boolean ar = field.isCollectionArray();
for (int i = 0; i < jarr.length(); i++) {
col.add(ar ? decodeArrayInternal(vc, jarr.get(i), jarr.getLineNumber(i), warnings) :
decodeObjectInternal(vc, jarr.get(i), jarr.getLineNumber(i), warnings));
}
value = col;
} else if (field.isEnum()) {
try {
String valString = value.toString();
if (valString != "") {
value = Enum.valueOf(type, valString.toUpperCase());
}
} catch (Exception ex) {
throw new CodecExceptionLineNumber(ex, valInfo);
}
} else if (field.isCodable()) {
value = decodeObjectInternal(type, value, valInfo, warnings);
}
field.set(object, json.getLineNumberInfo(), value, valInfo);
}
if (object instanceof SuperCodable) {
((SuperCodable) object).postDecode();
}
if (warnings != null) {