com.uphyca.stetho_realm.Database.java Source code

Java tutorial

Introduction

Here is the source code for com.uphyca.stetho_realm.Database.java

Source

/*
 * Copyright (c) 2015-present, uPhyca, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.uphyca.stetho_realm;

import android.content.Context;
import android.database.sqlite.SQLiteException;

import com.facebook.stetho.common.Util;
import com.facebook.stetho.inspector.jsonrpc.JsonRpcPeer;
import com.facebook.stetho.inspector.jsonrpc.JsonRpcResult;
import com.facebook.stetho.inspector.protocol.ChromeDevtoolsDomain;
import com.facebook.stetho.inspector.protocol.ChromeDevtoolsMethod;
import com.facebook.stetho.json.ObjectMapper;
import com.facebook.stetho.json.annotation.JsonProperty;

import org.json.JSONObject;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import io.realm.internal.ColumnType;
import io.realm.internal.LinkView;
import io.realm.internal.Row;
import io.realm.internal.Table;

public class Database implements ChromeDevtoolsDomain {
    private static final int MAX_EXECUTE_RESULTS = 250;

    private final RealmPeerManager realmPeerManager;
    private final ObjectMapper objectMapper;
    private final boolean withMetaTables;

    @SuppressWarnings("unused")
    public Database(Context context, RealmFilesProvider filesProvider) {
        this(context, filesProvider, false);
    }

    public Database(Context context, RealmFilesProvider filesProvider, boolean withMetaTables) {
        this.realmPeerManager = new RealmPeerManager(context, filesProvider);
        this.objectMapper = new ObjectMapper();
        this.withMetaTables = withMetaTables;
    }

    @ChromeDevtoolsMethod
    @SuppressWarnings("unused")
    public void enable(JsonRpcPeer peer, JSONObject params) {
        realmPeerManager.addPeer(peer);
    }

    @ChromeDevtoolsMethod
    @SuppressWarnings("unused")
    public void disable(JsonRpcPeer peer, JSONObject params) {
        realmPeerManager.removePeer(peer);
    }

    @ChromeDevtoolsMethod
    @SuppressWarnings("unused")
    public JsonRpcResult getDatabaseTableNames(JsonRpcPeer peer, JSONObject params) {
        GetDatabaseTableNamesRequest request = objectMapper.convertValue(params,
                GetDatabaseTableNamesRequest.class);
        GetDatabaseTableNamesResponse response = new GetDatabaseTableNamesResponse();
        response.tableNames = realmPeerManager.getDatabaseTableNames(request.databaseId, withMetaTables);
        return response;
    }

    @ChromeDevtoolsMethod
    @SuppressWarnings("unused")
    public JsonRpcResult executeSQL(JsonRpcPeer peer, JSONObject params) {
        ExecuteSQLRequest request = this.objectMapper.convertValue(params, ExecuteSQLRequest.class);

        try {
            return realmPeerManager.executeSQL(request.databaseId, request.query,
                    new RealmPeerManager.ExecuteResultHandler<ExecuteSQLResponse>() {
                        public ExecuteSQLResponse handleRawQuery() throws SQLiteException {
                            ExecuteSQLResponse response = new ExecuteSQLResponse();
                            response.columnNames = Collections.singletonList("success");
                            response.values = Collections.<Object>singletonList("true");
                            return response;
                        }

                        public ExecuteSQLResponse handleSelect(Table table, boolean addRowIndex)
                                throws SQLiteException {
                            ExecuteSQLResponse response = new ExecuteSQLResponse();

                            final ArrayList<String> columnNames = new ArrayList<>();
                            if (addRowIndex) {
                                columnNames.add("<index>");
                            }
                            for (int i = 0; i < table.getColumnCount(); i++) {
                                columnNames.add(table.getColumnName(i));
                            }

                            response.columnNames = columnNames;
                            response.values = flattenRows(table, MAX_EXECUTE_RESULTS, addRowIndex);
                            return response;
                        }

                        public ExecuteSQLResponse handleInsert(long insertedId) throws SQLiteException {
                            ExecuteSQLResponse response = new ExecuteSQLResponse();
                            response.columnNames = Collections.singletonList("ID of last inserted row");
                            response.values = Collections.<Object>singletonList(insertedId);
                            return response;
                        }

                        public ExecuteSQLResponse handleUpdateDelete(int count) throws SQLiteException {
                            ExecuteSQLResponse response = new ExecuteSQLResponse();
                            response.columnNames = Collections.singletonList("Modified rows");
                            response.values = Collections.<Object>singletonList(count);
                            return response;
                        }
                    });
        } catch (SQLiteException e) {
            Error error = new Error();
            error.code = 0;
            error.message = e.getMessage();
            ExecuteSQLResponse response = new ExecuteSQLResponse();
            response.sqlError = error;
            return response;
        }
    }

    private List<Object> flattenRows(Table table, int limit, boolean addRowIndex) {
        Util.throwIfNot(limit >= 0);
        final List<Object> flatList = new ArrayList<>();
        long numColumns = table.getColumnCount();

        final RowFetcher rowFetcher = RowFetcher.getInstance();
        for (long row = 0; row < limit && row < table.size(); row++) {
            final RowWrapper rowData = RowWrapper.wrap(rowFetcher.getRow(table, row));
            if (addRowIndex) {
                flatList.add(rowData.getIndex());
            }
            for (int column = 0; column < numColumns; column++) {
                switch (rowData.getColumnType(column)) {
                case INTEGER:
                    flatList.add(rowData.getLong(column));
                    break;
                case BOOLEAN:
                    flatList.add(rowData.getBoolean(column));
                    break;
                case STRING:
                    flatList.add(rowData.getString(column));
                    break;
                case BINARY:
                    flatList.add(rowData.getBinaryByteArray(column));
                    break;
                case FLOAT:
                    final float aFloat = rowData.getFloat(column);
                    if (Float.isNaN(aFloat)) {
                        flatList.add("NaN");
                    } else if (aFloat == Float.POSITIVE_INFINITY) {
                        flatList.add("Infinity");
                    } else if (aFloat == Float.NEGATIVE_INFINITY) {
                        flatList.add("-Infinity");
                    } else {
                        flatList.add(aFloat);
                    }
                    break;
                case DOUBLE:
                    final double aDouble = rowData.getDouble(column);
                    if (Double.isNaN(aDouble)) {
                        flatList.add("NaN");
                    } else if (aDouble == Double.POSITIVE_INFINITY) {
                        flatList.add("Infinity");
                    } else if (aDouble == Double.NEGATIVE_INFINITY) {
                        flatList.add("-Infinity");
                    } else {
                        flatList.add(aDouble);
                    }
                    break;
                case DATE:
                    flatList.add(rowData.getDate(column));
                    break;
                case LINK:
                    flatList.add(rowData.getLink(column));
                    break;
                case LINK_LIST:
                    flatList.add(rowData.getLinkList(column));
                    break;
                default:
                    flatList.add("unknown column type: " + rowData.getColumnType(column));
                    break;
                }
            }
        }

        if (limit < table.size()) {
            for (int column = 0; column < numColumns; column++) {
                flatList.add("{truncated}");
            }
        }

        return flatList;
    }

    private static class GetDatabaseTableNamesRequest {
        @JsonProperty(required = true)
        public String databaseId;
    }

    private static class GetDatabaseTableNamesResponse implements JsonRpcResult {
        @JsonProperty(required = true)
        public List<String> tableNames;
    }

    private static class ExecuteSQLRequest {
        @JsonProperty(required = true)
        public String databaseId;

        @JsonProperty(required = true)
        public String query;
    }

    private static class ExecuteSQLResponse implements JsonRpcResult {
        @JsonProperty
        public List<String> columnNames;

        @JsonProperty
        public List<Object> values;

        @JsonProperty
        public Error sqlError;
    }

    public static class AddDatabaseEvent {
        @JsonProperty(required = true)
        public DatabaseObject database;
    }

    public static class DatabaseObject {
        @JsonProperty(required = true)
        public String id;

        @JsonProperty(required = true)
        public String domain;

        @JsonProperty(required = true)
        public String name;

        @JsonProperty(required = true)
        public String version;
    }

    public static class Error {
        @JsonProperty(required = true)
        public String message;

        @JsonProperty(required = true)
        public int code;
    }

    private abstract static class RowFetcher {
        private static RowFetcher sInstance;

        static {
            try {
                Table.class.getMethod("getCheckedRow", Long.TYPE);
                sInstance = new RowFetcherFor_0_81();
            } catch (NoSuchMethodException e) {
                sInstance = new RowFetcherFor_0_80();
            }
        }

        public static RowFetcher getInstance() {
            return sInstance;
        }

        RowFetcher() {
        }

        public abstract Row getRow(Table targetTable, long index);
    }

    private static final class RowFetcherFor_0_80 extends RowFetcher {
        private final Method getRowMethod;

        RowFetcherFor_0_80() {
            try {
                getRowMethod = Table.class.getMethod("getRow", Long.TYPE);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("getRow method not found in Table class.");
            }
        }

        @Override
        public Row getRow(Table targetTable, long index) {
            try {
                return (Row) getRowMethod.invoke(targetTable, index);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }
    }

    private static final class RowFetcherFor_0_81 extends RowFetcher {
        @Override
        public Row getRow(Table targetTable, long index) {
            return targetTable.getCheckedRow(index);
        }
    }

    private abstract static class RowWrapper {
        private static final boolean s0_81_OR_NEWER;

        static {
            s0_81_OR_NEWER = Row.class.isInterface();
        }

        public static RowWrapper wrap(Row row) {
            if (s0_81_OR_NEWER) {
                return new RowWrapper_0_81(row);
            } else {
                return new RowWrapper_0_80(row);
            }
        }

        protected RowWrapper() {
        }

        public abstract long getIndex();

        public abstract ColumnType getColumnType(long columnIndex);

        public abstract long getLong(long columnIndex);

        public abstract boolean getBoolean(long columnIndex);

        public abstract float getFloat(long columnIndex);

        public abstract double getDouble(long columnIndex);

        public abstract Date getDate(long columnIndex);

        public abstract String getString(long columnIndex);

        public abstract byte[] getBinaryByteArray(long columnIndex);

        public abstract long getLink(long columnIndex);

        public abstract LinkView getLinkList(long columnIndex);

    }

    private static final class RowWrapper_0_80 extends RowWrapper {

        /*
         * 0.81 ?????? Row ? interface ??? 0.80 ?? class ???
         * ????????????(interface ????
         * ? invokeinterface ???? invokevirtual)
         * ?????0_80 ???????? Object????????(????????)
         */
        private final Object row;

        private Method getIndexMethod;
        private Method getColumnTypeMethod;
        private Method getLongMethod;
        private Method getBooleanMethod;
        private Method getFloatMethod;
        private Method getDoubleMethod;
        private Method getDateMethod;
        private Method getStringMethod;
        private Method getBinaryByteArrayMethod;
        private Method getLinkMethod;
        private Method getLinkListMethod;

        RowWrapper_0_80(Row row) {
            this.row = row;

            try {
                final Class<? extends Row> aClass = row.getClass();
                getIndexMethod = aClass.getMethod("getIndex");
                getColumnTypeMethod = aClass.getMethod("getColumnType", Long.TYPE);
                getLongMethod = aClass.getMethod("getLong", Long.TYPE);
                getBooleanMethod = aClass.getMethod("getBoolean", Long.TYPE);
                getFloatMethod = aClass.getMethod("getFloat", Long.TYPE);
                getDoubleMethod = aClass.getMethod("getDouble", Long.TYPE);
                getDateMethod = aClass.getMethod("getDate", Long.TYPE);
                getStringMethod = aClass.getMethod("getString", Long.TYPE);
                getBinaryByteArrayMethod = aClass.getMethod("getBinaryByteArray", Long.TYPE);
                getLinkMethod = aClass.getMethod("getLink", Long.TYPE);
                getLinkListMethod = aClass.getMethod("getLinkList", Long.TYPE);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public long getIndex() {
            try {
                return (long) getIndexMethod.invoke(row);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public ColumnType getColumnType(long columnIndex) {
            try {
                return (ColumnType) getColumnTypeMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public long getLong(long columnIndex) {
            try {
                return (long) getLongMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public boolean getBoolean(long columnIndex) {
            try {
                return (boolean) getBooleanMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public float getFloat(long columnIndex) {
            try {
                return (float) getFloatMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public double getDouble(long columnIndex) {
            try {
                return (double) getDoubleMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public Date getDate(long columnIndex) {
            try {
                return (Date) getDateMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public String getString(long columnIndex) {
            try {
                return (String) getStringMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public byte[] getBinaryByteArray(long columnIndex) {
            try {
                return (byte[]) getBinaryByteArrayMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public long getLink(long columnIndex) {
            try {
                return (long) getLinkMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }

        @Override
        public LinkView getLinkList(long columnIndex) {
            try {
                return (LinkView) getLinkListMethod.invoke(row, columnIndex);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e.getTargetException());
            }
        }
    }

    private static final class RowWrapper_0_81 extends RowWrapper {
        private final Row row;

        RowWrapper_0_81(Row row) {
            this.row = row;
        }

        @Override
        public long getIndex() {
            return row.getIndex();
        }

        @Override
        public ColumnType getColumnType(long columnIndex) {
            return row.getColumnType(columnIndex);
        }

        @Override
        public long getLong(long columnIndex) {
            return row.getLong(columnIndex);
        }

        @Override
        public boolean getBoolean(long columnIndex) {
            return row.getBoolean(columnIndex);
        }

        @Override
        public float getFloat(long columnIndex) {
            return row.getFloat(columnIndex);
        }

        @Override
        public double getDouble(long columnIndex) {
            return row.getDouble(columnIndex);
        }

        @Override
        public Date getDate(long columnIndex) {
            return row.getDate(columnIndex);
        }

        @Override
        public String getString(long columnIndex) {
            return row.getString(columnIndex);
        }

        @Override
        public byte[] getBinaryByteArray(long columnIndex) {
            return row.getBinaryByteArray(columnIndex);
        }

        @Override
        public long getLink(long columnIndex) {
            return row.getLink(columnIndex);
        }

        @Override
        public LinkView getLinkList(long columnIndex) {
            return row.getLinkList(columnIndex);
        }
    }

}