org.rapidandroid.ModelBootstrap.java Source code

Java tutorial

Introduction

Here is the source code for org.rapidandroid.ModelBootstrap.java

Source

/*
 * Copyright (C) 2009 Dimagi Inc., UNICEF
 *
 * 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 org.rapidandroid;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.rapidandroid.content.translation.MessageTranslator;
import org.rapidandroid.content.translation.ModelTranslator;
import org.rapidandroid.data.RapidSmsDBConstants;
import org.rapidandroid.data.RapidSmsDBConstants.FieldType;
import org.rapidsms.java.core.model.Field;
import org.rapidsms.java.core.model.Form;
import org.rapidsms.java.core.model.SimpleFieldType;
import org.rapidsms.java.core.parser.service.ParsingService.ParserType;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;

/**
 * @author Daniel Myung dmyung@dimagi.com
 * @created Jan 27, 2009 Summary:
 */
@SuppressLint("UseSparseArrays")
public class ModelBootstrap {
    private static SystemHealthTracking healthTracker = new SystemHealthTracking(ModelBootstrap.class);

    private static Context mContext;

    private static HashMap<Integer, Form> formIdCache = new HashMap<Integer, Form>();
    private static HashMap<Integer, Vector<Field>> fieldToFormHash = new HashMap<Integer, Vector<Field>>();
    private static HashMap<Integer, SimpleFieldType> fieldTypeHash = new HashMap<Integer, SimpleFieldType>();

    public static void InitApplicationDatabase(Context context) {
        mContext = context;
        // SAGES/pokuam1: force check existence of tables and forms
        if (true) {
            //if (isFieldTypeTableEmpty()) {
            healthTracker.logInfo("Bootstrapping fieldtypes, fields, and forms.");
            applicationInitialFormFieldTypesBootstrap();
        }
        MessageTranslator.updateMonitorHash(context);
    }

    //   private static boolean isFieldTypeTableEmpty() {
    //      Uri fieldtypeUri = RapidSmsDBConstants.FieldType.CONTENT_URI;
    //      Cursor fieldtypecheck = mContext.getContentResolver().query(fieldtypeUri, null, null, null, null);
    //      if (fieldtypecheck.getCount() == 0) {
    //         fieldtypecheck.close();
    //         return true;
    //      } else {
    //         // not empty!
    //         fieldtypecheck.close();
    //         return false;
    //      }
    //   }

    private static String loadAssetFile(String filename) {
        try {
            InputStream is = mContext.getAssets().open(filename);

            int size = is.available();

            // Read the entire asset into a local byte buffer.
            byte[] buffer = new byte[size];
            is.read(buffer);
            is.close();

            // Convert the buffer into a Java string.
            String text = new String(buffer);

            return text;

        } catch (IOException e) {
            // Should never happen!
            throw new RuntimeException(e);
        }
    }

    //TODO: make less code repeat
    private static String loadSdCardFile(String filename) {
        // InputStream is = mContext.getAssets().open(filename);
        File file = new File(filename);
        if (!file.exists()) {
            return null;
        }

        InputStream is = null;
        try {
            is = new BufferedInputStream(new FileInputStream(file));
            int size = is.available();

            // Read the entire asset into a local byte buffer.
            byte[] buffer = new byte[size];
            is.read(buffer);
            is.close();

            // Convert the buffer into a Java string.
            String text = new String(buffer);

            return text;

        } catch (IOException e) {
            // Should never happen!
            throw new RuntimeException(e);
        }
    }

    /**
     * Initial app startup, ONLY SHOULD BE RUN ONCE!!! called when the existence
     * of some data in the fieldtypes table is missing.
     * 
     * external-custom-fieldtypes: place them here: /sdcard/rapidandroid/externalcustomfieldtypes.json
     */
    private static void applicationInitialFormFieldTypesBootstrap() {
        healthTracker.logInfo("Loading field types and forms from assets.");
        loadFieldTypesFromAssets();
        insertFieldTypesIntoDBIfNecessary();

        loadInitialFormsFromAssets();
        checkIfFormTablesExistCreateIfNecessary();
    }

