/*
* Copyright 2010, The Android Open Source Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "GeolocationPositionCache.h"
#include "Geoposition.h"
#include "SQLValue.h"
#include "SQLiteDatabase.h"
#include "SQLiteFileSystem.h"
#include "SQLiteStatement.h"
#include "SQLiteTransaction.h"
namespace WebCore {
static const char* databaseName = "CachedGeoposition.db";
int GeolocationPositionCache::s_instances = 0;
RefPtr<Geoposition>* GeolocationPositionCache::s_cachedPosition;
String* GeolocationPositionCache::s_databaseFile = 0;
GeolocationPositionCache::GeolocationPositionCache()
{
if (!(s_instances++)) {
s_cachedPosition = new RefPtr<Geoposition>;
*s_cachedPosition = readFromDB();
}
}
GeolocationPositionCache::~GeolocationPositionCache()
{
if (!(--s_instances)) {
if (*s_cachedPosition)
writeToDB(s_cachedPosition->get());
delete s_cachedPosition;
}
}
void GeolocationPositionCache::setCachedPosition(Geoposition* cachedPosition)
{
*s_cachedPosition = cachedPosition;
}
Geoposition* GeolocationPositionCache::cachedPosition()
{
return s_cachedPosition->get();
}
void GeolocationPositionCache::setDatabasePath(const String& databasePath)
{
if (!s_databaseFile)
s_databaseFile = new String;
*s_databaseFile = SQLiteFileSystem::appendDatabaseFileNameToPath(databasePath, databaseName);
// If we don't have have a cached position, attempt to read one from the
// DB at the new path.
if (s_instances && !(*s_cachedPosition))
*s_cachedPosition = readFromDB();
}
PassRefPtr<Geoposition> GeolocationPositionCache::readFromDB()
{
SQLiteDatabase database;
if (!s_databaseFile || !database.open(*s_databaseFile))
return 0;
// Create the table here, such that even if we've just created the
// DB, the commands below should succeed.
if (!database.executeCommand("CREATE TABLE IF NOT EXISTS CachedPosition ("
"latitude REAL NOT NULL, "
"longitude REAL NOT NULL, "
"altitude REAL, "
"accuracy REAL NOT NULL, "
"altitudeAccuracy REAL, "
"heading REAL, "
"speed REAL, "
"timestamp INTEGER NOT NULL)"))
return 0;
SQLiteStatement statement(database, "SELECT * FROM CachedPosition");
if (statement.prepare() != SQLResultOk)
return 0;
if (statement.step() != SQLResultRow)
return 0;
bool providesAltitude = statement.getColumnValue(2).type() != SQLValue::NullValue;
bool providesAltitudeAccuracy = statement.getColumnValue(4).type() != SQLValue::NullValue;
bool providesHeading = statement.getColumnValue(5).type() != SQLValue::NullValue;
bool providesSpeed = statement.getColumnValue(6).type() != SQLValue::NullValue;
RefPtr<Coordinates> coordinates = Coordinates::create(statement.getColumnDouble(0), // latitude
statement.getColumnDouble(1), // longitude
providesAltitude, statement.getColumnDouble(2), // altitude
statement.getColumnDouble(3), // accuracy
providesAltitudeAccuracy, statement.getColumnDouble(4), // altitudeAccuracy
providesHeading, statement.getColumnDouble(5), // heading
providesSpeed, statement.getColumnDouble(6)); // speed
return Geoposition::create(coordinates.release(), statement.getColumnInt64(7)); // timestamp
}
void GeolocationPositionCache::writeToDB(const Geoposition* position)
{
ASSERT(position);
SQLiteDatabase database;
if (!s_databaseFile || !database.open(*s_databaseFile))
return;
SQLiteTransaction transaction(database);
if (!database.executeCommand("DELETE FROM CachedPosition"))
return;
SQLiteStatement statement(database, "INSERT INTO CachedPosition ("
"latitude, "
"longitude, "
"altitude, "
"accuracy, "
"altitudeAccuracy, "
"heading, "
"speed, "
"timestamp) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
if (statement.prepare() != SQLResultOk)
return;
statement.bindDouble(1, position->coords()->latitude());
statement.bindDouble(2, position->coords()->longitude());
if (position->coords()->canProvideAltitude())
statement.bindDouble(3, position->coords()->altitude());
else
statement.bindNull(3);
statement.bindDouble(4, position->coords()->accuracy());
if (position->coords()->canProvideAltitudeAccuracy())
statement.bindDouble(5, position->coords()->altitudeAccuracy());
else
statement.bindNull(5);
if (position->coords()->canProvideHeading())
statement.bindDouble(6, position->coords()->heading());
else
statement.bindNull(6);
if (position->coords()->canProvideSpeed())
statement.bindDouble(7, position->coords()->speed());
else
statement.bindNull(7);
statement.bindInt64(8, position->timestamp());
if (!statement.executeCommand())
return;
transaction.commit();
}
} // namespace WebCore