/* BasicDConnectMessage.java Copyright (c) 2016 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.message; import android.content.Intent; import android.os.Bundle; import android.os.Parcelable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * 共通で使用するメッセージ. * @author NTT DOCOMO, INC. */ class BasicDConnectMessage extends HashMap<String, Object> implements DConnectMessage { /** * シリアルバージョン. */ private static final long serialVersionUID = 1L; /** * 空っぽのメッセージを生成する. */ BasicDConnectMessage() { } /** * メッセージをMapから生成する. * * @param map メッセージMap */ BasicDConnectMessage(final Map<String, Object> map) { super(map); } /** * メッセージをJSONから生成する. * * @param json メッセージJSON * @throws JSONException JSONエラー. */ BasicDConnectMessage(final String json) throws JSONException { this(new JSONObject(json)); } /** * メッセージをJSONから生成する. * * @param json メッセージJSON * @throws JSONException JSONエラー. */ @SuppressWarnings("unchecked") BasicDConnectMessage(final JSONObject json) throws JSONException { convertJSONToMap(json, this); } /** * メッセージをIntentから生成する. * @param intent メッセージIntent */ BasicDConnectMessage(final Intent intent) throws JSONException { convertBundleToMap(intent.getExtras(), this); } /** * Stringを取得する. * @param key キー * @return 値 */ public String getString(final String key) { if (!containsKey(key)) { return null; } Object value = get(key); if (value != null && !(value instanceof String)) { return null; } return (String) value; } /** * intを取得する. * @param key キー * @return 値 */ public int getInt(final String key) { if (!containsKey(key)) { return 0; } Object value = get(key); if (value == null || !(value instanceof Integer)) { return 0; } return ((Number) value).intValue(); } @Override public long getLong(String key) { if (!containsKey(key)) { return 0; } Object value = get(key); if (value == null || !(value instanceof Long)) { return 0; } return ((Number) value).longValue(); } /** * booleanを取得する. * @param key キー * @return 値 */ public boolean getBoolean(final String key) { if (!containsKey(key)) { return false; } Object value = get(key); if (value == null || !(value instanceof Boolean)) { return false; } return (Boolean) value; } /** * doubleを取得する. * @param key キー * @return 値 */ public float getFloat(final String key) { if (!containsKey(key)) { return 0; } Object value = get(key); if (value == null || !(value instanceof Number)) { return 0f; } return ((Number) value).floatValue(); } @Override public double getDouble(String key) { if (!containsKey(key)) { return 0; } Object value = get(key); if (value == null || !(value instanceof Number)) { return 0f; } return ((Number) value).doubleValue(); } @Override public List<Object> getList(final String key) { if (!containsKey(key)) { return null; } Object value = get(key); if (value == null || !(value instanceof List<?>)) { return null; } return castListObject((List<?>) value); } @Override public DConnectMessage getMessage(final String key) { if (!containsKey(key)) { return null; } Object value = get(key); if (value == null || !(value instanceof DConnectMessage)) { return null; } return (DConnectMessage) value; } @Override public String toString(final int indent) { final StringBuilder builder = new StringBuilder(); AbstractMessageParser parser = new AbstractMessageParser() { private boolean mFirstKey; private int mParseInArray; private int mFirstArrayValue; private int mIndent; @Override public void startParse() { } @Override public void onKey(final String key) { if (!mFirstKey) { builder.append(","); } appendIndentSpace(); mFirstKey = false; builder.append("\""); builder.append(key); builder.append("\""); builder.append(":"); } @Override public void onValue(final Object value) { if ((mParseInArray & (1 << mIndent)) != 0) { if ((mFirstArrayValue & (1 << mIndent)) != 0) { builder.append(","); } appendIndentSpace(); } mFirstArrayValue |= (1 << mIndent); if (Integer.class.isInstance(value) || Float.class.isInstance(value) || Double.class.isInstance(value) || Long.class.isInstance(value) || Byte.class.isInstance(value) || Short.class.isInstance(value) || Boolean.class.isInstance(value)) { builder.append(value); } else if (value == null) { builder.append("null"); } else { builder.append("\""); builder.append(value); builder.append("\""); } } @Override public void startMap() { appendIndentSpace(); builder.append("{"); mFirstKey = true; mIndent++; } @Override public void endMap() { mFirstArrayValue &= ~(1 << mIndent); mIndent--; mFirstArrayValue |= (1 << mIndent); appendIndentSpace(); builder.append("}"); } @Override public void startArray() { mIndent++; if ((mFirstArrayValue & (1 << mIndent)) != 0) { builder.append(","); } mParseInArray |= (1 << mIndent); builder.append("["); } @Override public void endArray() { mParseInArray &= ~(1 << mIndent); mFirstArrayValue &= ~(1 << mIndent); mIndent--; mFirstArrayValue |= (1 << mIndent); appendIndentSpace(); builder.append("]"); } @Override public void endParse() { } private void appendIndentSpace() { if (indent <= 0) { return; } if (builder.length() > 0) { appendBreakLine(); } for (int i = 0; i < mIndent * indent; i++) { builder.append(" "); } } private void appendBreakLine() { if (indent <= 0) { return; } builder.append("\n"); } }; parser.parse(this); return builder.toString(); } private static void convertBundleToMap(final Bundle bundle, final DConnectMessage message) throws JSONException { if (bundle == null || message == null) { return; } for (String key : bundle.keySet()) { Object object = bundle.get(key); if (object instanceof Bundle) { DConnectMessage m = new BasicDConnectMessage(); convertBundleToMap((Bundle) object, m); message.put(key, m); } else if (object instanceof Integer[] || object instanceof Long[] || object instanceof Short[] || object instanceof Byte[] || object instanceof Character[] || object instanceof Float[] || object instanceof Double[] || object instanceof Boolean[] || object instanceof String[]) { Object[] bb = (Object[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof Integer) { message.put(key, ((Integer) object).intValue()); } else if (object instanceof int[]) { int[] bb = (int[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof Short) { message.put(key, ((Short) object).shortValue()); } else if (object instanceof short[]) { short[] bb = (short[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof Character) { message.put(key, ((Character) object).charValue()); } else if (object instanceof char[]) { char[] bb = (char[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof Byte) { message.put(key, ((Byte) object).byteValue()); } else if (object instanceof byte[]) { byte[] bb = (byte[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof Long) { message.put(key, ((Long) object).longValue()); } else if (object instanceof long[]) { long[] bb = (long[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof Float) { message.put(key, ((Float) object).floatValue()); } else if (object instanceof float[]) { float[] bb = (float[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof Double) { message.put(key, ((Double) object).doubleValue()); } else if (object instanceof double[]) { double[] bb = (double[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof Boolean) { message.put(key, ((Boolean) object).booleanValue()); } else if (object instanceof boolean[]) { boolean[] bb = (boolean[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } else if (object instanceof String) { message.put(key, (String) object); } else if (object instanceof Bundle) { DConnectMessage obj = new BasicDConnectMessage(); convertBundleToMap((Bundle) object, obj); message.put(key, obj); } else if (object instanceof Bundle[]) { Bundle[] bb = (Bundle[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { DConnectMessage obj = new BasicDConnectMessage(); convertBundleToMap(bb[i], obj); array.add(obj); } message.put(key, array); } else if (object instanceof Parcelable[]) { Parcelable[] bb = (Parcelable[]) object; List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { DConnectMessage obj = new BasicDConnectMessage(); if (bb[i] instanceof Bundle) { convertBundleToMap((Bundle) bb[i], obj); } array.add(obj); } message.put(key, array); } else if (object instanceof Object[]) { // プリミティブ型のラッパークラスの配列がObject[]として扱われる場合への対処 Object[] bb = (Object[]) object; if (isPrimitiveWrapperArray(bb)) { List array = new ArrayList(); for (int i = 0; i < bb.length; i++) { array.add(bb[i]); } message.put(key, array); } } else if (object instanceof List<?>) { List<?> bb = (List<?>) object; List array = new ArrayList(); for (int i = 0; i < bb.size(); i++) { Object v = bb.get(i); if (v instanceof Bundle) { DConnectMessage obj = new BasicDConnectMessage(); convertBundleToMap((Bundle) v, obj); array.add(obj); } else if (v instanceof Parcelable) { DConnectMessage obj = new BasicDConnectMessage(); convertBundleToMap((Bundle) v, obj); array.add(obj); } else { array.add(bb.get(i)); } } message.put(key, array); } } } private static void convertJSONToMap(final JSONObject root, final DConnectMessage message) throws JSONException { if (root == null || message == null) { return; } Iterator it = root.keys(); while (it.hasNext()) { String key = (String) it.next(); Object object = root.get(key); if (object instanceof JSONObject) { DConnectMessage m = new BasicDConnectMessage(); convertJSONToMap((JSONObject) object, m); message.put(key, m); } else if (object instanceof JSONArray) { JSONArray jsonArray = (JSONArray) object; int length = jsonArray.length(); List<Object> array = new ArrayList<>(length); for (int i = 0; i < length; i++) { Object obj = jsonArray.get(i); if (obj instanceof JSONObject) { DConnectMessage m = new BasicDConnectMessage(); convertJSONToMap((JSONObject) obj, m); array.add(m); } else { array.add(obj); } } message.put(key, array); } else { message.put(key, object); } } } /** * 指定したObject[]がプリミティブ型のラッパークラスの配列であるかどうかをチェックする. * <p> * なお、配列のすべての要素の型が同一でない場合、falseを返す. * 例えば、以下のような場合. * </p> * <pre> * {new Integer(0), new Double(0.0d)} // falseを返す * </pre> * @param array チェックするオブジェクト配列 * @return プリミティブ型のラッパークラスの配列である場合はtrue、そうでない場合はfalse */ private static boolean isPrimitiveWrapperArray(final Object[] array) { String classNameCache = null; for (int i = 0; i < array.length; i++) { Object obj = array[i]; if (obj != null) { if (isPrimitiveWrapper(obj)) { String className = obj.getClass().getName(); if (classNameCache != null) { if (!classNameCache.equals(className)) { return false; } } else { classNameCache = className; } } else { return false; } } } return true; } /** * 指定したObjectがプリミティブ型のラッパークラスであるかどうかをチェックする. * * @param obj チェックするオブジェクト * @return プリミティブ型のラッパークラスである場合はtrue、そうでない場合はfalse */ private static boolean isPrimitiveWrapper(final Object obj) { return obj instanceof Byte || obj instanceof Short || obj instanceof Integer || obj instanceof Long || obj instanceof Float || obj instanceof Double || obj instanceof Character || obj instanceof Boolean; } /** * List<Object>へキャストする. * @param list リスト * @return キャストされたリスト */ @SuppressWarnings("unchecked") private List<Object> castListObject(final List<?> list) { return (List<Object>) list; } }