    private static void insertFieldTypesIntoDBIfNecessary() {

        Iterator<Map.Entry<Integer, SimpleFieldType>> it = fieldTypeHash.entrySet().iterator();

        // for(int i = 0; i < forms.size(); i++) {
        while (it.hasNext()) {
            Map.Entry<Integer, SimpleFieldType> pairs = it.next();
            SimpleFieldType thetype = pairs.getValue();
            // make the URI and insert for the Fieldtype
            Uri fieldtypeUri = Uri.parse(RapidSmsDBConstants.FieldType.CONTENT_URI_STRING + thetype.getId());
            Cursor typeCursor = mContext.getContentResolver().query(fieldtypeUri, null, null, null, null);

            if (typeCursor.getCount() == 0) {
                ContentValues typecv = new ContentValues();

                typecv.put(BaseColumns._ID, thetype.getId());
                typecv.put(RapidSmsDBConstants.FieldType.DATATYPE, thetype.getDataType());
                typecv.put(RapidSmsDBConstants.FieldType.NAME, thetype.getReadableName());
                typecv.put(RapidSmsDBConstants.FieldType.REGEX, thetype.getRegex());

                Log.d("dimagi", "InsertFieldType: " + thetype.getId());
                Log.d("dimagi", "InsertFieldType: " + thetype.getDataType());
                Log.d("dimagi", "InsertFieldType: " + thetype.getReadableName());
                Log.d("dimagi", "InsertFieldType: " + thetype.getRegex());

                Uri insertedTypeUri = mContext.getContentResolver()
                        .insert(RapidSmsDBConstants.FieldType.CONTENT_URI, typecv);
                Log.d("dimagi", "********** Inserted SimpleFieldType into db: " + insertedTypeUri);

            } else if (typeCursor.getCount() == 1 && typeCursor.moveToFirst()) {
                // SAGES: update the fieldtype in the database -- the Name and/or Regex has changed
                int nameColIndx = typeCursor.getColumnIndex(FieldType.NAME);
                int regexColIndx = typeCursor.getColumnIndex(FieldType.REGEX);

                boolean isUpdatedFieldType = (!typeCursor.getString(nameColIndx).equals(thetype.getReadableName()));
                boolean isUpdatedRegex = (!typeCursor.getString(regexColIndx).equals(thetype.getRegex()));

                if (isUpdatedFieldType || isUpdatedRegex) {
                    ContentValues typecv = new ContentValues();

                    //typecv.put(BaseColumns._ID, thetype.getId());
                    typecv.put(RapidSmsDBConstants.FieldType.DATATYPE, thetype.getDataType());
                    typecv.put(RapidSmsDBConstants.FieldType.NAME, thetype.getReadableName());
                    typecv.put(RapidSmsDBConstants.FieldType.REGEX, thetype.getRegex());

                    Log.d("sages", "UpdateFieldType: " + thetype.getId());
                    Log.d("sages", "UpdateFieldType: " + thetype.getDataType());
                    Log.d("sages", "UpdateFieldType: " + thetype.getReadableName());
                    Log.d("sages", "UpdateFieldType: " + thetype.getRegex());

                    String whereClause = BaseColumns._ID + "= ?";
                    String[] whereClauseArgs = { String.valueOf(thetype.getId()) };
                    int numUpdatedType = mContext.getContentResolver().update(
                            RapidSmsDBConstants.FieldType.CONTENT_URI, typecv, whereClause, whereClauseArgs);
                    Log.d("sages", "********** Updated SimpleFieldType into db: " + numUpdatedType);
                }
            }
            typeCursor.close();
        }
    }

