/**
* ClarescoExperienceAPI
* Copyright
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* Please contact Claresco, www.claresco.com, if you have any questions.
**/
package com.claresco.tinman.sql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.TimeZone;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import com.claresco.tinman.json.JsonUtility;
import com.claresco.tinman.lrs.XapiAgent;
import com.claresco.tinman.lrs.XapiContext;
import com.claresco.tinman.lrs.XapiGroup;
import com.claresco.tinman.lrs.XapiInverseFunctionalIdentifier;
import com.claresco.tinman.lrs.XapiObject;
import com.claresco.tinman.lrs.XapiResult;
import com.claresco.tinman.lrs.XapiStatement;
import com.claresco.tinman.lrs.XapiVerb;
import com.claresco.tinman.lrs.XapiActor;
import com.claresco.tinman.servlet.XapiBadParamException;
import com.google.gson.Gson;
/**
* XapiStatementSqlReader.java
*
* Read from database
*
*
*
* @author rheza
* on Feb 26, 2014
*
*/
class XapiStatementSQLReader extends SQLReader {
protected PreparedStatement myUUIDRetrievalStatement;
protected PreparedStatement myIDRetrievalStatement;
protected PreparedStatement myRetrievalByActorStatement;
protected PreparedStatement myRetrievalByActorAsObjectStatement;
protected PreparedStatement myRetrievalByVerbStatement;
protected PreparedStatement myVoidedRetrievalStatement;
private String myTableName = "statement";
private XapiActorSQLReader myActorReader;
private XapiVerbSQLReader myVerbReader;
private XapiObjectSQLReader myObjectReader;
private XapiContextSQLReader myContextReader;
private XapiResultSQLReader myResultReader;
/**
* Description:
* Constructor
*
* Params:
*
*/
public XapiStatementSQLReader(Connection conn, XapiActorSQLReader theActorReader,
XapiVerbSQLReader theVerbReader, XapiObjectSQLReader theObjectReader, XapiContextSQLReader
theContextReader, XapiResultSQLReader theResultReader) throws SQLException{
this.myConn = conn;
this.myUUIDRetrievalStatement = SQLUtility.createRetrievalStatement(super.myConn,
myTableName, new String[]{"statementuuid", "isvoided"});
this.myIDRetrievalStatement = SQLUtility.createRetrievalStatement(super.myConn,
myTableName, "statementid");
this.myRetrievalByActorStatement = SQLUtility.createRetrievalStatement(myConn,
myTableName, "actorid");
this.myRetrievalByVerbStatement = SQLUtility.createRetrievalStatement(myConn,
myTableName, "verbid");
this.myRetrievalByActorAsObjectStatement = createActorAsObjectStatement();
this.myVoidedRetrievalStatement = SQLUtility.createRetrievalStatement(myConn, myTableName,
new String[]{"statementid", "isvoided"});
this.myActorReader = theActorReader;
this.myVerbReader = theVerbReader;
this.myObjectReader = theObjectReader;
this.myContextReader = theContextReader;
this.myResultReader = theResultReader;
}
/**
*
* Description:
* Return a statement object from the database based on its uuid
*
* Params:
* theUUID
*/
protected HashMap<Integer, XapiStatement> retrieveByUUID(String theUUID) throws SQLException{
this.myUUIDRetrievalStatement.setString(1, theUUID);
this.myUUIDRetrievalStatement.setInt(2, 0);
myResult = this.myUUIDRetrievalStatement.executeQuery();
HashMap<Integer, XapiStatement> statementArray = getStatementFromResult();
assert statementArray.size() <= 1;
return statementArray;
}
protected HashMap<Integer, XapiStatement> retrieveByUUID(UUID theUUID) throws SQLException{
return this.retrieveByUUID(theUUID.toString());
}
/**
*
* Description:
* Retrieve a statement object from the database based on its uuid
*
* Params:
*
*/
protected HashMap<Integer, XapiStatement> retrieveByID(int theID) throws SQLException
, XapiDataIntegrityException{
this.myIDRetrievalStatement.setInt(1, theID);
myResult = this.myIDRetrievalStatement.executeQuery();
HashMap<Integer, XapiStatement> statementArray = getStatementFromResult();
if(statementArray.size() > 1){
throw new XapiDuplicateStatementIDException("something went wrong");
}
return statementArray;
}
protected boolean doesStatementExists(String theUUID) throws SQLException{
this.myUUIDRetrievalStatement.setString(1, theUUID);
this.myUUIDRetrievalStatement.setInt(2, 0);
myResult = this.myUUIDRetrievalStatement.executeQuery();
if (!myResult.isBeforeFirst() ) {
return false;
}
return true;
}
/**
*
* Definition:
* Retrieve Statement based on its UUID
*
* Params:
*
*
*/
protected int findIDByUUID(String theUUID) throws SQLException{
this.myUUIDRetrievalStatement.setString(1, theUUID);
this.myUUIDRetrievalStatement.setInt(2, 0);
myResult = this.myUUIDRetrievalStatement.executeQuery();
if(!myResult.isBeforeFirst()){
return -1;
}
myResult.next();
return myResult.getInt(1);
}
/**
*
* Definition:
* Retrieve statements based on its actor id
*
* Params:
*
*
*/
protected HashMap<Integer, XapiStatement> retrieveStatementByActor(int actorID) throws SQLException{
// Find statements where actor is a subject
myRetrievalByActorStatement.setInt(1, actorID);
myResult = myRetrievalByActorStatement.executeQuery();
HashMap<Integer, XapiStatement> statementMap = getStatementFromResult();
// Find statements where actor is an object
myRetrievalByActorAsObjectStatement.setInt(1, actorID);
myResult = myRetrievalByActorAsObjectStatement.executeQuery();
statementMap.putAll(getStatementFromResult());
// Find groups which the actor belongs to and then find statement which those groups are in
ArrayList<Integer> groupIDArray = myActorReader.retrieveGroupsOfAgent(actorID);
for(Integer i : groupIDArray){
statementMap.putAll(retrieveStatementByActor(i));
}
return statementMap;
}
/**
*
* Definition:
* Return statements whose verbs match the IRI
*
* Params:
*
*
*/
protected HashMap<Integer, XapiStatement> retrieveStatementByVerb(String theVerbIRI) throws SQLException{
int theVerbID = myVerbReader.retrieveIDByValue(theVerbIRI);
myRetrievalByVerbStatement.setInt(1, theVerbID);
myResult = myRetrievalByVerbStatement.executeQuery();
HashMap<Integer, XapiStatement> statementMap = getStatementFromResult();
return statementMap;
}
private HashMap<Integer, XapiStatement> retrieveVoidedStatementByID(String theUUID) throws SQLException{
myVoidedRetrievalStatement.setString(1, theUUID);
myVoidedRetrievalStatement.setInt(2, 1);
myResult = myVoidedRetrievalStatement.executeQuery();
HashMap<Integer, XapiStatement> myVoidedStatementMap = getStatementFromResult();
return myVoidedStatementMap;
}
/**
*
* Definition:
* Helper method that will create a hashmap from result
*
* Params:
*
*
*/
private HashMap<Integer, XapiStatement> getStatementFromResult() throws SQLException{
HashMap<Integer, XapiStatement> statementArray = new HashMap<Integer, XapiStatement>();
while(myResult.next()){
UUID theId = UUID.fromString(myResult.getString("statementuuid"));
XapiActor theActor = myActorReader.retrieveByID(myResult.getInt("actorid"));
XapiVerb theVerb = myVerbReader.retrieveByID(myResult.getInt("verbid"));
XapiObject theObject = myObjectReader.retrieveByID(myResult.getInt("objectid"));
XapiResult theResult = null;
int theResultID = myResult.getInt("resultid");
if(!myResult.wasNull()){
theResult = myResultReader.retrieveByID(theResultID);
}
XapiContext theContext = null;
int theContextID = myResult.getInt("contextid");
if(!myResult.wasNull()){
theContext = myContextReader.retrieveByID(theContextID);
}
String theTSString = null;
Timestamp theTS = myResult.getTimestamp("sttime");
if(!myResult.wasNull()){
DateTime theTimestamp = SQLUtility.getDatetime(theTS);
theTSString = theTimestamp.withZoneRetainFields(DateTimeZone.UTC).toString();
}
// Voided statement should not be returned
int isVoided = myResult.getInt("isVoided");
if(isVoided == 0){
statementArray.put(myResult.getInt("statementid") ,new XapiStatement(theId, theActor,
theVerb, theObject, theResult, theContext, theTSString));
}
}
return statementArray;
}
protected HashMap<Integer, XapiStatement> handleConjuctionQuery(HashMap<String, String> myParamMap) throws SQLException,
XapiDataIntegrityException, XapiSQLOperationProblemException{
if(myParamMap.size() == 0){
throw new XapiConflictingParamException("There is no parameter");
}
if(myParamMap.containsKey("statementId") && myParamMap.size() > 1){
throw new XapiConflictingParamException("Other parameters are not suitable with statementId");
}else if(myParamMap.containsKey("voidedStatementId") && myParamMap.size() > 1){
throw new XapiConflictingParamException("Other parameters are not suitable with voidedStatementId");
}else if(myParamMap.containsKey("statementId") && myParamMap.containsKey("voidedStatementId")){
throw new XapiConflictingParamException("Can't have both statementId and voidedStatementId");
}else if(myParamMap.containsKey("statementId") && myParamMap.size() == 1){
return retrieveByUUID(myParamMap.get("statementId"));
}else if(myParamMap.containsKey("voidedStatementId") && myParamMap.size() == 1){
return retrieveVoidedStatementByID(myParamMap.get("voidedStatementId"));
}else{
ArrayList<Integer> myStatementIDs = retrieveStatementsByParameter(myParamMap);
HashMap<Integer, XapiStatement> myResultMap = new HashMap<Integer, XapiStatement>();
if(myStatementIDs == null){
return null;
}
for(Integer i : myStatementIDs){
myResultMap.put(i, retrieveByID(i.intValue()).get(i));
}
return myResultMap;
}
}
private ArrayList<Integer> retrieveStatementsByParameter(HashMap<String, String> paramMaps)
throws SQLException, XapiDataIntegrityException, XapiSQLOperationProblemException{
String topHalf = "select st.statementid from statement st";
String bottomHalf = "where";
// This is the conjuction query
int i = 1;
HashMap<String, Integer> positionMap = new HashMap<String, Integer>();
for(String s : paramMaps.keySet()){
if(s.equals("statementId")){
bottomHalf += "st.statementid = ?";
positionMap.put(s, new Integer(i));
i++;
}else if(s.equals("agent")){
topHalf += createTopHalfString("actor", "a", "actorid");
bottomHalf += createBottomHalfString("a", "actorid");
positionMap.put(s, new Integer(i));
i++;
}else if(s.equals("verb")){
topHalf += createTopHalfString("verb", "v", "verbid");
bottomHalf += createBottomHalfString("v", "verbiri");
positionMap.put(s, new Integer(i));
i++;
}else if(s.equals("activity")){
topHalf += createTopHalfString("object", "obj", "objectid");
topHalf += createTopHalfString("activity", "actv", "activityid");
bottomHalf += createBottomHalfString("actv", "actviri");
positionMap.put(s, new Integer(i));
i++;
}else if(s.equals("since")){
bottomHalf += createBottomHalfString("st", "ststored", ">");
positionMap.put(s, new Integer(i));
i++;
}else if(s.equals("until")){
bottomHalf += createBottomHalfString("st", "ststored", "<");
positionMap.put(s, new Integer(i));
i++;
}
}
bottomHalf += createBottomHalfString("st", "isvoided");
positionMap.put("isvoided", i);
bottomHalf = bottomHalf.replace("where and", "where");
// Always order by stored time in desceding onder
String ending = " order by ststored desc;";
String fullString = topHalf + " " + bottomHalf + ending;
return executeConjQueryStatement(paramMaps, positionMap, fullString);
}
private String createTopHalfString(String tableName, String tableNickname, String fieldName){
String theString = " left join %s %s using (%s)";
return String.format(theString, tableName, tableNickname, fieldName);
}
private String createBottomHalfString(String tableNickname, String fieldName){
String theS = " and %s.%s = ?";
return String.format(theS, tableNickname, fieldName);
}
private String createBottomHalfString(String tableNickname, String fieldName, String operator){
String theS = " and %s.%s %s ?";
return String.format(theS, tableNickname, fieldName, operator);
}
private ArrayList<Integer> executeConjQueryStatement(HashMap<String, String> paramMaps,
HashMap<String, Integer> locationMaps, String conjQueryStatement) throws SQLException,
XapiSQLOperationProblemException{
PreparedStatement myConjQueryStatement = myConn.prepareStatement(conjQueryStatement);
DateTimeFormatter theFormatter = ISODateTimeFormat.dateTimeParser();
for(String paramName : paramMaps.keySet()){
String paramValue = paramMaps.get(paramName);
int locationIndex = locationMaps.get(paramName).intValue();
if(paramName.equals("statementId")){
myConjQueryStatement.setString(locationIndex, paramValue);
}else if(paramName.equals("agent")){
myConjQueryStatement.setInt(locationIndex, Integer.parseInt(paramValue));
}else if(paramName.equals("verb")){
myConjQueryStatement.setString(locationIndex, paramValue);
}else if(paramName.equals("activity")){
myConjQueryStatement.setString(locationIndex, paramValue);
}else if(paramName.equals("since")){
try{
DateTime myTimeStamp = theFormatter.parseDateTime(paramValue);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
myConjQueryStatement.setTimestamp(locationIndex, SQLUtility.getTimestamp(myTimeStamp), cal);
}catch(IllegalArgumentException exc){
throw new XapiSQLOperationProblemException("Having trouble reading the timestamp");
}
}else if(paramName.equals("until")){
try{
DateTime myTimeStamp = theFormatter.parseDateTime(paramValue);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
myConjQueryStatement.setTimestamp(locationIndex, SQLUtility.getTimestamp(myTimeStamp), cal);
}catch(IllegalArgumentException exc){
throw new XapiSQLOperationProblemException("Having trouble parsing the timestamp");
}
}
}
myConjQueryStatement.setInt(locationMaps.get("isvoided").intValue(), 0);
myResult = myConjQueryStatement.executeQuery();
ArrayList<Integer> arrayIDs = new ArrayList<Integer>();
while(myResult.next()){
arrayIDs.add(new Integer(myResult.getInt("statementid")));
}
if(arrayIDs.isEmpty()){
return null;
}
return arrayIDs;
}
/**
*
* Definition:
* Helper method to create statement to retrieve actor as object
*
* Params:
*
*
*/
private PreparedStatement createActorAsObjectStatement() throws SQLException{
String theString = "select * from statement where objectid in (select objectid from object where " +
"actorid = ?)";
return super.myConn.prepareStatement(theString);
}
/**
* Description:
* Close everything makes everything safe
*/
protected void close() throws SQLException{
super.close();
SQLUtility.closeStatement(this.myIDRetrievalStatement);
SQLUtility.closeStatement(this.myUUIDRetrievalStatement);
SQLUtility.closeStatement(myRetrievalByActorAsObjectStatement);
SQLUtility.closeStatement(myRetrievalByActorStatement);
SQLUtility.closeStatement(myRetrievalByVerbStatement);
}
public static void main(String[] args) {
try {
} catch (Exception e) {
e.printStackTrace();
}
}
}