/* * Copyright (c) 2016 LibreTasks - https://github.com/biotinker/LibreTasks * * This file is free software: you may copy, redistribute and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * This file incorporates work covered by the following copyright and * permission notice: /******************************************************************************* * Copyright 2009, 2010 Omnidroid - http://code.google.com/p/omnidroid * * 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 libretasks.app.model; import static libretasks.app.model.CursorHelper.getBooleanFromCursor; import static libretasks.app.model.CursorHelper.getLongFromCursor; import static libretasks.app.model.CursorHelper.getStringFromCursor; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import libretasks.app.R; import libretasks.app.controller.datatypes.DataType; import libretasks.app.controller.datatypes.FactoryDataType; import libretasks.app.model.db.DataFilterDbAdapter; import libretasks.app.model.db.DataTypeDbAdapter; import libretasks.app.model.db.DbHelper; import libretasks.app.model.db.LogActionDbAdapter; import libretasks.app.model.db.LogDbAdapter; import libretasks.app.model.db.LogGeneralDbAdapter; import libretasks.app.model.db.LogEventDbAdapter; import libretasks.app.model.db.RegisteredActionDbAdapter; import libretasks.app.model.db.RegisteredActionParameterDbAdapter; import libretasks.app.model.db.RegisteredAppDbAdapter; import libretasks.app.model.db.RegisteredEventAttributeDbAdapter; import libretasks.app.model.db.RegisteredEventDbAdapter; import libretasks.app.model.db.RuleActionDbAdapter; import libretasks.app.model.db.RuleActionParameterDbAdapter; import libretasks.app.model.db.RuleDbAdapter; import libretasks.app.model.db.RuleFilterDbAdapter; import libretasks.app.view.simple.model.ModelAction; import libretasks.app.view.simple.model.ModelApplication; import libretasks.app.view.simple.model.ModelAttribute; import libretasks.app.view.simple.model.ModelEvent; import libretasks.app.view.simple.model.ModelFilter; import libretasks.app.view.simple.model.ModelLog; import libretasks.app.view.simple.model.ModelParameter; import libretasks.app.view.simple.model.ModelRuleAction; import libretasks.app.view.simple.model.ModelRuleFilter; import libretasks.app.view.simple.model.Rule; import libretasks.app.view.simple.model.RuleNode; /** * This class serves as an access layer of the database for Omnidroid's UI data model * representation. * * TODO(ehotou): Consider throwing exceptions (or Logging) when db operations fail. */ public class UIDbHelper { private static final String TAG = UIDbHelper.class.getSimpleName(); // Database management private DbHelper dbHelper; private SQLiteDatabase database; // Database Adapters private DataTypeDbAdapter dataTypeDbAdapter; private DataFilterDbAdapter dataFilterDbAdapter; private RegisteredAppDbAdapter registeredAppDbAdapter; private RegisteredEventDbAdapter registeredEventDbAdapter; private RegisteredActionDbAdapter registeredActionDbAdapter; private RegisteredEventAttributeDbAdapter registeredEventAttributeDbAdapter; private RegisteredActionParameterDbAdapter registeredActionParameterDbAdapter; private RuleFilterDbAdapter ruleFilterDbAdapter; private RuleActionDbAdapter ruleActionDbAdapter; private RuleActionParameterDbAdapter ruleActionParameterDbAdapter; private RuleDbAdapter ruleDbAdapter; private LogEventDbAdapter logEventDbAdapter; private LogActionDbAdapter logActionDbAdapter; private LogGeneralDbAdapter logGeneralDbAdapter; // Hash maps for storing cached data for quick lookup private Map<Long, String> dataTypeNames; private Map<Long, String> dataTypeClassNames; private Map<Long, String> dataFilterNames; private Map<Long, ModelApplication> applications; private Map<Long, ModelEvent> events; private Map<Long, ModelAction> actions; private Map<Long, ModelAttribute> globalAttributes; private Map<Long, ModelAttribute> specificAttributes; private Map<Long, ModelParameter> parameters; // Get user configured settings private SharedPreferences settings; // This flag marks whether this helper is closed private boolean isClosed = false; /** * Reset the db, drop all necessary table, and recreate them and repopulate them again */ public void resetDB() { dbHelper.cleanup(database); } public UIDbHelper(Context context) { dbHelper = new DbHelper(context); database = dbHelper.getWritableDatabase(); // Initialize db adapters dataTypeDbAdapter = new DataTypeDbAdapter(database); dataFilterDbAdapter = new DataFilterDbAdapter(database); registeredAppDbAdapter = new RegisteredAppDbAdapter(database); registeredEventDbAdapter = new RegisteredEventDbAdapter(database); registeredActionDbAdapter = new RegisteredActionDbAdapter(database); registeredEventAttributeDbAdapter = new RegisteredEventAttributeDbAdapter(database); registeredActionParameterDbAdapter = new RegisteredActionParameterDbAdapter(database); ruleFilterDbAdapter = new RuleFilterDbAdapter(database); ruleActionDbAdapter = new RuleActionDbAdapter(database); ruleActionParameterDbAdapter = new RuleActionParameterDbAdapter(database); ruleDbAdapter = new RuleDbAdapter(database); logEventDbAdapter = new LogEventDbAdapter(database); logActionDbAdapter = new LogActionDbAdapter(database); logGeneralDbAdapter = new LogGeneralDbAdapter(database); // Initialize db cache dataTypeNames = new HashMap<Long, String>(); dataTypeClassNames = new HashMap<Long, String>(); dataFilterNames = new HashMap<Long, String>(); applications = new HashMap<Long, ModelApplication>(); events = new LinkedHashMap<Long, ModelEvent>(); actions = new HashMap<Long, ModelAction>(); globalAttributes = new HashMap<Long, ModelAttribute>(); specificAttributes = new HashMap<Long, ModelAttribute>(); parameters = new HashMap<Long, ModelParameter>(); // Load db cache maps loadDbCache(); } /** * Close the UIDbHelper. UI needs to call this method when it is done with the database * connection. UIDbHelper is not usable after calling this method. */ public void close() { isClosed = true; dbHelper.close(); database.close(); } /** * Load cached data into hash maps */ private void loadDbCache() { // TODO(ehotou) Consider lazy initialization for some of these stuff. // Load Preferences settings = dbHelper.getSharedPreferences(); // Load DataTypes Cursor cursor = dataTypeDbAdapter.fetchAll(); while (cursor.moveToNext()) { dataTypeNames.put(getLongFromCursor(cursor, DataTypeDbAdapter.KEY_DATATYPEID), getStringFromCursor(cursor, DataTypeDbAdapter.KEY_DATATYPENAME)); dataTypeClassNames.put(getLongFromCursor(cursor, DataTypeDbAdapter.KEY_DATATYPEID), getStringFromCursor(cursor, DataTypeDbAdapter.KEY_DATATYPECLASSNAME)); } cursor.close(); // Load Filters cursor = dataFilterDbAdapter.fetchAll(); while (cursor.moveToNext()) { dataFilterNames.put(getLongFromCursor(cursor, DataFilterDbAdapter.KEY_DATAFILTERID), getStringFromCursor(cursor, DataFilterDbAdapter.KEY_DATAFILTERDISPLAYNAME)); } cursor.close(); // Load Applications cursor = registeredAppDbAdapter.fetchAll(); while (cursor.moveToNext()) { ModelApplication application = new ModelApplication(getStringFromCursor(cursor, RegisteredAppDbAdapter.KEY_APPNAME), "", // TODO(ehotou) After implementing desc for app, // load it here R.drawable.icon_application_unknown, getLongFromCursor(cursor, RegisteredAppDbAdapter.KEY_APPID), getBooleanFromCursor(cursor, RegisteredAppDbAdapter.KEY_LOGIN), getStringFromCursor(cursor, RegisteredAppDbAdapter.KEY_USERNAME), getStringFromCursor(cursor, RegisteredAppDbAdapter.KEY_PASSWORD)); applications.put(application.getDatabaseId(), application); } cursor.close(); // Load Events in alphabetical order cursor = registeredEventDbAdapter.fetchAllOrdered(); while (cursor.moveToNext()) { ModelEvent event = new ModelEvent(getLongFromCursor(cursor, RegisteredEventDbAdapter.KEY_EVENTID), getStringFromCursor(cursor, RegisteredEventDbAdapter.KEY_EVENTNAME), "", // TODO(ehotou) After implementing // description for event, load it here R.drawable.icon_event_unknown); events.put(event.getDatabaseId(), event); } cursor.close(); // Load Event Attributes cursor = registeredEventAttributeDbAdapter.fetchAllGlobalAttributes(); while (cursor.moveToNext()) { ModelAttribute attribute = new ModelAttribute(getLongFromCursor(cursor, RegisteredEventAttributeDbAdapter.KEY_EVENTATTRIBUTEID), getLongFromCursor(cursor, RegisteredEventAttributeDbAdapter.KEY_EVENTID), getLongFromCursor(cursor, RegisteredEventAttributeDbAdapter.KEY_DATATYPEID), getStringFromCursor(cursor, RegisteredEventAttributeDbAdapter.KEY_EVENTATTRIBUTENAME), "", // TODO(ehotou) After // implementing desc for attribute, load it here R.drawable.icon_attribute_unknown); globalAttributes.put(attribute.getDatabaseId(), attribute); } cursor.close(); cursor = registeredEventAttributeDbAdapter.fetchAllSpecificAttibutes(); while (cursor.moveToNext()) { ModelAttribute attribute = new ModelAttribute(getLongFromCursor(cursor, RegisteredEventAttributeDbAdapter.KEY_EVENTATTRIBUTEID), getLongFromCursor(cursor, RegisteredEventAttributeDbAdapter.KEY_EVENTID), getLongFromCursor(cursor, RegisteredEventAttributeDbAdapter.KEY_DATATYPEID), getStringFromCursor(cursor, RegisteredEventAttributeDbAdapter.KEY_EVENTATTRIBUTENAME), "", // TODO(ehotou) After // implementing desc for attribute, load it here R.drawable.icon_attribute_unknown); specificAttributes.put(attribute.getDatabaseId(), attribute); } cursor.close(); // Load Action Parameters cursor = registeredActionParameterDbAdapter.fetchAll(); while (cursor.moveToNext()) { ModelParameter parameter = new ModelParameter(getLongFromCursor(cursor, RegisteredActionParameterDbAdapter.KEY_ACTIONPARAMETERID), getLongFromCursor(cursor, RegisteredActionParameterDbAdapter.KEY_ACTIONID), getLongFromCursor(cursor, RegisteredActionParameterDbAdapter.KEY_DATATYPEID), getStringFromCursor(cursor, RegisteredActionParameterDbAdapter.KEY_ACTIONPARAMETERNAME), "" // TODO(ehotou) After // implementing desc for parameter, load it here ); parameters.put(parameter.getDatabaseId(), parameter); } cursor.close(); // Load Actions cursor = registeredActionDbAdapter.fetchAll(); while (cursor.moveToNext()) { ModelApplication application = applications.get(getLongFromCursor(cursor, RegisteredActionDbAdapter.KEY_APPID)); long actionID = getLongFromCursor(cursor, RegisteredActionDbAdapter.KEY_ACTIONID); // Load parameters for each action ArrayList<ModelParameter> parameterList = new ArrayList<ModelParameter>(); for (ModelParameter parameter : parameters.values()) { if (parameter.getForeignKeyActionId() == actionID) { parameterList.add(parameter); } } ModelAction action = new ModelAction(getStringFromCursor(cursor, RegisteredActionDbAdapter.KEY_ACTIONNAME), "", // TODO(ehotou) After implementing desc for // action, load it here R.drawable.icon_action_unknown, actionID, application, parameterList); actions.put(actionID, action); } cursor.close(); } /** * Create a dataType object with a type matches dataTypeID, containing specific data. * * @param dataTypeID * is id of the dataType to be created * * @param data * is the content of the data within the dataType object * * @return a dataType object */ private DataType getDataType(long dataTypeID, String data) { String dataTypeClassName = dataTypeClassNames.get(dataTypeID); return FactoryDataType.createObject(dataTypeClassName, data); } /** * @return all applications as an ArrayList * @throws IllegalStateException * if this helper is closed */ public ArrayList<ModelApplication> getAllApplications() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } ArrayList<ModelApplication> applicationList = new ArrayList<ModelApplication>(applications .size()); applicationList.addAll(applications.values()); return applicationList; } /** * Updates the application username and password in the database. * * @param modelApp * the application object to be updated * @return true if success, or false otherwise. * @throws IllegalStateException * if this helper is closed */ public boolean updateApplicationLoginInfo(ModelApplication modelApp) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } return registeredAppDbAdapter.update(modelApp.getDatabaseId(), null, null, null, null, modelApp .getUsername(), modelApp.getPassword()); } /** * Clears the application username and password in the database. Sets them as empty strings. * * @param modelApp * the application object to be updated * @return true if success, or false otherwise. * @throws IllegalStateException * if this helper is closed */ public boolean clearApplicationLoginInfo(ModelApplication modelApp) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } return registeredAppDbAdapter.update(modelApp.getDatabaseId(), null, null, null, null, "", ""); } /** * @param appDatabaseId * the application database Id * @return application * @throws IllegalStateException * if this helper is closed */ public ModelApplication getApplication(long appDatabaseId) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = registeredAppDbAdapter.fetch(appDatabaseId); String typeName = getStringFromCursor(cursor, RegisteredAppDbAdapter.KEY_APPNAME); long databaseID = getLongFromCursor(cursor, RegisteredAppDbAdapter.KEY_APPID); boolean loginEnabled = getBooleanFromCursor(cursor, RegisteredAppDbAdapter.KEY_LOGIN); String username = getStringFromCursor(cursor, RegisteredAppDbAdapter.KEY_USERNAME); String password = getStringFromCursor(cursor, RegisteredAppDbAdapter.KEY_PASSWORD); cursor.close(); return new ModelApplication(typeName, "", R.drawable.icon_application_unknown, databaseID, loginEnabled, username, password); } /** * @return all events as an ArrayList * @throws IllegalStateException * if this helper is closed */ public ArrayList<ModelEvent> getAllEvents() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } ArrayList<ModelEvent> eventList = new ArrayList<ModelEvent>(events.size()); eventList.addAll(events.values()); return eventList; } /** * @param application * is a ModelApplication object * * @return all actions associated with one application as an ArrayList * @throws IllegalStateException * if this helper is closed */ public ArrayList<ModelAction> getActionsForApplication(ModelApplication application) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } ArrayList<ModelAction> actionList = new ArrayList<ModelAction>(actions.size()); for (ModelAction action : actions.values()) { if (action.getApplication().getDatabaseId() == application.getDatabaseId()) { actionList.add(action); } } return actionList; } /** * @param event * is a ModelEvent object * * @return all attributes associated with one event as an ArrayList * @throws IllegalStateException * if this helper is closed */ public List<ModelAttribute> getAttributesForEvent(ModelEvent event) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } List<ModelAttribute> attributesList = new ArrayList<ModelAttribute>(specificAttributes.size()); attributesList.addAll(globalAttributes.values()); for (ModelAttribute attribute : specificAttributes.values()) { if (attribute.getForeignKeyEventId() == event.getDatabaseId()) { attributesList.add(attribute); } } return attributesList; } /** * Get the application associated with the action * * @param action * the action * @return the application associated with the action */ public ModelApplication getApplicationFromAction(ModelAction action) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = registeredActionDbAdapter.fetch(action.getDatabaseId()); long appID = CursorHelper.getLongFromCursor(cursor, RegisteredActionDbAdapter.KEY_APPID); cursor.close(); return getApplication(appID); } /** * @param attribute * is a ModelAttribute object * * @return all filters associated with one attribute as an ArrayList * @throws IllegalStateException * if this helper is closed */ public ArrayList<ModelFilter> getFiltersForAttribute(ModelAttribute attribute) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } // Fetch all filter that filters on this attribute's dataType, set filterName to null, // set compareWithDatatypeID to null Cursor cursor = dataFilterDbAdapter.fetchAll(null, null, attribute.getDatatype(), null); ArrayList<ModelFilter> filterList = new ArrayList<ModelFilter>(cursor.getCount()); while (cursor.moveToNext()) { long filterID = getLongFromCursor(cursor, DataFilterDbAdapter.KEY_DATAFILTERID); String filterName = dataFilterNames.get(filterID); filterList.add(new ModelFilter(filterName, "" // TODO(ehotou) After implementing desc for // filter, load it here , R.drawable.icon_filter_unknown, filterID, attribute)); } cursor.close(); return filterList; } /** * @return all Rule objects (sparse version just contains id, name and enabled) as an ArrayList * @throws IllegalStateException * if this helper is closed */ public ArrayList<Rule> getRules() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = ruleDbAdapter.fetchAll(); ArrayList<Rule> ruleList = new ArrayList<Rule>(cursor.getCount()); while (cursor.moveToNext()) { ruleList.add(loadRuleSparse(cursor)); } cursor.close(); return ruleList; } /** * @param ruleId * is the rule id * * @return a fully loaded Rule object with databaseId * @throws IllegalStateException * if this helper is closed */ public Rule loadRule(long ruleId) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursorRule = ruleDbAdapter.fetch(ruleId); Rule rule = loadRuleSparse(cursorRule); // Fetch and set the root event. ModelEvent event = events.get(getLongFromCursor(cursorRule, RuleDbAdapter.KEY_EVENTID)); rule.setRootEvent(event); Log.d("UIDbhelper", "rule name: " + rule.getName()); // Add all filters for this rule to the root node in a tree format. addFiltersToRuleNode(ruleId, rule.getRootNode()); Log.d("UIDbhelper", "rule root event added: " + rule.getRootNode().getItem().getTypeName()); // Add all actions for this rule ArrayList<ModelRuleAction> actionList = getActionsForRule(ruleId); for (ModelRuleAction action : actionList) { rule.getRootNode().addChild(action); } Log.d("UIDbhelper", "rule loaded: " + rule.getNaturalLanguageString()); cursorRule.close(); return rule; } /** * @return a sparse version of rule object */ private Rule loadRuleSparse(Cursor cursorRule) { Rule rule = new Rule(getLongFromCursor(cursorRule, RuleDbAdapter.KEY_RULEID)); rule.setName(getStringFromCursor(cursorRule, RuleDbAdapter.KEY_RULENAME)); rule.setDescription(getStringFromCursor(cursorRule, RuleDbAdapter.KEY_RULEDESC)); rule.setIsEnabled(getBooleanFromCursor(cursorRule, RuleDbAdapter.KEY_ENABLED)); rule.setNotification(getBooleanFromCursor(cursorRule, RuleDbAdapter.KEY_NOTIFICATION)); return rule; } /** * Adds all filters to a root node in tree form. * * TODO(ehotou) consider moving part of this method which constructs the tree structure to some * util/builder method within Rule class * * @param ruleId * is id of the rule record to be loaded * * @param rootEvent * the event node at the root of the rule structure */ private void addFiltersToRuleNode(long ruleId, RuleNode rootEvent) { // Map<filterId, filterParentId>, for all filters of the rule. HashMap<Long, Long> parentIds = new HashMap<Long, Long>(); // All filters keyed by filterId for quick lookup below. HashMap<Long, ModelRuleFilter> filtersUnlinked = new HashMap<Long, ModelRuleFilter>(); // Fetch all ruleFilter associated with this rule, set other parameters to be null Cursor cursorRuleFilters = ruleFilterDbAdapter.fetchAll(ruleId, null, null, null, null, null); while (cursorRuleFilters.moveToNext()) { // Get attribute that this ruleFilter associated with long attributeID = getLongFromCursor(cursorRuleFilters, RuleFilterDbAdapter.KEY_EVENTATTRIBUTEID); ModelAttribute attribute; if (specificAttributes.containsKey(attributeID)) { attribute = specificAttributes.get(attributeID); } else { attribute = globalAttributes.get(attributeID); } // Load the user defined data within this rule filter String filterInputFromUser = getStringFromCursor(cursorRuleFilters, RuleFilterDbAdapter.KEY_RULEFILTERDATA); Log.d("addFiltersToRuleNode", "trying to construct a " + attribute.getDatatype() + " dataType with: " + filterInputFromUser); // For filters, first load up the model filter, then supply it to // the model rule filter instance. long filterID = getLongFromCursor(cursorRuleFilters, RuleFilterDbAdapter.KEY_DATAFILTERID); String filterName = dataFilterNames.get(filterID); Cursor cursorFilter = dataFilterDbAdapter.fetch(filterID); long compareWithDataTypeId = getLongFromCursor(cursorFilter, DataFilterDbAdapter.KEY_COMPAREWITHDATATYPEID); // Construct a data type object DataType filterData = getDataType(compareWithDataTypeId, filterInputFromUser); Log.d("addFiltersToRuleNode", "The object constructed is : " + filterData); ModelFilter modelFilter = new ModelFilter(filterName, "", // TODO(ehotou) After implementing // desc for filter, load it here. R.drawable.icon_filter_unknown, filterID, attribute); ModelRuleFilter filter = new ModelRuleFilter(getLongFromCursor(cursorRuleFilters, RuleFilterDbAdapter.KEY_RULEFILTERID), modelFilter, filterData); // Insert filterId, filterParentId long filterParentId = getLongFromCursor(cursorRuleFilters, RuleFilterDbAdapter.KEY_PARENTRULEFILTERID); parentIds.put(filter.getDatabaseId(), filterParentId); // Store the filter instance itself. filtersUnlinked.put(filter.getDatabaseId(), filter); cursorFilter.close(); } cursorRuleFilters.close(); // Keep iterating over all filters until we link each instance. This can be // improved if we know we'll get the filter records ordered in a tree hierarchy, // otherwise we can stick with this - we just keep passing over all the records // until we reconstruct each parent-child relationship. HashMap<Long, RuleNode> filtersLinked = new HashMap<Long, RuleNode>(); Iterator<Long> it = filtersUnlinked.keySet().iterator(); while (it.hasNext()) { Long filterId = it.next(); ModelRuleFilter filter = filtersUnlinked.get(filterId); Long parentFilterId = parentIds.get(filterId); if (parentFilterId < 1) { // This is a top-level filter, its parent is the root node. RuleNode node = rootEvent.addChild(filter); filtersLinked.put(filterId, node); it.remove(); } else { // Add this filter to its parent node. The node may not have // been constructed yet, so we have to skip it and handle it // on a subsequent iteration. RuleNode nodeParent = filtersLinked.get(parentFilterId); if (nodeParent != null) { RuleNode nodeChild = nodeParent.addChild(filter); filtersLinked.put(filterId, nodeChild); it.remove(); } } } } /** * Get all actions associated with a saved rule */ private ArrayList<ModelRuleAction> getActionsForRule(long ruleId) { Cursor cursorRuleActions = ruleActionDbAdapter.fetchAll(ruleId, null); ArrayList<ModelRuleAction> ruleActionList = new ArrayList<ModelRuleAction>(cursorRuleActions .getCount()); while (cursorRuleActions.moveToNext()) { long ruleActionID = getLongFromCursor(cursorRuleActions, RuleActionDbAdapter.KEY_RULEACTIONID); // Get the action ModelAction action = actions.get(getLongFromCursor(cursorRuleActions, RuleActionDbAdapter.KEY_ACTIONID)); // Get parameters of this action ArrayList<ModelParameter> actionParameters = action.getParameters(); // Get user data Cursor cursorRuleActionParameters = ruleActionParameterDbAdapter.fetchAll(ruleActionID, null, null); int count = cursorRuleActionParameters.getCount(); ArrayList<DataType> userData = new ArrayList<DataType>(count); for (int j = 0; j < count; j++) { cursorRuleActionParameters.moveToNext(); DataType data = getDataType(actionParameters.get(j).getDatatype(), getStringFromCursor( cursorRuleActionParameters, RuleActionParameterDbAdapter.KEY_RULEACTIONPARAMETERDATA)); userData.add(data); } ModelRuleAction ruleAction = new ModelRuleAction(getLongFromCursor(cursorRuleActions, RuleActionDbAdapter.KEY_RULEACTIONID), action, userData); cursorRuleActionParameters.close(); ruleActionList.add(ruleAction); } cursorRuleActions.close(); return ruleActionList; } /** * Given a rule, save it to the database. * * @return id of the saved rule record * * @throws IllegalStateException * if this helper is closed */ public long saveRule(Rule rule) throws Exception { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } ModelEvent event = (ModelEvent) rule.getRootNode().getItem(); ArrayList<RuleNode> ruleFilterList = rule.getFilterBranches(); ArrayList<ModelRuleAction> ruleActionList = rule.getActions(); if (rule.getDatabaseId() > 0) { deleteRule(rule.getDatabaseId()); } String ruleName = rule.getName(); String ruleDesc = rule.getDescription(); long ruleID = ruleDbAdapter.insert(event.getDatabaseId(), ruleName == null || ruleName.length() == 0 ? "New Rule" : ruleName, ruleDesc == null || ruleDesc.length() == 0 ? "" : ruleDesc, rule.getIsEnabled()); // Create all ruleAction records for (ModelRuleAction ruleAction : ruleActionList) { long ruleActionID = ruleActionDbAdapter.insert(ruleID, ruleAction.getModelAction() .getDatabaseId()); ArrayList<ModelParameter> parameterList = ruleAction.getModelAction().getParameters(); ArrayList<DataType> dataList = ruleAction.getDatas(); for (int i = 0; i < dataList.size(); i++) { ruleActionParameterDbAdapter.insert(ruleActionID, parameterList.get(i).getDatabaseId(), dataList.get(i).toString()); } } // Save all rule filters for (RuleNode filterNode : ruleFilterList) { saveFilterRuleNode(ruleID, -1, filterNode); } return ruleID; } /** * Recursively write each node of the filter branches to the database. * * @param node * is root of the ruleFilterNode tree to be added */ private void saveFilterRuleNode(long ruleID, long parentRuleNodeID, RuleNode node) { ModelRuleFilter filter = (ModelRuleFilter) node.getItem(); long ruleFilterID = ruleFilterDbAdapter.insert(ruleID, filter.getModelFilter().getAttribute() .getDatabaseId(), -1L, // TODO(ehotou) after implementing external, insert it here // TODO: (ehotou) verify ModelFilter id is what we want here (not ModelRuleFilter): filter.getModelFilter().getDatabaseId(), parentRuleNodeID, filter.getData().toString()); // insert all children filters recursively: for (RuleNode filterNode : node.getChildren()) { saveFilterRuleNode(ruleID, ruleFilterID, filterNode); } } /** * Delete a rule record as well as ruleAction, ruleActionParameter and ruleFilters records * associated with it. * * @param ruleID * is id of the rule to be delete */ public void deleteRule(long ruleID) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } ruleDbAdapter.delete(ruleID); // Delete all rule actions from database Cursor cursorRuleAction = ruleActionDbAdapter.fetchAll(ruleID, null); while (cursorRuleAction.moveToNext()) { long ruleActionID = getLongFromCursor(cursorRuleAction, RuleActionDbAdapter.KEY_RULEACTIONID); ruleActionDbAdapter.delete(ruleActionID); // Delete all rule parameters of this rule action Cursor cursorRuleActionParameter = ruleActionParameterDbAdapter.fetchAll(ruleActionID, null, null); while (cursorRuleActionParameter.moveToNext()) { long ruleActionParameterID = getLongFromCursor(cursorRuleActionParameter, RuleActionParameterDbAdapter.KEY_RULEACTIONPARAMETERID); ruleActionParameterDbAdapter.delete(ruleActionParameterID); } cursorRuleActionParameter.close(); } cursorRuleAction.close(); // Delete all rule filters from database Cursor cursorFilter = ruleFilterDbAdapter.fetchAll(ruleID, null, null, null, null, null); while (cursorFilter.moveToNext()) { long ruleFilterID = getLongFromCursor(cursorFilter, RuleFilterDbAdapter.KEY_RULEFILTERID); ruleFilterDbAdapter.delete(ruleFilterID); } cursorFilter.close(); } /** * Delete certain set of rules * * @param rules * is the set of Rules to be deleted */ public void deleteRules(List<? extends Rule> rules) { for (Rule rule : rules) { deleteRule(rule.getDatabaseId()); } } /** * Delete all rules */ public void deleteAllRules() { deleteRules(getRules()); } /** * Update enabled flag of one rule record * * @param ruleID * is id of the rule record to be updated * * @param enabled * is the enabled flag */ public void setRuleEnabled(long ruleID, boolean enabled) { ruleDbAdapter.update(ruleID, null, null, null, enabled, null); } public List<ModelLog> getEventLogs() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = logEventDbAdapter.fetchAll(); ArrayList<ModelLog> logList = new ArrayList<ModelLog>(cursor.getCount()); while (cursor.moveToNext()) { Long logID = getLongFromCursor(cursor, LogDbAdapter.KEY_ID); Long logTimestamp = getLongFromCursor(cursor, LogDbAdapter.KEY_TIMESTAMP); String logName = getStringFromCursor(cursor, LogEventDbAdapter.KEY_EVENTNAME); String logDesc = getStringFromCursor(cursor, LogDbAdapter.KEY_DESCRIPTION); logList.add(new ModelLog(logID, logName, logDesc, R.drawable.icon_event_unknown, logTimestamp, ModelLog.TYPE_EVENT)); } cursor.close(); return logList; } public Cursor getEventLogsCursor() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = logEventDbAdapter.fetchAll(); return cursor; } /** * @return the sharedPreferences to allow for get/setting of user preferences. */ public SharedPreferences getSharedPreferences() { return settings; } public Cursor getEventLogCursor(long eventID) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = logEventDbAdapter.fetch(eventID); return cursor; } public ModelLog getEventLog(long eventID) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = logEventDbAdapter.fetch(eventID); long logID = getLongFromCursor(cursor, LogDbAdapter.KEY_ID); String logName = getStringFromCursor(cursor, LogEventDbAdapter.KEY_EVENTNAME); long logTimestamp = getLongFromCursor(cursor, LogDbAdapter.KEY_TIMESTAMP); String logDesc = getStringFromCursor(cursor, LogDbAdapter.KEY_DESCRIPTION); ModelLog log = new ModelLog(logID, logName, logDesc, R.drawable.icon_event_unknown, logTimestamp, ModelLog.TYPE_EVENT); cursor.close(); return log; } public void deleteEventLogs() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } logEventDbAdapter.deleteAll(); } public void deleteActionLogs() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } logActionDbAdapter.deleteAll(); } public List<ModelLog> getActionLogs() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = logActionDbAdapter.fetchAll(); ArrayList<ModelLog> logList = new ArrayList<ModelLog>(cursor.getCount()); while (cursor.moveToNext()) { long logID = getLongFromCursor(cursor, LogDbAdapter.KEY_ID); String logName = getStringFromCursor(cursor, LogActionDbAdapter.KEY_ACTIONEVENTNAME); long logTimestamp = getLongFromCursor(cursor, LogDbAdapter.KEY_TIMESTAMP); String logDesc = getStringFromCursor(cursor, LogDbAdapter.KEY_DESCRIPTION); logList.add(new ModelLog(logID, logName, logDesc, R.drawable.icon_action_unknown, logTimestamp, ModelLog.TYPE_ACTION)); } cursor.close(); return logList; } public ModelLog getActionLog(long id) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = logActionDbAdapter.fetch(id); long logID = getLongFromCursor(cursor, LogDbAdapter.KEY_ID); String logName = getStringFromCursor(cursor, LogActionDbAdapter.KEY_ACTIONEVENTNAME); long logTimestamp = getLongFromCursor(cursor, LogDbAdapter.KEY_TIMESTAMP); String logDesc = getStringFromCursor(cursor, LogDbAdapter.KEY_DESCRIPTION); ModelLog log = new ModelLog(logID, logName, logDesc, R.drawable.icon_action_unknown, logTimestamp, ModelLog.TYPE_ACTION); cursor.close(); return log; } public List<ModelLog> getGeneralLogs() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = logGeneralDbAdapter.fetchAll(); ArrayList<ModelLog> logList = new ArrayList<ModelLog>(cursor.getCount()); while (cursor.moveToNext()) { long logID = getLongFromCursor(cursor, LogDbAdapter.KEY_ID); String logName = getStringFromCursor(cursor, LogGeneralDbAdapter.KEY_DESCRIPTION); long logTimestamp = getLongFromCursor(cursor, LogDbAdapter.KEY_TIMESTAMP); String logDesc = getStringFromCursor(cursor, LogDbAdapter.KEY_DESCRIPTION); logList.add(new ModelLog(logID, logName, logDesc, R.drawable.icon_log_general, logTimestamp, ModelLog.TYPE_GENERAL)); } cursor.close(); return logList; } public ModelLog getGeneralLog(long id) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = logGeneralDbAdapter.fetch(id); long logID = getLongFromCursor(cursor, LogDbAdapter.KEY_ID); String logName = getStringFromCursor(cursor, LogGeneralDbAdapter.KEY_DESCRIPTION); long logTimestamp = getLongFromCursor(cursor, LogDbAdapter.KEY_TIMESTAMP); String logDesc = getStringFromCursor(cursor, LogDbAdapter.KEY_DESCRIPTION); ModelLog log = new ModelLog(logID, logName, logDesc, R.drawable.icon_log_general, logTimestamp, ModelLog.TYPE_GENERAL); cursor.close(); return log; } public void deleteGeneralLogs() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } logGeneralDbAdapter.deleteAll(); } public List<ModelLog> getAllLogs() { ArrayList<ModelLog> logs = new ArrayList<ModelLog>(); logs.addAll(getGeneralLogs()); logs.addAll(getEventLogs()); logs.addAll(getActionLogs()); Collections.sort(logs, new Comparator<ModelLog>() { public int compare(ModelLog o1, ModelLog o2) { return o1.compareTo(o2); } }); return logs; } public ModelLog getLog(int type, long id) { if (type == ModelLog.TYPE_EVENT) { return getEventLog(id); } else if (type == ModelLog.TYPE_ACTION) { return getActionLog(id); } else if (type == ModelLog.TYPE_GENERAL) { return getGeneralLog(id); } else { return null; } } public void deleteAllLogs() { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } logEventDbAdapter.deleteAll(); logActionDbAdapter.deleteAll(); logGeneralDbAdapter.deleteAll(); } /** * * @param eventID * database id * * @throws IllegarStateException * when database is closed * @return number of rules for event or -1 if no matching eventID. */ public int getRuleCount(Long eventID) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } Cursor cursor = ruleDbAdapter.fetchAll(eventID, null, null, null, null); if (cursor != null) { int count = cursor.getCount(); cursor.close(); return count; } return -1; } public void setNotification(Long ruleId, Boolean notification) { if (isClosed) { throw new IllegalStateException(TAG + " is closed."); } ruleDbAdapter.update(ruleId, null, null, null, null, notification); } }