    private static void loadFieldTypesFromAssets() {
        String types = loadAssetFile("definitions/fieldtypes.json");
        String customtypes = loadAssetFile("definitions/customfieldtypes.json");
        // SAGES: loading the custom fieldtype files
        String sdcard = RapidAndroidApplication.PATH_SDCARD;
        String externalcustomtypes = loadSdCardFile(sdcard + "/rapidandroid/externalcustomfieldtypes.json");

        try {
            JSONArray typesarray = new JSONArray(types);

            int arrlength = typesarray.length();
            for (int i = 0; i < arrlength; i++) {
                try {
                    JSONObject obj = typesarray.getJSONObject(i);
                    Log.d("dimagi", "type loop: " + i + " model: " + obj.getString("model"));
                    if (!obj.getString("model").equals("rapidandroid.fieldtype")) {
                        Log.d("dimagi", "###" + obj.getString("model") + "###");
                        throw new IllegalArgumentException("Error in parsing fieldtypes.json");
                    }

                    int pk = obj.getInt("pk");
                    JSONObject jsonfields = obj.getJSONObject("fields");
                    Log.d("dimagi", "#### Regex from file: " + jsonfields.getString("name") + " ["
                            + jsonfields.getString("regex") + "]");
                    SimpleFieldType newtype = new SimpleFieldType(pk, jsonfields.getString("datatype"),
                            jsonfields.getString("regex"), jsonfields.getString("name"));
                    fieldTypeHash.put(Integer.valueOf(pk), newtype);
                } catch (JSONException e) {
                }
            }
        } catch (JSONException e) {
        }
        try {
            // SAGES/pokuam1: load custom fieldtypes from sdcard but always append
            JSONArray customtypesarray = new JSONArray(customtypes);

            int arrlength = customtypesarray.length();
            for (int i = 0; i < arrlength; i++) {
                try {
                    JSONObject obj = customtypesarray.getJSONObject(i);
                    Log.d("sages", "type loop: " + i + " model: " + obj.getString("model"));
                    if (!obj.getString("model").equals("rapidandroid.fieldtype")) {
                        Log.d("sages", "###" + obj.getString("model") + "###");
                        throw new IllegalArgumentException("Error in parsing fieldtypes.json");
                    }

                    int pk = obj.getInt("pk");
                    JSONObject jsonfields = obj.getJSONObject("fields");
                    Log.d("sages", "#### Regex from file: " + jsonfields.getString("name") + " ["
                            + jsonfields.getString("regex") + "]");
                    SimpleFieldType newtype = new SimpleFieldType(pk, jsonfields.getString("datatype"),
                            jsonfields.getString("regex"), jsonfields.getString("name"));
                    fieldTypeHash.put(Integer.valueOf(pk), newtype);
                } catch (JSONException e) {
                }
            }
        } catch (JSONException e) {
        }
        // SAGES/pokuam1: load EXTERNAL (on the sdcard) custom fieldtypes from sdcard but always append
        if (externalcustomtypes != null) {
            try {
                JSONArray externalcustomtypesarray = new JSONArray(externalcustomtypes);

                int arrlength = externalcustomtypesarray.length();
                for (int i = 0; i < arrlength; i++) {
                    try {
                        JSONObject obj = externalcustomtypesarray.getJSONObject(i);
                        Log.d("sages", "type loop: " + i + " model: " + obj.getString("model"));
                        if (!obj.getString("model").equals("rapidandroid.fieldtype")) {
                            Log.d("sages", "###" + obj.getString("model") + "###");
                            throw new IllegalArgumentException("Error in parsing fieldtypes.json");
                        }

                        int pk = obj.getInt("pk");
                        JSONObject jsonfields = obj.getJSONObject("fields");
                        Log.d("sages", "#### Regex from file: " + jsonfields.getString("name") + " ["
                                + jsonfields.getString("regex") + "]");
                        SimpleFieldType newtype = new SimpleFieldType(pk, jsonfields.getString("datatype"),
                                jsonfields.getString("regex"), jsonfields.getString("name"));
                        fieldTypeHash.put(Integer.valueOf(pk), newtype);
                    } catch (JSONException e) {
                    }
                }
            } catch (JSONException e) {
            }
        }
    }

