/**************************************************************************************************
* Copyright (C) 2010 Sense Observation Systems, Rotterdam, the Netherlands. All rights reserved. *
*************************************************************************************************/
package nl.sense_os.service.storage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import android.util.Log;
import nl.sense_os.service.constants.SensorData.DataPoint;
/**
* Hacky solution for parsing SQL-like queries on the LocalStorage. Normal ContentProviders will not
* need this class because they can handle these queries themselves, but our {@link LocalStorage}
* class does not.
*
* @author Steven Mulder <steven@sense-os.nl>
* @see LocalStorage
*/
public class ParserUtils {
@SuppressWarnings("unused")
private static final String TAG = "ParserUtils";
private static String fixCompareSigns(String s) {
String result = s.replaceAll(" = ", "=");
result = result.replaceAll("= ", "=");
result = result.replaceAll(" =", "=");
result = result.replaceAll(" > ", ">");
result = result.replaceAll("> ", ">");
result = result.replaceAll(" >", ">");
result = result.replaceAll(" < ", "<");
result = result.replaceAll("< ", "<");
result = result.replaceAll(" <", "<");
result = result.replaceAll(" != ", "!=");
result = result.replaceAll("!= ", "!=");
result = result.replaceAll(" !=", "!=");
return result;
}
public static String getSelectedDeviceUuid(String selection, String[] selectionArgs) {
String deviceUuid = null;
if (selection != null && selection.contains(DataPoint.DEVICE_UUID)) {
// preprocess the selection string a bit
selection = fixCompareSigns(selection);
int eqKeyStart = selection.indexOf(DataPoint.DEVICE_UUID + "='");
if (-1 != eqKeyStart) {
// selection contains "device_uuid='"
int uuidStart = eqKeyStart + (DataPoint.DEVICE_UUID + "='").length();
int uuidEnd = selection.indexOf("'", uuidStart);
uuidEnd = uuidEnd == -1 ? selection.length() - 1 : uuidEnd;
deviceUuid = selection.substring(uuidStart, uuidEnd);
}
}
return deviceUuid;
}
/**
* Tries to parse the selection String to see which data has to be returned for the query. Looks
* for occurrences of {@link DataPoint#SENSOR_NAME} and {@link DataPoint#SENSOR_DESCRIPTION} in
* the selection String.
*
* @param allSensors
* Set of all possible sensors, used to form the selection from.
* @param selection
* Selection string from the query.
* @param selectionArgs
* Selection arguments. Not used yet.
* @return List of sensor names that are included in the query.
*/
public static List<String> getSelectedSensors(Set<String> allSensors, String selection,
String[] selectionArgs) {
List<String> sensorNameMatches = getSensorNameMatches(allSensors, selection);
// List<String> result = getSensorDescriptionMatches(new HashSet<String>(sensorNameMatches),
// selection);
return sensorNameMatches;
}
/**
* Tries to parse the selection String to see which data has to be returned for the query. Looks
* for occurrences of "timestamp" in the selection String.
*
* @param selection
* Selection string from the query.
* @param selectionArgs
* Selection arguments. Not used yet.
* @return Array with minimum and maximum time stamp for the query result.
*/
public static long[] getSelectedTimeRange(String selection, String[] selectionArgs) {
long minTimestamp = Long.MIN_VALUE;
long maxTimestamp = Long.MAX_VALUE;
if (selection != null && selection.contains(DataPoint.TIMESTAMP)) {
// preprocess the selection string a bit
selection = fixCompareSigns(selection);
int eqKeyStart = selection.indexOf(DataPoint.TIMESTAMP + "=");
int neqKeyStart = selection.indexOf(DataPoint.TIMESTAMP + "!=");
int leqKeyStart = selection.indexOf(DataPoint.TIMESTAMP + "<=");
int ltKeyStart = selection.indexOf(DataPoint.TIMESTAMP + "<");
int geqKeyStart = selection.indexOf(DataPoint.TIMESTAMP + ">=");
int gtKeyStart = selection.indexOf(DataPoint.TIMESTAMP + ">");
if (-1 != eqKeyStart) {
// selection contains "timestamp='"
int timestampStart = eqKeyStart + (DataPoint.TIMESTAMP + "=").length();
int timestampEnd = selection.indexOf(" ", timestampStart);
timestampEnd = timestampEnd == -1 ? selection.length() : timestampEnd;
String timestamp = selection.substring(timestampStart, timestampEnd);
if (timestamp.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.TIMESTAMP + " = " + timestamp);
minTimestamp = maxTimestamp = Long.parseLong(timestamp);
} else if (-1 != neqKeyStart) {
// selection contains "timestamp!='"
int timestampStart = neqKeyStart + (DataPoint.TIMESTAMP + "!=").length();
int timestampEnd = selection.indexOf(" ", timestampStart);
timestampEnd = timestampEnd == -1 ? selection.length() : timestampEnd;
String timestamp = selection.substring(timestampStart, timestampEnd);
if (timestamp.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.TIMESTAMP + " != " + timestamp);
// use default timestamps
}
if (-1 != geqKeyStart) {
// selection contains "timestamp>='"
int timestampStart = geqKeyStart + (DataPoint.TIMESTAMP + ">=").length();
int timestampEnd = selection.indexOf(" ", timestampStart);
timestampEnd = timestampEnd == -1 ? selection.length() : timestampEnd;
String timestamp = selection.substring(timestampStart, timestampEnd);
if (timestamp.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.TIMESTAMP + " >= " + timestamp);
minTimestamp = Long.parseLong(timestamp);
} else if (-1 != gtKeyStart) {
// selection contains "timestamp>'"
int timestampStart = gtKeyStart + (DataPoint.TIMESTAMP + ">").length();
int timestampEnd = selection.indexOf(" ", timestampStart);
timestampEnd = timestampEnd == -1 ? selection.length() : timestampEnd;
String timestamp = selection.substring(timestampStart, timestampEnd);
if (timestamp.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.TIMESTAMP + " > " + timestamp);
minTimestamp = Long.parseLong(timestamp) - 1;
}
if (-1 != leqKeyStart) {
// selection contains "timestamp<='"
int timestampStart = leqKeyStart + (DataPoint.TIMESTAMP + "<=").length();
int timestampEnd = selection.indexOf(" ", timestampStart);
timestampEnd = timestampEnd == -1 ? selection.length() : timestampEnd;
String timestamp = selection.substring(timestampStart, timestampEnd);
if (timestamp.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.TIMESTAMP + " <= " + timestamp);
maxTimestamp = Long.parseLong(timestamp);
} else if (-1 != ltKeyStart) {
// selection contains "timestamp<'"
int timestampStart = ltKeyStart + (DataPoint.TIMESTAMP + "<").length();
int timestampEnd = selection.indexOf(" ", timestampStart);
timestampEnd = timestampEnd == -1 ? selection.length() : timestampEnd;
String timestamp = selection.substring(timestampStart, timestampEnd);
if (timestamp.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.TIMESTAMP + " < " + timestamp);
maxTimestamp = Long.parseLong(timestamp) - 1;
}
} else {
// no selection: return all times
return new long[] { Long.MIN_VALUE, Long.MAX_VALUE };
}
return new long[] { minTimestamp, maxTimestamp };
}
public static int getSelectedTransmitState(String selection, String[] selectionArgs) {
int result = -1;
if (selection != null && selection.contains(DataPoint.TRANSMIT_STATE)) {
// preprocess the selection a bit
selection = selection.replaceAll(" = ", "=");
selection = selection.replaceAll("= ", "=");
selection = selection.replaceAll(" =", "=");
selection = selection.replaceAll(" != ", "!=");
selection = selection.replaceAll("!= ", "!=");
selection = selection.replaceAll(" !=", "!=");
int eqKeyStart = selection.indexOf(DataPoint.TRANSMIT_STATE + "=");
int neqKeyStart = selection.indexOf(DataPoint.TRANSMIT_STATE + "!=")
+ (DataPoint.TRANSMIT_STATE + "!=").length();
if (-1 != eqKeyStart) {
// selection contains "sensor_name='"
int stateStart = eqKeyStart + (DataPoint.TRANSMIT_STATE + "=").length();
int stateEnd = selection.indexOf(" ", stateStart);
stateEnd = stateEnd == -1 ? selection.length() - 1 : stateEnd;
String state = selection.substring(stateStart, stateEnd);
if (state.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.TRANSMIT_STATE + " = " + state + "");
result = state.equals("1") ? 1 : 0;
} else if (-1 != neqKeyStart) {
// selection contains "sensor_name!='"
int stateStart = neqKeyStart + (DataPoint.TRANSMIT_STATE + "!=").length();
int stateEnd = selection.indexOf(" ", stateStart);
stateEnd = stateEnd == -1 ? selection.length() - 1 : stateEnd;
String notState = selection.substring(stateStart, stateEnd);
if (notState.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.TRANSMIT_STATE + " != " + notState +
// "");
result = notState.equals("1") ? 0 : 1;
} else {
throw new IllegalArgumentException("Parser cannot handle selection query: "
+ selection);
}
}
return result;
}
/**
* Returns list of sensors that have match the selected sensor description. If the selection
* String does not contain {@link DataPoint#SENSOR_DESCRIPTION}, the complete list of sensors is
* returned.
*
* @param allSensors
* @param selection
* @return
*/
private static List<String> getSensorDescriptionMatches(Set<String> allSensors, String selection) {
List<String> result = new ArrayList<String>();
if (selection != null && selection.contains(DataPoint.SENSOR_DESCRIPTION)) {
// preprocess the selection string a bit
selection = fixCompareSigns(selection);
int eqKeyStart = selection.indexOf(DataPoint.SENSOR_DESCRIPTION + "='");
int neqKeyStart = selection.indexOf(DataPoint.SENSOR_DESCRIPTION + "!='")
+ (DataPoint.SENSOR_DESCRIPTION + "!='").length();
if (-1 != eqKeyStart) {
// selection contains "sensor_description='"
int descriptionStart = eqKeyStart + (DataPoint.SENSOR_DESCRIPTION + "='").length();
int descriptionEnd = selection.indexOf("'", descriptionStart);
descriptionEnd = descriptionEnd == -1 ? selection.length() - 1 : descriptionEnd;
String description = selection.substring(descriptionStart, descriptionEnd);
if (description.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
Log.v(TAG, "Query contains: " + DataPoint.SENSOR_DESCRIPTION + " = '" +
description + "'");
for (String key : allSensors) {
if (key.endsWith("(" + description + ")") || key.equals(description)) {
result.add(key);
}
}
} else if (-1 != neqKeyStart) {
// selection contains "sensor_description!='"
int descriptionStart = neqKeyStart
+ (DataPoint.SENSOR_DESCRIPTION + "!='").length();
int descriptionEnd = selection.indexOf("'", descriptionStart);
descriptionEnd = descriptionEnd == -1 ? selection.length() - 1 : descriptionEnd;
String notDescription = selection.substring(descriptionStart, descriptionEnd);
if (notDescription.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.SENSOR_DESCRIPTION + "!= '" +
// notDescription + "'");
for (String key : allSensors) {
if (!(key.endsWith("(" + notDescription + ")") || key.equals(notDescription))) {
result.add(key);
}
}
} else {
throw new IllegalArgumentException("Parser cannot handle selection query: "
+ selection);
}
} else {
// no selection: return all sensor names
result.addAll(allSensors);
}
// return a copy of the list of names
return new ArrayList<String>(result);
}
/**
* Returns list of sensors that have match the selected sensor name. If the selection String
* does not contain {@link DataPoint#SENSOR_NAME}, the complete list of sensors is returned.
*
* @param allSensors
* @param selection
* @return
*/
private static List<String> getSensorNameMatches(Set<String> allSensors, String selection) {
List<String> result = new ArrayList<String>();
if (selection != null && selection.contains(DataPoint.SENSOR_NAME)) {
// preprocess the selection string a bit
selection = fixCompareSigns(selection);
int eqKeyStart = selection.indexOf(DataPoint.SENSOR_NAME + "='");
int neqKeyStart = selection.indexOf(DataPoint.SENSOR_NAME + "!='")
+ (DataPoint.SENSOR_NAME + "!='").length();
if (-1 != eqKeyStart) {
// selection contains "sensor_name='"
int sensorNameStart = eqKeyStart + (DataPoint.SENSOR_NAME + "='").length();
int sensorNameEnd = selection.indexOf("'", sensorNameStart);
sensorNameEnd = sensorNameEnd == -1 ? selection.length() - 1 : sensorNameEnd;
String sensorName = selection.substring(sensorNameStart, sensorNameEnd);
if (sensorName.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.SENSOR_NAME + " = '" + sensorName +
// "'");
boolean inStorage = false;
for (String key : allSensors) {
if (key.startsWith(sensorName)) {
result.add(key);
inStorage = true;
}
}
// sometimes we want to select sensors that are not currently in the storage
if (!inStorage) {
result.add(sensorName);
}
} else if (-1 != neqKeyStart) {
// selection contains "sensor_name!='"
int sensorNameStart = neqKeyStart + (DataPoint.SENSOR_NAME + "!='").length();
int sensorNameEnd = selection.indexOf("'", sensorNameStart);
sensorNameEnd = sensorNameEnd == -1 ? selection.length() - 1 : sensorNameEnd;
String notSensorName = selection.substring(sensorNameStart, sensorNameEnd);
if (notSensorName.equals("?")) {
throw new IllegalArgumentException(
"LocalStorage cannot handle queries with arguments array, sorry...");
}
// Log.v(TAG, "Query contains: " + DataPoint.SENSOR_NAME + " != '" + notSensorName +
// "'");
for (String key : allSensors) {
if (!key.startsWith(notSensorName)) {
result.add(key);
}
}
} else {
throw new IllegalArgumentException("Parser cannot handle selection query: "
+ selection);
}
} else {
// no selection: return all sensor names
result.addAll(allSensors);
}
// return a copy of the list of names
return new ArrayList<String>(result);
}
private ParserUtils() {
// class should not be instantiated
}
}