/*
* Copyright (C) 2014 Divide.io
*
* 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 io.divide.client.cache;
import com.google.inject.Inject;
import iBoxDB.LocalServer.AutoBox;
import iBoxDB.LocalServer.DB;
import iBoxDB.LocalServer.E.CommitExpection;
import iBoxDB.LocalServer.IFunction;
import io.divide.client.Config;
import io.divide.shared.server.DAO;
import io.divide.shared.transitory.TransientObject;
import io.divide.shared.transitory.query.Clause;
import io.divide.shared.transitory.query.OPERAND;
import io.divide.shared.transitory.query.Query;
import io.divide.shared.util.ObjectUtils;
import java.io.File;
import java.util.*;
import static io.divide.shared.util.ObjectUtils.isArray;
public class LocalStorageIBoxDb<T1 extends TransientObject,T2 extends TransientObject> implements DAO<T1,T2> {
//#from <table> where <condition> order by <field1> desc,<field2> limit <0,-1> #Condition == != < <= > >= & | #IFunction=[F1,F2,F3]
DB db;
AutoBox box;
@Inject
public LocalStorageIBoxDb(Config config){
this(config.fileSavePath + "dbs");
}
public LocalStorageIBoxDb(String path){
System.out.println("iBox: " + path);
File f = new File(path);
f.mkdirs();
db = new DB(path);
// db.getConfig().ensureTable("Wrapper", Wrapper.class, "Key", "Table");
db.ensureTable(Wrapper.class, "Wrapper", "Key", "Table");
box = db.open();
}
@Override
public void save(T1... objects) throws DAOException {
for(TransientObject b : objects){
try {
boolean exists = exists(b);
System.out.println("Exists: " + exists);
if(exists) {
System.out.println("update: "+ b.getObjectKey());
box.update("Wrapper", new Wrapper(b));
} else {
System.out.println("insert: " + b.getObjectKey());
box.insert("Wrapper", new Wrapper(b));
}
} catch (CommitExpection e){
e.printStackTrace();
throw new DAOException(e);
}
}
}
@Override
public void delete(T1... objects) throws DAOException {
String[] keys = new String[objects.length];
for(int x=0;x<objects.length;x++){
keys[x]=objects[x].getObjectKey();
}
Iterable<Wrapper> x = internalGet(objects[0].getObjectType(), keys);
for(Wrapper wr : x){
box.delete("Wrapper", wr);
}
// String[] keys = new String[objects.length];
// for(int x=0;x<objects.length;x++){
// keys[x]=objects[x].getObjectKey();
// }
//
// String table = objects[0].getObjectType();
//
// Iterable<Wrapper> toDelete = internalGet(table, keys);
//
// box.delete("Wrapper", ObjectUtils.toArray(Wrapper.class, toDelete));
}
@Override
public boolean exists(TransientObject... objects) {
if(objects.length == 0) return false;
boolean found = true;
for(TransientObject o : objects){
Wrapper o1 = iBoxUtils.GetFrist(box.select(Wrapper.class,"from Wrapper where Key==? && Table==?", o.getObjectKey(), o.getObjectType() ));
if(o1 == null) found = false;
}
return found;
}
@Override
public int count(String type) {
return (int) box.selectCount("from Wrapper where Table==?", type);
}
@Override
public <B extends T2> List<B> query(Query query) {
Class<B> type = null;
try {
type = (Class<B>) Class.forName(Query.reverseTable(query.getFrom()));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String table = query.getFrom();
Map<Integer,Clause> where = query.getWhere();
List<Object> args = new ArrayList<Object>(); args.add(table);
StringBuilder sb = new StringBuilder();
sb.append("from Wrapper where Table==? ");
if(where.size()>0)
{
sb.append("&& (");
for(Clause c : where.values()){
// System.out.println(c);
if(c.getPreOperator() != null && c.getPreOperator().length() != 0){
// System.out.println("Conditional: " + c.getPreOperator());
OPERAND.Conditional con = OPERAND.Conditional.from(c.getPreOperator());
// System.out.println("Conditional: " + con);
String conString="";
switch (con){
case AND: conString = "||"; break;
case OR: conString = "&&"; break;
}
sb.append(" ").append(conString).append(" [").append(c.getBefore()).append("]");
}
else
sb.append(" [").append(c.getBefore()).append("]");
args.add(new QueryArray(c.getOperand(),c.getAfter()));
}
// for(Clause c : where.values()){
// sb
// .append(" ")
// .append(c.getPreOperator())
// .append(" ")
// .append(c.getBefore())
// .append(" ")
// .append(c.getOperand())
// .append(" ")
// .append(c.getAfter());
// }
sb.append(')');
}
if(query.getLimit() != null){
sb.append(" limit " + "0," + query.getLimit());
}
// System.out.println("Query: " + sb.toString());
// System.out.println("Args: " + args);
Iterable<Wrapper> list = box.select(Wrapper.class,sb.toString(),args.toArray());
// System.out.println("Found: " + list);
List<B> bList = new ArrayList<B>();
for(Wrapper w : list){
// System.out.println(w);
bList.add(w.toObject(type));
}
return bList;
}
@Override
public <O extends T2> Collection<O> get(String type, String... keys) throws DAOException {
System.out.println("get("+type+"): " + ObjectUtils.v2c(keys));
Class<O> clazz = null;
try {
clazz = (Class<O>) Class.forName(Query.reverseTable(type));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
List<O> list = new ArrayList<O>();
for(Wrapper w : internalGet(type,keys)) {
list.add(w.toObject(clazz));
}
return list;
}
private Iterable<Wrapper> internalGet(String type, String... keys){
StringBuilder query = new StringBuilder();
query.append("from Wrapper where Table==? && (");
List<String> args = new ArrayList<String>(); args.add(type);
for(int x=0;x<keys.length;x++){
args.add(keys[x]);
query.append("Key==?");
if(x<keys.length - 1) query.append(" || ");
}
query.append(')');
Iterable<Wrapper> x = box.select(Wrapper.class, query.toString(), args.toArray());
return x;
}
public static class iBoxUtils{
public static <T> T GetFrist(Iterable<T> list) {
for (T o : list) {
return o;
}
return null;
}
}
public static class QueryArray implements IFunction {
private OPERAND operand;
private String match;
public QueryArray(String operand, String match) {
this.operand = OPERAND.from(operand);
// System.out.println("Operand: " + operand);
this.match = match;
}
public Object execute(int argCount, Object[] args) {
// System.out.println("execute: " + Arrays.asList(args));
if(compare(args[0])) return true; // since we use this for all queries, check against the match first, then check against embedded arrays
if(!isArray(args[0])) return false;
if (args[0] == null) {
return false;
}
Object[] tags = (Object[]) args[0];
for (Object t : tags) {
// System.out.println("Tag: " + t);
if(compare(t))return true;
}
return false;
}
private boolean compare(Object one){
int result = match.compareToIgnoreCase(String.valueOf(one));
// System.out.println(one + " " + operand + " " + match + ": " + result);
switch (operand){
case CONTAINS:
case EQ: return result == 0;
case GREATER_THAN: return result < 0;
case GREATER_THAN_EQ: return (result < 0 || result == 0);
case LESS_THAN: return result > 0;
case LESS_THAN_EQ: return (result > 0 || result == 0);
}
return false;
}
// public Object execute(int argCount, Object[] args) {
// return recursiveCheck(args,match);
// }
//
// private static boolean recursiveCheck(Object object, Object target){
// if(object == null) return false;
// if(object.equals(target)) return true;
// if(!isArray(object))return false;
// else {
// Object[] tags = (Object[]) object;
// for(Object o : tags){
// if(recursiveCheck(o,target)) return true;
// }
// }
// return false;
// }
@Override
public String toString() {
return "QueryArray{" +
"match" + operand + match + '\'' +
'}';
}
}
}