    private static void loadInitialFormsFromAssets() {
        parseFieldsFromAssets();
        parseFormsFromAssets();

        parseFieldsFromLoadableAssets();
        parseFormsFromLoadableAssets();
    }

    /**
     * Loads externally defined field types from /sdcard/rapidandroid/loadablefields.json file
     * @return void
     */
    // TODO pokuam1 - refactor to reuse original method, see parseFieldsFromAssets()
    private static void parseFieldsFromLoadableAssets() {
        String sdcard = RapidAndroidApplication.PATH_SDCARD;
        String sdcardFields = loadSdCardFile(sdcard + "/rapidandroid/loadablefields.json");

        if (sdcardFields != null) {
            try {
                JSONArray fieldsarray = new JSONArray(sdcardFields);
                int arrlength = fieldsarray.length();
                for (int i = 0; i < arrlength; i++) {
                    try {
                        JSONObject obj = fieldsarray.getJSONObject(i);

                        if (!obj.getString("model").equals("rapidandroid.field")) {

                        }

                        int pk = obj.getInt("pk");

                        JSONObject jsonfields = obj.getJSONObject("fields");
                        int form_id = jsonfields.getInt("form");
                        Field newfield = new Field(pk, jsonfields.getInt("sequence"), jsonfields.getString("name"),
                                jsonfields.getString("prompt"),
                                fieldTypeHash.get(Integer.valueOf(jsonfields.getInt("fieldtype"))));

                        Integer formInt = Integer.valueOf(form_id);
                        if (!fieldToFormHash.containsKey(formInt)) {
                            fieldToFormHash.put(formInt, new Vector<Field>());
                            Log.d("sages", "### adding a key again?!" + formInt);
                        }
                        fieldToFormHash.get(formInt).add(newfield);
                        Log.d("sages", "#### Parsed field: " + newfield.getFieldId() + " [" + newfield.getName()
                                + "] newlength: " + fieldToFormHash.get(formInt).size());

                    } catch (JSONException e) {
                        // TODO Auto-generated catch block
                        Log.d("sages", e.getMessage());

                    }
                }
            } catch (JSONException e) {
            }
        }
    }

    private static void parseFieldsFromAssets() {
        String fields = loadAssetFile("definitions/fields.json");
        try {
            JSONArray fieldsarray = new JSONArray(fields);
            int arrlength = fieldsarray.length();
            for (int i = 0; i < arrlength; i++) {
                try {
                    JSONObject obj = fieldsarray.getJSONObject(i);

                    if (!obj.getString("model").equals("rapidandroid.field")) {

                    }

                    int pk = obj.getInt("pk");

                    JSONObject jsonfields = obj.getJSONObject("fields");
                    int form_id = jsonfields.getInt("form");
                    Field newfield = new Field(pk, jsonfields.getInt("sequence"), jsonfields.getString("name"),
                            jsonfields.getString("prompt"),
                            fieldTypeHash.get(Integer.valueOf(jsonfields.getInt("fieldtype"))));

                    Integer formInt = Integer.valueOf(form_id);
                    if (!fieldToFormHash.containsKey(formInt)) {
                        fieldToFormHash.put(formInt, new Vector<Field>());
                        Log.d("dimagi", "### adding a key again?!" + formInt);
                    }
                    fieldToFormHash.get(formInt).add(newfield);
                    Log.d("dimagi", "#### Parsed field: " + newfield.getFieldId() + " [" + newfield.getName()
                            + "] newlength: " + fieldToFormHash.get(formInt).size());

                } catch (JSONException e) {
                    // TODO Auto-generated catch block
                    Log.d("dimagi", e.getMessage());

                }
            }
        } catch (JSONException e) {
        }
    }

