blob: ca2f8ed5d96d86d12f0e84c4336b266635b677a6 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2021 the Eclipse BaSyx Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
******************************************************************************/
package org.eclipse.basyx.tools.sqlproxy;
import java.sql.ResultSet;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.basyx.tools.sql.driver.ISQLDriver;
import org.eclipse.basyx.tools.sql.query.DynamicSQLQuery;
import org.eclipse.basyx.tools.sql.query.DynamicSQLUpdate;
/**
* This class implements a map that mirrors its contents into a SQL database
*
* - The map represents the root map that is linked into the SQL database as one table
* - Subsequent maps map to subsequent SQL tables
*
* A SQL table has the following structure:
* - name:text | type:integer | value:text
*
* @author kuhn
*
*/
public class SQLMap extends SQLProxy implements Map<String, Object> {
/**
* Constructor
*
* @param rootElement SQLRootElement for this element
* @param tableId Table ID of this element in SQL database
*/
public SQLMap(SQLRootElement rootElement, int tableId) {
// Invoke base constructor
super(rootElement.getDriver(), rootElement.getSqlTableID() + "__" + tableId, rootElement);
}
/**
* Constructor
*
* @param rootElement SQLRootElement for this element
* @param tableIdWithprefix Table ID of this element in SQL database with prefix
*/
public SQLMap(SQLRootElement rootElement, String tableIdWithprefix) {
// Invoke base constructor
super(rootElement.getDriver(), tableIdWithprefix, rootElement);
}
/**
* Constructor for creating a new SQLMap from another Map
*/
public SQLMap(SQLRootElement rootElement, Map<String, Object> other) {
this(rootElement, createMapTable(rootElement));
for (Entry<String, Object> entry : other.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
/**
* Create a new table for a map and return its generated id (relative to the root element)
*
* @return The table id in the root element scope
*/
private static int createMapTable(SQLRootElement rootElement) {
int tableId = rootElement.getNextIdentifier();
rootElement.createMap(tableId);
return tableId;
}
/**
* Get number of map elements
*/
@Override
public int size() {
// Build query string
String queryString = "SELECT * FROM elements."+ getSqlTableID();
// - Build dynamic query
// - basically, the last parameter is not used here, as getRaw does not post process query results
DynamicSQLQuery dynQuery = new DynamicSQLQuery(getDriver(), queryString, "mapArray(name:String,value:String,type:String)");
// Execute query and get result set
ResultSet result = dynQuery.getRaw();
// Calculate size
return getSize(result);
}
/**
* Check if map is empty
*/
@Override
public boolean isEmpty() {
// Map is empty iff its size equals 0
return (size() == 0);
}
/**
* Check if map contains the given key (name)
*/
@Override
public boolean containsKey(Object key) {
// Use new driver for operation
return containsKey(getDriver(), key);
}
/**
* Check if map contains the given key (name)
*/
protected boolean containsKey(ISQLDriver drv, Object key) {
// Build query string
String queryString = "SELECT * FROM elements." + getSqlTableID() + " WHERE name='$name'";
// - Build dynamic query
// - basically, the last parameter is not used here, as getRaw does not post process query results
DynamicSQLQuery dynQuery = new DynamicSQLQuery(drv, queryString, "mapArray(name:String,value:String,type:String)");
// Build query parameter
Map<String, Object> parameter = new HashMap<>();
// - Put name in map
parameter.put("name", key);
// Execute query, get result set
ResultSet result = dynQuery.getRaw(parameter);
// Data base table contains key iff result set size > 1
return getSize(result) > 0;
}
/**
* Check if map contains the given value
*/
@Override
public boolean containsValue(Object value) {
// Use new driver for operation
return containsValue(getDriver(), value);
}
/**
* Check if map contains the given value
*/
protected boolean containsValue(ISQLDriver drv, Object value) {
// Build query string
String queryString = "SELECT * FROM elements."+ getSqlTableID() +" WHERE value='$value'";
// - Build dynamic query
// - basically, the last parameter is not used here, as getRaw does not post process query results
DynamicSQLQuery dynQuery = new DynamicSQLQuery(drv, queryString, "mapArray(name:String,value:String,type:String)");
// Build query parameter
Map<String, Object> parameter = new HashMap<>();
// - Put name in map
parameter.put("value", SQLTableRow.getValueAsString(value));
// Execute query, get result set
ResultSet result = dynQuery.getRaw(parameter);
// Data base table contains key iff result set size > 1
return getSize(result) > 0;
}
/**
* Get value of map element that is identified by given key
*/
@Override
public Object get(Object key) {
// Get value from SQL database table
return getValueFromMap(getSqlTableID(), key.toString());
}
/**
* Put a key into a map
*/
@Override
public Object put(String key, Object value) {
putValue(getDriver(), key, value);
// Return inserted object
return value;
}
/**
* Puts arbitrary values in the map (even if they already exist). Does not commit the changes
* using the SQLDriver.
*/
protected void putValue(ISQLDriver sqlDrv, String key, Object value) {
value = convertToSimpleValue(value);
putSimpleValue(sqlDrv, key, value);
}
/**
* Puts simple values to the map (those, that can be directly converted to a table row). Does not commit the
* changes using the SQLDriver.
*/
private void putSimpleValue(ISQLDriver sqlDrv, String key, Object value) {
if (containsKey(sqlDrv, key)) {
updateInMapSimple(sqlDrv, getSqlTableID(), new SQLTableRow(key, value));
} else {
addToMapSimple(sqlDrv, getSqlTableID(), new SQLTableRow(key, value));
}
}
/**
* Converts arbitrary values to simple ones that can be directly added to the map's table as a single row.
* Currently, maps are supported. For arbitrary maps, a new table will be created that can then be referenced in
* this map.
*/
@SuppressWarnings("unchecked")
private Object convertToSimpleValue(Object value) {
// If the value is a map that can not be directly referenced by this root element
if (value instanceof Map && (!(value instanceof SQLMap)
|| !(((SQLMap) value).sqlRootElement.getSqlTableID().equals(sqlRootElement.getSqlTableID())))) {
// Create a new referable SQLMap out of the value in the scope of this root element
return new SQLMap(sqlRootElement, (Map<String, Object>) value);
}
return value;
}
/**
* Remove element with key from map
*/
@Override
public Object remove(Object key) {
// Return value
Object result = null;
// Get element from map for return value
result = getValueFromMap(getDriver(), getSqlTableID(), key.toString());
// Delete element from map
String updateString = "DELETE FROM elements."+getSqlTableID()+" WHERE name='$name'";
DynamicSQLUpdate dynUpdate = new DynamicSQLUpdate(getDriver(), updateString);
// Parameter map
Map<String, Object> parameter = new HashMap<>();
// - Put name in map
parameter.put("name", key);
// - Execute delete
dynUpdate.accept(parameter);
// Return element
return result;
}
/**
* Put all elements into map
*/
@Override @SuppressWarnings("unchecked")
public void putAll(Map<? extends String, ? extends Object> map) {
// Remove old elements
removeAllKeys(getDriver(), (Set<String>) map.keySet());
// Create map elements
Collection<SQLTableRow> mapElements = new LinkedList<>();
// - Fill collection
for (String key: map.keySet()) {mapElements.add(new SQLTableRow(key, map.get(key)));}
// Add elements to map
addToMapMultiple(getDriver(), getSqlTableID(), mapElements);
}
/**
* Delete all map elements
*/
@Override
public void clear() {
// Build SQL update string
String updateString = "DELETE FROM elements."+ getSqlTableID();
DynamicSQLUpdate dynUpdate = new DynamicSQLUpdate(getDriver(), updateString);
// Empty parameter set
Map<String, Object> parameter = new HashMap<>();
// Run SQL query
dynUpdate.accept(parameter);
}
/**
* Get the set of keys from SQL database
*/
@Override @SuppressWarnings("unchecked")
public Set<String> keySet() {
// Get key set - keys are stored in "name" column in table
return (Set<String>) getSingleMapColumnRaw(getSqlTableID(), "name");
}
/**
* Get map values that are contained in the SQL database
*/
@Override @SuppressWarnings("unchecked")
public Collection<Object> values() {
// Get values
List<Map<String, Object>> sqlResult = (List<Map<String, Object>>) getMapColumnRaw(getSqlTableID(), "type", "value");
// Build types
Collection<Object> result = new LinkedList<Object>();
// - Fill result
for (Map<String, Object> singleResult: sqlResult) {
result.add(SQLTableRow.getValueFromString(sqlRootElement, Integer.parseInt((String) singleResult.get("type")), (String) singleResult.get("value")));
}
// Return result
return result;
}
/**
* Return map elements as entry sets
*/
@Override @SuppressWarnings("unchecked")
public Set<Entry<String, Object>> entrySet() {
// Get values
List<Map<String, Object>> sqlResult = (List<Map<String, Object>>) getMapColumnRaw(getSqlTableID(), "name", "type", "value");
// Build result
Set<Entry<String, Object>> result = new HashSet<>();
// Fill hash set - iterate result
for (Map<String, Object> singleResult: sqlResult) {
// Deserialize value from string
Object value = SQLTableRow.getValueFromString(sqlRootElement, Integer.parseInt((String) singleResult.get("type")), (String) singleResult.get("value"));
// Build entry
Entry<String, Object> resultEntry = new AbstractMap.SimpleEntry<String, Object>((String) singleResult.get("name"), value);
// Add result entry to result
result.add(resultEntry);
}
// Return result
return result;
}
}