/******************************************************************************* * Copyright 2013-2015 alladin-IT GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package at.alladin.rmbt.shared.hstoreparser; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import at.alladin.rmbt.shared.hstoreparser.annotation.HstoreCast; import at.alladin.rmbt.shared.hstoreparser.annotation.HstoreCollection; import at.alladin.rmbt.shared.hstoreparser.annotation.HstoreKey; /** * * @author lb * */ public class HstoreParser<T> { private Class<T> clazz; private HashMap<String, Field> fieldsWithKeys; private Constructor<T> constructor; private Hstore hstore; /** * * @param clazz * @throws HstoreParseException */ public HstoreParser(Class<T> clazz, Hstore hstore) throws HstoreParseException { this.clazz = clazz; this.fieldsWithKeys = new HashMap<>(); this.hstore = hstore; //search all fields of all classes for annotated items initFields(clazz); //find empty constructor try { constructor = clazz.getConstructor(); } catch (NoSuchMethodException | SecurityException e) { throw new HstoreParseException(HstoreParseException.HSTORE_CONSTRUCTOR_EXCEPTION + clazz.getCanonicalName(), e); } } /** * * @param clazz * @throws HstoreParseException */ private void initFields(Class<?> clazz) throws HstoreParseException { if (clazz.getSuperclass() != null) { initFields(clazz.getSuperclass()); } for (Field f : clazz.getDeclaredFields()) { if (f.isAnnotationPresent(HstoreKey.class)) { //annotation: HstoreKey was found String hstoreKey = ((HstoreKey) f.getAnnotation(HstoreKey.class)).value(); //check for duplicates: if (!fieldsWithKeys.containsKey(hstoreKey)) { fieldsWithKeys.put(hstoreKey, f); } else { throw new HstoreParseException(HstoreParseException.HSTORE_OBJECT_KEY_ALREADY_IN_USE + hstoreKey); } } if (f.isAnnotationPresent(HstoreCollection.class)) { //annotation: HstoreCollection was found Class<?> hstoreClazz = ((HstoreCollection) f.getAnnotation(HstoreCollection.class)).value(); //insert class into hstore if (!hstore.getParserMap().containsKey(hstoreClazz)) { hstore.addClass(hstoreClazz); } } } } /** * * @param hstore * @return * @throws HstoreParseException * @throws JSONException */ public T fromString(String hstore) throws HstoreParseException { try { return fromJson(new JSONObject("{" + hstore.replace("=>", ":") + "}")); } catch (JSONException e) { throw new HstoreParseException(HstoreParseException.HSTORE_FORMAT_UNSUPPORTED + hstore, e); } } /** * * @param hstore * @return * @throws HstoreParseException */ @SuppressWarnings({ "unchecked", "rawtypes" }) public T fromJson(JSONObject json) throws HstoreParseException { T object; try { object = constructor.newInstance(); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new HstoreParseException(HstoreParseException.HSTORE_PARSE_EXCEPTION + e.getLocalizedMessage(), e); } if (object == null) { throw new HstoreParseException(HstoreParseException.HSTORE_COULD_NOT_INSTANTIATE + clazz.getCanonicalName()); } //iterate through all json entries: Iterator<String> jsonKeys = json.keys(); while (jsonKeys.hasNext()) { String key = jsonKeys.next(); //if fieldsWithKeys contain a key (=it was annotated with @HstoreKey) then try to set the value of the field in T object if (fieldsWithKeys.containsKey(key)) { final Field field = fieldsWithKeys.get(key); try { Object value = getFromJsonByField(json, key, field); field.setAccessible(true); //cast / new instance needed? if (field.isAnnotationPresent(HstoreCollection.class)) { Class<?> fieldClazz = field.getType(); //System.out.println("FieldClazz: " + fieldClazz + " -> " + fieldClazz.isAssignableFrom(Collection.class)); if (Collection.class.isAssignableFrom(fieldClazz)) { //get collection and instantiate if necessary Collection collection = (Collection) field.get(object); if (collection == null) { collection = (Collection) fieldClazz.newInstance(); } ParameterizedType fieldType = (ParameterizedType) field.getGenericType(); Class<?> genericClazz = (Class<?>) fieldType.getActualTypeArguments()[0]; //System.out.println("genericClazz: " + genericClazz); //Object jsonObject = hstore.toJson((String) value, genericClazz); Object jsonObject = null; //System.out.println(value); if (!value.equals(JSONObject.NULL)) { if (value instanceof JSONArray || value instanceof JSONObject) { jsonObject = hstore.toJson(value.toString(), genericClazz); } else { jsonObject = hstore.toJson((String) value, genericClazz); } } else { jsonObject = hstore.toJson(null, genericClazz); } if (jsonObject != null) { if (jsonObject instanceof JSONArray) { //System.out.println(jsonObject); Object[] array = hstore.fromJSONArray((JSONArray) jsonObject, genericClazz); for (int i = 0; i < array.length; i++) { //System.out.println("Created element: " + array[i]); collection.add(array[i]); } } else { Object element = hstore.fromString((String) value, genericClazz); //System.out.println("Created element: " + element); collection.add(element); } } value = collection; } else { throw new HstoreParseException(HstoreParseException.HSTORE_MUST_BE_A_COLLECTION + field + " - " + clazz.getCanonicalName()); } } if (field.isAnnotationPresent(HstoreCast.class)) { HstoreCast castDef = field.getAnnotation(HstoreCast.class); if (castDef.simpleCast()) { //simple cast field.set(object, castDef.clazz().cast(value)); } else { //new instance field.set(object, castDef.clazz().getConstructor(castDef.constructorParamClazz()).newInstance(value)); } } else { if (JSONObject.NULL.equals(value)) { field.set(object, null); } else { field.set(object, parseFieldValue(field, value)); } } } catch (IllegalAccessException | IllegalArgumentException | JSONException e) { throw new HstoreParseException(HstoreParseException.HSTORE_COULD_NOT_INSTANTIATE + clazz.getCanonicalName(), e); } catch (InstantiationException e) { throw new HstoreParseException(HstoreParseException.HSTORE_COULD_NOT_INSTANTIATE + clazz.getCanonicalName(), e); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassCastException e) { System.out.println("field: " + field.toString() + ", key: " + key); e.printStackTrace(); } } } return object; } /** * * @param hstoreKey * @param object * @return * @throws HstoreParseException */ public <U> Object getValue(String hstoreKey, U object) throws HstoreParseException { Field field = fieldsWithKeys.get(hstoreKey); if (field != null) { try { field.setAccessible(true); return field.get(object); } catch (IllegalArgumentException | IllegalAccessException e) { throw new HstoreParseException(HstoreParseException.HSTORE_COULD_NOT_GET_VALUE + clazz.getCanonicalName() + "." + field.getName() + "\n", e); } } return null; } /** * * @return */ public Set<Field> getAnnotatedFields() { return new HashSet<>(fieldsWithKeys.values()); } /** * * @return * @throws HstoreParseException */ public <U> Map<String, Object> getValueMap(U object) throws HstoreParseException { HashMap<String, Object> resultMap = new HashMap<>(); for (Entry<String, Field> e : fieldsWithKeys.entrySet()) { try { e.getValue().setAccessible(true); resultMap.put(e.getKey(), e.getValue().get(object)); } catch (Exception ex) { ex.printStackTrace(); throw new HstoreParseException(HstoreParseException.HSTORE_COULD_NOT_GET_VALUE + clazz.getCanonicalName() + "." + e.getValue().getName() + "\n", ex); } } return resultMap; } /** * parses a simple hstore String to a JSONObject * @param hstore * @return {@link JSONObject} or null if an error occured */ public static JSONObject parseToJson(String hstore) { try { return new JSONObject("{" + hstore.replace("=>", ":") + "}"); } catch (JSONException e) { return null; } } /** * * @param f * @param o * @return */ public static Object parseFieldValue(Field f, Object o) { if (o != JSONObject.NULL) { if (f.getType().equals(Integer.class) || f.getType().equals(Integer.TYPE)) { return Integer.parseInt(String.valueOf(o)); } else if (f.getType().equals(String.class)) { return String.valueOf(o); } else if (f.getType().equals(Long.class) || f.getType().equals(Long.TYPE)) { return Long.parseLong(String.valueOf(o)); } else if (f.getType().equals(Boolean.class) || f.getType().equals(Boolean.TYPE)) { return Boolean.parseBoolean(String.valueOf(o)); } else if (f.getType().equals(Float.class) || f.getType().equals(Float.TYPE)) { return Float.parseFloat(String.valueOf(o)); } else if (f.getType().equals(Double.class) || f.getType().equals(Double.TYPE)) { return Double.parseDouble(String.valueOf(o)); } else if (f.getType().equals(Short.class) || f.getType().equals(Short.TYPE)) { return Short.parseShort(String.valueOf(o)); } else { return o; } } return null; } /** * get a specific key from json by preserving the fields type * @param json * @param key * @param toField * @return * @throws JSONException */ public static Object getFromJsonByField(JSONObject json, String key, Field toField) throws JSONException { final Object o = json.get(key); if (o != JSONObject.NULL) { if (toField.getType().equals(Integer.class) || toField.getType().equals(Integer.TYPE)) { return Integer.parseInt(String.valueOf(o)); } else if (toField.getType().equals(String.class)) { return o; } else if (toField.getType().equals(Long.class) || toField.getType().equals(Long.TYPE)) { return Long.parseLong(String.valueOf(o)); } else if (toField.getType().equals(Boolean.class) || toField.getType().equals(Boolean.TYPE)) { return Boolean.parseBoolean(String.valueOf(o)); } else if (toField.getType().equals(Float.class) || toField.getType().equals(Float.TYPE)) { return Float.parseFloat(String.valueOf(o)); } else if (toField.getType().equals(Double.class) || toField.getType().equals(Double.TYPE)) { return Double.parseDouble(String.valueOf(o)); } else { return o; } } return JSONObject.NULL; } /** * * @param json * @param key * @param toField * @return * @throws JSONException */ public static Object getFromJsonByField2(JSONObject json, String key, Field toField) throws JSONException { final Object o = json.get(key); final Class<?> fieldType = toField.getType(); if (o != JSONObject.NULL) { if (fieldType.equals(Integer.class) || fieldType.equals(Integer.TYPE)) { return json.getInt(key); } else if (fieldType.equals(String.class)) { return o; } else if (fieldType.equals(Long.class) || fieldType.equals(Long.TYPE)) { return json.getLong(key); } else if (fieldType.equals(Boolean.class) || fieldType.equals(Boolean.TYPE)) { return json.getBoolean(key); } else if (fieldType.equals(Float.class) || fieldType.equals(Float.TYPE)) { return (float) json.getDouble(key); } else if (fieldType.equals(Double.class) || fieldType.equals(Double.TYPE)) { return json.getDouble(key); } else { return o; } } return JSONObject.NULL; } }