    private static void parseFormsFromAssets() {
        String forms = loadAssetFile("definitions/forms.json");

        try {
            JSONArray formarray = new JSONArray(forms);
            int arrlength = formarray.length();
            for (int i = 0; i < arrlength; i++) {
                try {
                    JSONObject obj = formarray.getJSONObject(i);

                    if (!obj.getString("model").equals("rapidandroid.form")) {
                    }

                    int pk = obj.getInt("pk");
                    Integer pkInt = pk;
                    JSONObject jsonfields = obj.getJSONObject("fields");

                    Field[] fieldarr = new Field[fieldToFormHash.get(pkInt).size()];
                    for (int q = 0; q < fieldarr.length; q++) {
                        fieldarr[q] = fieldToFormHash.get(pkInt).get(q);
                    }
                    Form newform = new Form(pk, jsonfields.getString("formname"), jsonfields.getString("prefix"),
                            jsonfields.getString("description"), fieldarr,
                            ParserType.getTypeFromConfig(jsonfields.getString("parsemethod")));
                    formIdCache.put(pkInt, newform);

                } catch (JSONException e) {
                    // TODO Auto-generated catch block
                    Log.d("dimagi", e.getMessage());
                }
            }
        } catch (JSONException e) {
        }
    }

    /**
     * Loads externally defined forms from /sdcard/rapidandroid/loadableforms.json file
     * @return void
     * 
     */
    // TODO pokuam1 - refactor to reuse original method, see parseFormsFromAssets()
    private static void parseFormsFromLoadableAssets() {
        // String forms = loadAssetFile("definitions/forms.json");
        String sdcard = RapidAndroidApplication.PATH_SDCARD;
        String sdcardForms = loadSdCardFile(sdcard + "/rapidandroid/loadableforms.json");

        if (sdcardForms != null) {
            try {
                JSONArray formarray = new JSONArray(sdcardForms);
                int arrlength = formarray.length();
                Integer curPkInt = null;
                for (int i = 0; i < arrlength; i++) {
                    try {
                        JSONObject obj = formarray.getJSONObject(i);

                        if (!obj.getString("model").equals("rapidandroid.form")) {
                        }

                        int pk = obj.getInt("pk");
                        Integer pkInt = pk;
                        curPkInt = pkInt;
                        JSONObject jsonfields = obj.getJSONObject("fields");

                        Field[] fieldarr = new Field[fieldToFormHash.get(pkInt).size()];
                        for (int q = 0; q < fieldarr.length; q++) {
                            fieldarr[q] = fieldToFormHash.get(pkInt).get(q);
                        }
                        Form newform = new Form(pk, jsonfields.getString("formname"),
                                jsonfields.getString("prefix"), jsonfields.getString("description"), fieldarr,
                                ParserType.getTypeFromConfig(jsonfields.getString("parsemethod")));
                        formIdCache.put(pkInt, newform);

                    } catch (JSONException e) {
                        // TODO Auto-generated catch block
                        Log.d("sages", e.getMessage());
                    } catch (NullPointerException npe) {
                        Log.e("sages", "pkInt: " + curPkInt + "may have caused exception.");
                        throw npe;
                    }
                }
            } catch (JSONException e) {
            }
        }
    }

    private static void checkIfFormTablesExistCreateIfNecessary() {
        // so, todo:
        // check if tables exist
        // else

        Iterator<Map.Entry<Integer, Form>> it = formIdCache.entrySet().iterator();

        // for(int i = 0; i < forms.size(); i++) {
        while (it.hasNext()) {
            Map.Entry<Integer, Form> pairs = it.next();
            Form f = pairs.getValue();

            Log.d("dimagi", "**** inserting form " + f.getFormName());

            // insert the form first
            Uri formUri = Uri.parse(RapidSmsDBConstants.Form.CONTENT_URI_STRING + f.getFormId());
            Cursor crform = mContext.getContentResolver().query(formUri, null, null, null, null);
            //         boolean newFormInserted = false;
            if (crform.getCount() == 0) {
                ModelTranslator.addFormToDatabase(f);
            }
            crform.close();

        }
    }
}