Adding in the development version of the new data store system.
This commit is contained in:
parent
541c6d2e0a
commit
2bca9a6572
530
src/com/massivecraft/mcore4/store/Coll.java
Normal file
530
src/com/massivecraft/mcore4/store/Coll.java
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.MPlugin;
|
||||||
|
import com.massivecraft.mcore4.Predictate;
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategy;
|
||||||
|
import com.massivecraft.mcore4.store.storeadapter.StoreAdapter;
|
||||||
|
|
||||||
|
public class Coll<E, L> implements CollInterface<E, L>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// WHAT DO WE HANDLE?
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected String name;
|
||||||
|
@Override public String name() { return this.name; }
|
||||||
|
|
||||||
|
protected Class<E> entityClass;
|
||||||
|
@Override public Class<E> entityClass() { return this.entityClass; }
|
||||||
|
|
||||||
|
protected Class<L> idClass;
|
||||||
|
@Override public Class<L> idClass() { return this.idClass; }
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SUPPORTING SYSTEM
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected MPlugin mplugin;
|
||||||
|
@Override public MPlugin mplugin() { return this.mplugin; }
|
||||||
|
|
||||||
|
protected Db<?> db;
|
||||||
|
@Override public Db<?> db() { return this.db; }
|
||||||
|
@Override public Driver<?> driver() { return this.db.driver(); }
|
||||||
|
|
||||||
|
protected IdStrategy<L, ?> idStrategy;
|
||||||
|
@Override public IdStrategy<L, ?> idStrategy() { return this.idStrategy; }
|
||||||
|
|
||||||
|
protected StoreAdapter storeAdapter;
|
||||||
|
@Override public StoreAdapter storeAdapter() { return this.storeAdapter; }
|
||||||
|
|
||||||
|
protected Object collDriverObject;
|
||||||
|
@Override public Object collDriverObject() { return this.collDriverObject; }
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// STORAGE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected Set<L> ids = Collections.newSetFromMap(new ConcurrentHashMap<L, Boolean>());
|
||||||
|
@Override public Collection<L> ids() { return Collections.unmodifiableCollection(this.ids); }
|
||||||
|
@Override public Collection<L> idsRemote() { return this.db().driver().ids(this); }
|
||||||
|
|
||||||
|
protected Set<E> entities = Collections.newSetFromMap(new ConcurrentHashMap<E, Boolean>());
|
||||||
|
@Override public Collection<E> getAll() { return Collections.unmodifiableCollection(this.entities); }
|
||||||
|
@Override public Collection<E> getAll(Predictate<E> where) { return MStoreUtil.uglySQL(this.getAll(), where, null, null, null); }
|
||||||
|
@Override public Collection<E> getAll(Predictate<E> where, Comparator<E> orderby) { return MStoreUtil.uglySQL(this.getAll(), where, orderby, null, null); }
|
||||||
|
@Override public Collection<E> getAll(Predictate<E> where, Comparator<E> orderby, Integer limit) { return MStoreUtil.uglySQL(this.getAll(), where, orderby, limit, null); }
|
||||||
|
@Override public Collection<E> getAll(Predictate<E> where, Comparator<E> orderby, Integer limit, Integer offset) { return MStoreUtil.uglySQL(this.getAll(), where, orderby, limit, offset); }
|
||||||
|
|
||||||
|
protected Map<L, E> id2entity = new ConcurrentHashMap<L, E>();
|
||||||
|
@Override public Map<L, E> id2entity() { return Collections.unmodifiableMap(this.id2entity); }
|
||||||
|
@Override
|
||||||
|
public E get(Object oid)
|
||||||
|
{
|
||||||
|
return this.get(oid, this.creative());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public E get(Object oid, boolean creative)
|
||||||
|
{
|
||||||
|
return this.get(oid, creative, true);
|
||||||
|
}
|
||||||
|
protected E get(Object oid, boolean creative, boolean noteChange)
|
||||||
|
{
|
||||||
|
L id = this.idFix(oid);
|
||||||
|
if (id == null) return null;
|
||||||
|
E ret = this.id2entity.get(id);
|
||||||
|
if (ret != null) return ret;
|
||||||
|
if ( ! creative) return null;
|
||||||
|
return this.create(id, noteChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E getBestMatch(Object oid)
|
||||||
|
{
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the id for this entity.
|
||||||
|
protected Map<E, L> entity2id = new ConcurrentHashMap<E, L>();
|
||||||
|
@Override public Map<E, L> entity2id() { return Collections.unmodifiableMap(this.entity2id); }
|
||||||
|
@Override public L id(E entity) { return this.entity2id.get(entity); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public L idFix(Object oid)
|
||||||
|
{
|
||||||
|
if (oid == null) return null;
|
||||||
|
if (oid.getClass() == this.idClass) return this.idClass.cast(oid);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// BAHAVIOR
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected boolean creative;
|
||||||
|
@Override public boolean creative() { return this.creative; }
|
||||||
|
@Override public void creative(boolean val) { this.creative = val; }
|
||||||
|
|
||||||
|
// Should that instance be saved or not?
|
||||||
|
// If it is default it should not be saved.
|
||||||
|
@Override public boolean isDefault(E entity) { return false; }
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// CREATE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
// This simply creates and returns a new instance
|
||||||
|
// It does not detach/attach or anything. Just creates a new instance.
|
||||||
|
@Override
|
||||||
|
public E createNewInstance()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return this.entityClass.newInstance();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new instance with automatic id
|
||||||
|
@Override
|
||||||
|
public E create()
|
||||||
|
{
|
||||||
|
return this.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new instance with the requested id
|
||||||
|
@Override
|
||||||
|
public synchronized E create(Object oid)
|
||||||
|
{
|
||||||
|
return this.create(oid, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized E create(Object oid, boolean noteChange)
|
||||||
|
{
|
||||||
|
E entity = this.createNewInstance();
|
||||||
|
if (this.attach(entity, oid, noteChange) == null) return null;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// ATTACH AND DETACH
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public L attach(E entity)
|
||||||
|
{
|
||||||
|
return this.attach(entity, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized L attach(E entity, Object oid)
|
||||||
|
{
|
||||||
|
return this.attach(entity, oid, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized L attach(E entity, Object oid, boolean noteChange)
|
||||||
|
{
|
||||||
|
// Check entity
|
||||||
|
if (entity == null) return null;
|
||||||
|
L id = this.id(entity);
|
||||||
|
if (id != null) return id;
|
||||||
|
|
||||||
|
// Check/Fix id
|
||||||
|
if (oid == null)
|
||||||
|
{
|
||||||
|
id = this.idStrategy().generate(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
id = this.idFix(oid);
|
||||||
|
if (id == null) return null;
|
||||||
|
if (this.ids.contains(id)) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach
|
||||||
|
this.ids.add(id);
|
||||||
|
this.entities.add(entity);
|
||||||
|
this.id2entity.put(id, entity);
|
||||||
|
this.entity2id.put(entity, id);
|
||||||
|
|
||||||
|
// Make note of the change
|
||||||
|
if (noteChange)
|
||||||
|
{
|
||||||
|
this.localAttachIds.add(id);
|
||||||
|
this.changedIds.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public synchronized E detach(Object o)
|
||||||
|
{
|
||||||
|
// What id is this?
|
||||||
|
L id = null;
|
||||||
|
if (this.idClass.isInstance(o))
|
||||||
|
{
|
||||||
|
id = (L)o;
|
||||||
|
}
|
||||||
|
else if (this.entityClass.isInstance(o))
|
||||||
|
{
|
||||||
|
id = this.entity2id.get(o);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
id = this.idFix(o);
|
||||||
|
}
|
||||||
|
if (id == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove @ local
|
||||||
|
E ret = this.removeAtLocal(id);
|
||||||
|
|
||||||
|
// Identify the change
|
||||||
|
this.localDetachIds.add(id);
|
||||||
|
this.changedIds.add(id);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IDENTIFIED CHANGES
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected Set<L> localAttachIds = Collections.newSetFromMap(new ConcurrentHashMap<L, Boolean>());
|
||||||
|
protected Set<L> localDetachIds = Collections.newSetFromMap(new ConcurrentHashMap<L, Boolean>());
|
||||||
|
protected Set<L> changedIds = Collections.newSetFromMap(new ConcurrentHashMap<L, Boolean>());
|
||||||
|
|
||||||
|
protected synchronized void clearIdentifiedChanges(L id)
|
||||||
|
{
|
||||||
|
this.localAttachIds.remove(id);
|
||||||
|
this.localDetachIds.remove(id);
|
||||||
|
this.changedIds.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SYNCLOG
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected Map<L, Long> lastMtime = new ConcurrentHashMap<L, Long>();
|
||||||
|
protected Map<L, Object> lastRaw = new ConcurrentHashMap<L, Object>();
|
||||||
|
protected Set<L> lastDefault = Collections.newSetFromMap(new ConcurrentHashMap<L, Boolean>());
|
||||||
|
|
||||||
|
protected synchronized void clearSynclog(L id)
|
||||||
|
{
|
||||||
|
this.lastMtime.remove(id);
|
||||||
|
this.lastRaw.remove(id);
|
||||||
|
this.lastDefault.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SYNC LOWLEVEL IO ACTIONS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized E removeAtLocal(L id)
|
||||||
|
{
|
||||||
|
this.clearIdentifiedChanges(id);
|
||||||
|
this.clearSynclog(id);
|
||||||
|
|
||||||
|
this.ids.remove(id);
|
||||||
|
E entity = this.id2entity.remove(id);
|
||||||
|
if (entity == null) return null;
|
||||||
|
|
||||||
|
this.entity2id.remove(entity);
|
||||||
|
this.entities.remove(entity);
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void removeAtRemote(L id)
|
||||||
|
{
|
||||||
|
this.clearIdentifiedChanges(id);
|
||||||
|
this.clearSynclog(id);
|
||||||
|
|
||||||
|
this.db().driver().delete(this, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void saveToRemote(L id)
|
||||||
|
{
|
||||||
|
this.clearIdentifiedChanges(id);
|
||||||
|
this.clearSynclog(id);
|
||||||
|
|
||||||
|
E entity = this.id2entity.get(id);
|
||||||
|
if (entity == null) return;
|
||||||
|
|
||||||
|
Object raw = this.storeAdapter().read(this, entity);
|
||||||
|
this.lastRaw.put(id, raw);
|
||||||
|
|
||||||
|
if (this.isDefault(entity))
|
||||||
|
{
|
||||||
|
this.db.driver().delete(this, id);
|
||||||
|
this.lastDefault.add(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Long mtime = this.db.driver().save(this, id, raw);
|
||||||
|
if (mtime == null) return; // This fail should not happen often. We could handle it better though.
|
||||||
|
this.lastMtime.put(id, mtime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void loadFromRemote(L id)
|
||||||
|
{
|
||||||
|
this.clearIdentifiedChanges(id);
|
||||||
|
|
||||||
|
Entry<?, Long> entry = this.db().driver().load(this, id);
|
||||||
|
if (entry == null) return;
|
||||||
|
|
||||||
|
Object raw = entry.getKey();
|
||||||
|
if (raw == null) return;
|
||||||
|
|
||||||
|
Long mtime = entry.getValue();
|
||||||
|
if (mtime == null) return;
|
||||||
|
|
||||||
|
E entity = this.get(id, true, false);
|
||||||
|
|
||||||
|
this.storeAdapter().write(this, raw, entity);
|
||||||
|
|
||||||
|
// Store adapter again since result of a database read may be "different" from entity read.
|
||||||
|
this.lastRaw.put(id, this.storeAdapter().read(this, entity));
|
||||||
|
this.lastMtime.put(id, mtime);
|
||||||
|
this.lastDefault.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SYNC DECIDE AND BASIC DO
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModificationState examineId(L id)
|
||||||
|
{
|
||||||
|
return this.examineId(id, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModificationState examineId(L id, Long remoteMtime)
|
||||||
|
{
|
||||||
|
return this.examineId(id, remoteMtime, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ModificationState examineId(L id, Long remoteMtime, boolean remoteMtimeSupplied)
|
||||||
|
{
|
||||||
|
if (this.localDetachIds.contains(id)) return ModificationState.LOCAL_DETACH;
|
||||||
|
if (this.localAttachIds.contains(id)) return ModificationState.LOCAL_ATTACH;
|
||||||
|
|
||||||
|
E localEntity = this.id2entity.get(id);
|
||||||
|
if ( ! remoteMtimeSupplied)
|
||||||
|
{
|
||||||
|
remoteMtime = this.driver().mtime(this, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean existsLocal = (localEntity != null);
|
||||||
|
boolean existsRemote = (remoteMtime != null);
|
||||||
|
|
||||||
|
if (existsLocal && existsRemote)
|
||||||
|
{
|
||||||
|
Long lastMtime = this.lastMtime.get(id);
|
||||||
|
if (remoteMtime.equals(lastMtime) == false) return ModificationState.REMOTE_ALTER;
|
||||||
|
|
||||||
|
Object lastRaw = this.lastRaw.get(id);
|
||||||
|
Object currentRaw = this.storeAdapter.read(this, localEntity);
|
||||||
|
if (currentRaw.equals(lastRaw) == false) return ModificationState.LOCAL_ALTER;
|
||||||
|
}
|
||||||
|
else if (existsLocal)
|
||||||
|
{
|
||||||
|
if ( ! this.lastDefault.contains(id)) return ModificationState.REMOTE_DETACH;
|
||||||
|
}
|
||||||
|
else if (existsRemote)
|
||||||
|
{
|
||||||
|
return ModificationState.REMOTE_ATTACH;
|
||||||
|
}
|
||||||
|
else if ( ! existsLocal && ! existsRemote)
|
||||||
|
{
|
||||||
|
return ModificationState.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModificationState.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void syncId(L id)
|
||||||
|
{
|
||||||
|
ModificationState mstate = this.examineId(id);
|
||||||
|
|
||||||
|
//System.out.println("syncId \""+id+"\" "+mstate);
|
||||||
|
|
||||||
|
switch (mstate)
|
||||||
|
{
|
||||||
|
case LOCAL_ALTER:
|
||||||
|
case LOCAL_ATTACH:
|
||||||
|
this.saveToRemote(id);
|
||||||
|
break;
|
||||||
|
case LOCAL_DETACH:
|
||||||
|
this.removeAtRemote(id);
|
||||||
|
break;
|
||||||
|
case REMOTE_ALTER:
|
||||||
|
case REMOTE_ATTACH:
|
||||||
|
this.loadFromRemote(id);
|
||||||
|
break;
|
||||||
|
case REMOTE_DETACH:
|
||||||
|
this.removeAtLocal(id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.clearIdentifiedChanges(id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void syncSuspects()
|
||||||
|
{
|
||||||
|
for (L id : this.changedIds)
|
||||||
|
{
|
||||||
|
this.syncId(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void syncAll()
|
||||||
|
{
|
||||||
|
// Find all ids
|
||||||
|
Set<L> allids = new HashSet<L>(this.ids);
|
||||||
|
allids.addAll(this.driver().ids(this));
|
||||||
|
for (L id : allids)
|
||||||
|
{
|
||||||
|
this.syncId(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findSuspects()
|
||||||
|
{
|
||||||
|
// Get remote id and mtime snapshot
|
||||||
|
Map<L, Long> id2RemoteMtime = this.db().driver().id2mtime(this);
|
||||||
|
|
||||||
|
// Compile a list of all ids (both remote and local)
|
||||||
|
Set<L> allids = new HashSet<L>();
|
||||||
|
allids.addAll(id2RemoteMtime.keySet());
|
||||||
|
allids.addAll(this.ids);
|
||||||
|
|
||||||
|
// Check for modifications
|
||||||
|
for (L id : allids)
|
||||||
|
{
|
||||||
|
Long remoteMtime = id2RemoteMtime.get(id);
|
||||||
|
ModificationState state = this.examineId(id, remoteMtime);
|
||||||
|
if (state.modified())
|
||||||
|
{
|
||||||
|
//System.out.println("It seems "+id+" has state "+state);
|
||||||
|
this.changedIds.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SYNC RUNNABLES / SCHEDULING
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected Runnable tickTask;
|
||||||
|
@Override public Runnable tickTask() { return this.tickTask; }
|
||||||
|
@Override
|
||||||
|
public void onTick()
|
||||||
|
{
|
||||||
|
this.syncSuspects();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExamineThread<E, L> examineThread;
|
||||||
|
@Override public Thread examineThread() { return this.examineThread; }
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// CONSTRUCTORS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public Coll(MPlugin mplugin, Db<?> db, String idStrategyName, String name, Class<E> entityClass, Class<L> idClass, boolean creative)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.entityClass = entityClass;
|
||||||
|
this.idClass = idClass;
|
||||||
|
this.creative = creative;
|
||||||
|
|
||||||
|
this.mplugin = mplugin;
|
||||||
|
this.db = db;
|
||||||
|
this.storeAdapter = this.db.driver().getStoreAdapter();
|
||||||
|
this.idStrategy = this.db.driver().getIdStrategy(idStrategyName);
|
||||||
|
if (this.idStrategy == null)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("UNKNOWN: The id stragegy \""+idStrategyName+"\" is unknown to the driver \""+db.driver().name()+"\".");
|
||||||
|
}
|
||||||
|
else if (this.idStrategy.getLocalClass() != idClass)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("MISSMATCH: The id stragegy \""+idStrategyName+"\" for the driver \""+db.driver().name()+"\" uses \""+this.idStrategy.getLocalClass().getSimpleName()+"\" but the collection "+this.name+"/"+this.getClass().getSimpleName()+" uses \""+idClass.getSimpleName()+"\".");
|
||||||
|
}
|
||||||
|
this.collDriverObject = db.getCollDriverObject(this);
|
||||||
|
|
||||||
|
final Coll<E, L> me = this;
|
||||||
|
this.tickTask = new Runnable()
|
||||||
|
{
|
||||||
|
@Override public void run() { me.onTick(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
this.examineThread = new ExamineThread<E, L>(this);
|
||||||
|
}
|
||||||
|
}
|
137
src/com/massivecraft/mcore4/store/CollInterface.java
Normal file
137
src/com/massivecraft/mcore4/store/CollInterface.java
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.MPlugin;
|
||||||
|
import com.massivecraft.mcore4.Predictate;
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategy;
|
||||||
|
import com.massivecraft.mcore4.store.storeadapter.StoreAdapter;
|
||||||
|
|
||||||
|
public interface CollInterface<E, L>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// WHAT DO WE HANDLE?
|
||||||
|
// -------------------------------------------- //
|
||||||
|
public String name();
|
||||||
|
public Class<E> entityClass();
|
||||||
|
public Class<L> idClass();
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SUPPORTING SYSTEM
|
||||||
|
// -------------------------------------------- //
|
||||||
|
public MPlugin mplugin();
|
||||||
|
|
||||||
|
public Db<?> db();
|
||||||
|
public Driver<?> driver();
|
||||||
|
public StoreAdapter storeAdapter();
|
||||||
|
public IdStrategy<L, ?> idStrategy();
|
||||||
|
public Object collDriverObject();
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// STORAGE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
public Collection<L> ids();
|
||||||
|
public Collection<L> idsRemote();
|
||||||
|
|
||||||
|
public Collection<E> getAll();
|
||||||
|
public Collection<E> getAll(Predictate<E> where);
|
||||||
|
public Collection<E> getAll(Predictate<E> where, Comparator<E> orderby);
|
||||||
|
public Collection<E> getAll(Predictate<E> where, Comparator<E> orderby, Integer limit);
|
||||||
|
public Collection<E> getAll(Predictate<E> where, Comparator<E> orderby, Integer limit, Integer offset);
|
||||||
|
|
||||||
|
public Map<L, E> id2entity();
|
||||||
|
public E get(Object oid);
|
||||||
|
public E get(Object oid, boolean creative);
|
||||||
|
public E getBestMatch(Object oid);
|
||||||
|
|
||||||
|
public Map<E, L> entity2id();
|
||||||
|
public L id(E entity);
|
||||||
|
public L idFix(Object oid);
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// BAHAVIOR
|
||||||
|
// -------------------------------------------- //
|
||||||
|
public boolean creative();
|
||||||
|
public void creative(boolean val);
|
||||||
|
|
||||||
|
// A default entity will not be saved.
|
||||||
|
// This is often used together with creative collections to save disc space.
|
||||||
|
public boolean isDefault(E entity);
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// CREATE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
// This simply creates and returns a new instance
|
||||||
|
// It does not detach/attach or anything. Just creates a new instance.
|
||||||
|
public E createNewInstance();
|
||||||
|
|
||||||
|
// Create new instance with automatic id
|
||||||
|
public E create();
|
||||||
|
// Create new instance with the requested id
|
||||||
|
public E create(Object oid);
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// ATTACH AND DETACH
|
||||||
|
// -------------------------------------------- //
|
||||||
|
public L attach(E entity);
|
||||||
|
public L attach(E entity, Object oid);
|
||||||
|
public E detach(Object o);
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IDENTIFIED CHANGES
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
/*
|
||||||
|
public Set<L> localAttachIds();
|
||||||
|
public Set<L> localDetachIds();
|
||||||
|
public Set<L> changedIds();
|
||||||
|
public void clearIdentifiedChanges(L id);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SYNC LOG
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
/*
|
||||||
|
public Map<L, Long> lastMtime();
|
||||||
|
public Map<L, Object> lastRaw();
|
||||||
|
public Set<L> lastDefault();
|
||||||
|
public void clearSynclog(L id);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SYNC LOWLEVEL IO ACTIONS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public E removeAtLocal(L id);
|
||||||
|
public void removeAtRemote(L id);
|
||||||
|
public void saveToRemote(L id);
|
||||||
|
public void loadFromRemote(L id);
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SYNC EXAMINE AND DO
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public ModificationState examineId(L id);
|
||||||
|
public ModificationState examineId(L id, Long remoteMtime);
|
||||||
|
|
||||||
|
public void syncId(L id);
|
||||||
|
public void syncSuspects();
|
||||||
|
public void syncAll();
|
||||||
|
public void findSuspects();
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// SYNC RUNNABLES / SCHEDULING
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
// The tickTask simply runs the onTick method.
|
||||||
|
public Runnable tickTask();
|
||||||
|
public void onTick();
|
||||||
|
|
||||||
|
public Thread examineThread();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
16
src/com/massivecraft/mcore4/store/Db.java
Normal file
16
src/com/massivecraft/mcore4/store/Db.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface Db<R>
|
||||||
|
{
|
||||||
|
public String name();
|
||||||
|
|
||||||
|
public boolean drop();
|
||||||
|
|
||||||
|
public Set<String> collnames();
|
||||||
|
|
||||||
|
public Driver<R> driver();
|
||||||
|
|
||||||
|
public Object getCollDriverObject(Coll<?, ?> coll);
|
||||||
|
}
|
12
src/com/massivecraft/mcore4/store/DbAbstract.java
Normal file
12
src/com/massivecraft/mcore4/store/DbAbstract.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public abstract class DbAbstract<R> implements Db<R>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Set<String> collnames()
|
||||||
|
{
|
||||||
|
return this.driver().collnames(this);
|
||||||
|
}
|
||||||
|
}
|
57
src/com/massivecraft/mcore4/store/DbGson.java
Normal file
57
src/com/massivecraft/mcore4/store/DbGson.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonElement;
|
||||||
|
import com.massivecraft.mcore4.util.DiscUtil;
|
||||||
|
|
||||||
|
public class DbGson extends DbAbstract<JsonElement>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// FIELDS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public File dir;
|
||||||
|
|
||||||
|
protected DriverGson driver;
|
||||||
|
@Override public DriverGson driver() { return driver; }
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// CONSTRUCTORS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public DbGson(DriverGson driver, File folder)
|
||||||
|
{
|
||||||
|
this.driver = driver;
|
||||||
|
this.dir = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name()
|
||||||
|
{
|
||||||
|
return dir.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drop()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return DiscUtil.deleteRecursive(this.dir);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCollDriverObject(Coll<?, ?> coll)
|
||||||
|
{
|
||||||
|
return new File(dir, coll.name());
|
||||||
|
}
|
||||||
|
}
|
56
src/com/massivecraft/mcore4/store/DbMongo.java
Normal file
56
src/com/massivecraft/mcore4/store/DbMongo.java
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.BasicDBObject;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.DB;
|
||||||
|
|
||||||
|
public class DbMongo extends DbAbstract<BasicDBObject>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// FIELDS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public DB db;
|
||||||
|
|
||||||
|
protected DriverMongo driver;
|
||||||
|
@Override public DriverMongo driver() { return driver; }
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// CONSTRUCTORS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public DbMongo(DriverMongo driver, DB db)
|
||||||
|
{
|
||||||
|
this.driver = driver;
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String name()
|
||||||
|
{
|
||||||
|
return db.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drop()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.db.dropDatabase();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCollDriverObject(Coll<?, ?> coll)
|
||||||
|
{
|
||||||
|
return db.getCollection(coll.name());
|
||||||
|
}
|
||||||
|
}
|
55
src/com/massivecraft/mcore4/store/Driver.java
Normal file
55
src/com/massivecraft/mcore4/store/Driver.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategy;
|
||||||
|
import com.massivecraft.mcore4.store.storeadapter.StoreAdapter;
|
||||||
|
|
||||||
|
public interface Driver<R>
|
||||||
|
{
|
||||||
|
// Returns the name of the driver.
|
||||||
|
public String name();
|
||||||
|
|
||||||
|
// This is the rawdata format this driver works with.
|
||||||
|
// Could for example be JsonElement or DBObject
|
||||||
|
public Class<R> getRawdataClass();
|
||||||
|
|
||||||
|
// This is some sort of database specific id strategy with built in adapter
|
||||||
|
public boolean registerIdStrategy(IdStrategy<?, ?> idStrategy);
|
||||||
|
public <L> IdStrategy<L, ?> getIdStrategy(String idStrategyName);
|
||||||
|
|
||||||
|
// Get the default store adapter for the driver.
|
||||||
|
public StoreAdapter getStoreAdapter();
|
||||||
|
|
||||||
|
// Get a database instance from the driver
|
||||||
|
public Db<R> db(String uri);
|
||||||
|
|
||||||
|
// What collections are in the database?
|
||||||
|
public Set<String> collnames(Db<?> db);
|
||||||
|
|
||||||
|
// Is id X in the collection?
|
||||||
|
public <L> boolean containsId(Coll<?, L> coll, L id);
|
||||||
|
|
||||||
|
// When was X last altered?
|
||||||
|
public <L> Long mtime(Coll<?, L> coll, L id);
|
||||||
|
|
||||||
|
// What ids are in the collection?
|
||||||
|
public <L> Collection<L> ids(Coll<?, L> coll);
|
||||||
|
|
||||||
|
// Return a map of all ids with their corresponding mtimes
|
||||||
|
public <L> Map<L, Long> id2mtime(Coll<?, L> coll);
|
||||||
|
|
||||||
|
// Load the raw data for X. The second part of the entry is the remote mtime at the load.
|
||||||
|
public <L> Entry<R, Long> load(Coll<?, L> coll, L id);
|
||||||
|
|
||||||
|
// Save raw data as X
|
||||||
|
// Return value is the new mtime (we caused the change).
|
||||||
|
// If the mtime is null something failed.
|
||||||
|
public <L> Long save(Coll<?, L> coll, L id, final Object rawData);
|
||||||
|
|
||||||
|
// Delete X
|
||||||
|
public <L> void delete(Coll<?, L> coll, L id);
|
||||||
|
}
|
34
src/com/massivecraft/mcore4/store/DriverAbstract.java
Normal file
34
src/com/massivecraft/mcore4/store/DriverAbstract.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategy;
|
||||||
|
|
||||||
|
public abstract class DriverAbstract<R> implements Driver<R>
|
||||||
|
{
|
||||||
|
public DriverAbstract(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String name;
|
||||||
|
@Override public String name() { return this.name; }
|
||||||
|
|
||||||
|
protected Map<String, IdStrategy<?, ?>> idStrategies = new HashMap<String, IdStrategy<?, ?>>();
|
||||||
|
@Override
|
||||||
|
public boolean registerIdStrategy(IdStrategy<?, ?> idStrategy)
|
||||||
|
{
|
||||||
|
if (idStrategies.containsKey(idStrategy.getName())) return false;
|
||||||
|
idStrategies.put(idStrategy.getName(), idStrategy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <L> IdStrategy<L, ?> getIdStrategy(String idStrategyName)
|
||||||
|
{
|
||||||
|
IdStrategy<?, ?> idStrategy = idStrategies.get(idStrategyName);
|
||||||
|
return (IdStrategy<L, ?>) idStrategy;
|
||||||
|
}
|
||||||
|
}
|
196
src/com/massivecraft/mcore4/store/DriverGson.java
Normal file
196
src/com/massivecraft/mcore4/store/DriverGson.java
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonElement;
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonParser;
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategyAiGson;
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategyOidGson;
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategyUuidMongoAndGson;
|
||||||
|
import com.massivecraft.mcore4.store.storeadapter.StoreAdapter;
|
||||||
|
import com.massivecraft.mcore4.store.storeadapter.StoreAdapterGson;
|
||||||
|
import com.massivecraft.mcore4.util.DiscUtil;
|
||||||
|
|
||||||
|
public class DriverGson extends DriverAbstract<JsonElement>
|
||||||
|
{
|
||||||
|
protected final static String DOTJSON = ".json";
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override public Class<JsonElement> getRawdataClass() { return JsonElement.class; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoreAdapter getStoreAdapter()
|
||||||
|
{
|
||||||
|
return StoreAdapterGson.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Db<JsonElement> db(String uri)
|
||||||
|
{
|
||||||
|
// "gson://" is 7 chars
|
||||||
|
File folder = new File(uri.substring(7));
|
||||||
|
folder.mkdirs();
|
||||||
|
return new DbGson(this, folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> collnames(Db<?> db)
|
||||||
|
{
|
||||||
|
Set<String> ret = new LinkedHashSet<String>();
|
||||||
|
|
||||||
|
for (File f : ((DbGson)db).dir.listFiles())
|
||||||
|
{
|
||||||
|
if ( ! f.isDirectory()) continue;
|
||||||
|
ret.add(f.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> boolean containsId(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
return fileFromId(coll, id).isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Long mtime(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
File file = fileFromId(coll, id);
|
||||||
|
if ( ! file.isFile()) return null;
|
||||||
|
return file.lastModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Collection<L> ids(Coll<?, L> coll)
|
||||||
|
{
|
||||||
|
List<L> ret = new ArrayList<L>();
|
||||||
|
|
||||||
|
// Scan the collection folder for .json files
|
||||||
|
File collDir = getCollDir(coll);
|
||||||
|
if ( ! collDir.isDirectory()) return ret;
|
||||||
|
for(File file : collDir.listFiles(JsonFileFilter.get()))
|
||||||
|
{
|
||||||
|
// Then convert them to what they should be
|
||||||
|
String remoteId = idFromFile(file);
|
||||||
|
L localId = coll.idStrategy().remoteToLocal(remoteId);
|
||||||
|
ret.add(localId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Map<L, Long> id2mtime(Coll<?, L> coll)
|
||||||
|
{
|
||||||
|
Map<L, Long> ret = new HashMap<L, Long>();
|
||||||
|
|
||||||
|
// Scan the collection folder for .json files
|
||||||
|
File collDir = getCollDir(coll);
|
||||||
|
if ( ! collDir.isDirectory()) return ret;
|
||||||
|
for(File file : collDir.listFiles(JsonFileFilter.get()))
|
||||||
|
{
|
||||||
|
// Then convert them to what they should be
|
||||||
|
String remoteId = idFromFile(file);
|
||||||
|
L localId = coll.idStrategy().remoteToLocal(remoteId);
|
||||||
|
ret.put(localId, file.lastModified());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Entry<JsonElement, Long> load(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
File file = fileFromId(coll, id);
|
||||||
|
Long mtime = file.lastModified();
|
||||||
|
if (mtime == 0) return null;
|
||||||
|
String content = DiscUtil.readCatch(file);
|
||||||
|
if (content == null) return null;
|
||||||
|
JsonElement raw = new JsonParser().parse(content);
|
||||||
|
return new SimpleEntry<JsonElement, Long>(raw, mtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Long save(Coll<?, L> coll, L id, Object rawData)
|
||||||
|
{
|
||||||
|
File file = fileFromId(coll, id);
|
||||||
|
getCollDir(coll).mkdirs();
|
||||||
|
String content = coll.mplugin().gson.toJson((JsonElement)rawData);
|
||||||
|
if (DiscUtil.writeCatch(file, content) == false) return null;
|
||||||
|
return file.lastModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> void delete(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
File file = fileFromId(coll, id);
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// UTIL
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static File getCollDir(Coll<?, ?> coll)
|
||||||
|
{
|
||||||
|
return (File) coll.collDriverObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String idFromFile(File file)
|
||||||
|
{
|
||||||
|
if (file == null) return null;
|
||||||
|
String name = file.getName();
|
||||||
|
return name.substring(0, name.length()-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <L> File fileFromId(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
File collDir = getCollDir(coll);
|
||||||
|
String idString = (String)coll.idStrategy().localToRemote(id);
|
||||||
|
File idFile = new File(collDir, idString+DOTJSON);
|
||||||
|
return idFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CONSTRUCTORS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
public static String NAME = "gson";
|
||||||
|
|
||||||
|
private DriverGson()
|
||||||
|
{
|
||||||
|
super(NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static DriverGson instance;
|
||||||
|
public static DriverGson get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
instance = new DriverGson();
|
||||||
|
instance.registerIdStrategy(IdStrategyAiGson.get());
|
||||||
|
instance.registerIdStrategy(IdStrategyOidGson.get());
|
||||||
|
instance.registerIdStrategy(IdStrategyUuidMongoAndGson.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
228
src/com/massivecraft/mcore4/store/DriverMongo.java
Normal file
228
src/com/massivecraft/mcore4/store/DriverMongo.java
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.BasicDBObject;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.DB;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.DBCollection;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.DBCursor;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.MongoURI;
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategyAiMongo;
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategyOidMongo;
|
||||||
|
import com.massivecraft.mcore4.store.idstrategy.IdStrategyUuidMongoAndGson;
|
||||||
|
import com.massivecraft.mcore4.store.storeadapter.StoreAdapter;
|
||||||
|
import com.massivecraft.mcore4.store.storeadapter.StoreAdapterMongo;
|
||||||
|
|
||||||
|
public class DriverMongo extends DriverAbstract<BasicDBObject>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// STATIC
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public final static String ID_FIELD = "_id";
|
||||||
|
public final static String MTIME_FIELD = "_mtime";
|
||||||
|
|
||||||
|
public final static BasicDBObject dboEmpty = new BasicDBObject();
|
||||||
|
public final static BasicDBObject dboKeysId = new BasicDBObject().append(ID_FIELD, 1);
|
||||||
|
public final static BasicDBObject dboKeysMtime = new BasicDBObject().append(MTIME_FIELD, 1);
|
||||||
|
public final static BasicDBObject dboKeysIdandMtime = new BasicDBObject().append(ID_FIELD, 1).append(MTIME_FIELD, 1);
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override public Class<BasicDBObject> getRawdataClass() { return BasicDBObject.class; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoreAdapter getStoreAdapter()
|
||||||
|
{
|
||||||
|
return StoreAdapterMongo.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Db<BasicDBObject> db(String uri)
|
||||||
|
{
|
||||||
|
DB db = this.getDbInner(uri);
|
||||||
|
return new DbMongo(this, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> collnames(Db<?> db)
|
||||||
|
{
|
||||||
|
return ((DbMongo)db).db.getCollectionNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> boolean containsId(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
DBCollection dbcoll = fixColl(coll);
|
||||||
|
DBCursor cursor = dbcoll.find(new BasicDBObject(ID_FIELD, coll.idStrategy().localToRemote(id)));
|
||||||
|
return cursor.count() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Long mtime(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
DBCollection dbcoll = fixColl(coll);
|
||||||
|
BasicDBObject found = (BasicDBObject)dbcoll.findOne(new BasicDBObject(ID_FIELD, coll.idStrategy().localToRemote(id)), dboKeysMtime);
|
||||||
|
if (found == null) return null;
|
||||||
|
if ( ! found.containsField(MTIME_FIELD)) return null; // This should not happen! But better to ignore than crash?
|
||||||
|
return found.getLong(MTIME_FIELD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Collection<L> ids(Coll<?, L> coll)
|
||||||
|
{
|
||||||
|
List<L> ret = null;
|
||||||
|
|
||||||
|
DBCollection dbcoll = fixColl(coll);
|
||||||
|
|
||||||
|
DBCursor cursor = dbcoll.find(dboEmpty, dboKeysId);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ret = new ArrayList<L>(cursor.count());
|
||||||
|
while(cursor.hasNext())
|
||||||
|
{
|
||||||
|
Object remoteId = cursor.next().get(ID_FIELD);
|
||||||
|
L localId = coll.idStrategy().remoteToLocal(remoteId);
|
||||||
|
ret.add(localId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Map<L, Long> id2mtime(Coll<?, L> coll)
|
||||||
|
{
|
||||||
|
Map<L, Long> ret = null;
|
||||||
|
|
||||||
|
DBCollection dbcoll = fixColl(coll);
|
||||||
|
|
||||||
|
DBCursor cursor = dbcoll.find(dboEmpty, dboKeysIdandMtime);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ret = new HashMap<L, Long>(cursor.count());
|
||||||
|
while(cursor.hasNext())
|
||||||
|
{
|
||||||
|
BasicDBObject raw = (BasicDBObject)cursor.next();
|
||||||
|
Object remoteId = raw.get(ID_FIELD);
|
||||||
|
L localId = coll.idStrategy().remoteToLocal(remoteId);
|
||||||
|
if ( ! raw.containsField(MTIME_FIELD)) continue; // This should not happen! But better to ignore than crash?
|
||||||
|
Long mtime = raw.getLong(MTIME_FIELD);
|
||||||
|
ret.put(localId, mtime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Entry<BasicDBObject, Long> load(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
DBCollection dbcoll = fixColl(coll);
|
||||||
|
BasicDBObject raw = (BasicDBObject)dbcoll.findOne(new BasicDBObject(ID_FIELD, coll.idStrategy().localToRemote(id)));
|
||||||
|
if (raw == null) return null;
|
||||||
|
Long mtime = (Long) raw.removeField(MTIME_FIELD);
|
||||||
|
return new SimpleEntry<BasicDBObject, Long>(raw, mtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> Long save(Coll<?, L> coll, L id, Object rawData)
|
||||||
|
{
|
||||||
|
DBCollection dbcoll = fixColl(coll);
|
||||||
|
|
||||||
|
// We shallow copy here in order to stop the extra "_mtime" field from messing up the lastRaw.
|
||||||
|
BasicDBObject data = (BasicDBObject)rawData;
|
||||||
|
data = (BasicDBObject)data.clone();
|
||||||
|
Long mtime = System.currentTimeMillis();
|
||||||
|
data.put(MTIME_FIELD, mtime);
|
||||||
|
|
||||||
|
Object remoteId = coll.idStrategy().localToRemote(id);
|
||||||
|
dbcoll.update(new BasicDBObject(ID_FIELD, remoteId), data, true, false);
|
||||||
|
|
||||||
|
return mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <L> void delete(Coll<?, L> coll, L id)
|
||||||
|
{
|
||||||
|
fixColl(coll).remove(new BasicDBObject(ID_FIELD, coll.idStrategy().localToRemote(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// UTIL
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
protected static DBCollection fixColl(Coll<?, ?> coll)
|
||||||
|
{
|
||||||
|
return (DBCollection) coll.collDriverObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DB getDbInner(String uri)
|
||||||
|
{
|
||||||
|
MongoURI muri = new MongoURI(uri);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DB db = muri.connectDB();
|
||||||
|
|
||||||
|
if (muri.getUsername() == null) return db;
|
||||||
|
|
||||||
|
if ( ! db.authenticate(muri.getUsername(), muri.getPassword()))
|
||||||
|
{
|
||||||
|
//log(Level.SEVERE, "... db authentication failed.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
//log(Level.SEVERE, "... mongo connection failed.");
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CONSTRUCTORS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
private DriverMongo()
|
||||||
|
{
|
||||||
|
super("mongodb");
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static DriverMongo instance;
|
||||||
|
public static DriverMongo get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
instance = new DriverMongo();
|
||||||
|
instance.registerIdStrategy(IdStrategyAiMongo.get());
|
||||||
|
instance.registerIdStrategy(IdStrategyOidMongo.get());
|
||||||
|
instance.registerIdStrategy(IdStrategyUuidMongoAndGson.get());
|
||||||
|
}
|
||||||
|
}
|
74
src/com/massivecraft/mcore4/store/Entity.java
Normal file
74
src/com/massivecraft/mcore4/store/Entity.java
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.store.accessor.Accessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usage of this class is highly optional. You may persist anything. If you are
|
||||||
|
* creating the class to be persisted yourself, it might be handy to extend this
|
||||||
|
* Entity class. It just contains a set of shortcut methods.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Self referencing generic using the "getThis trick".
|
||||||
|
// http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ206
|
||||||
|
public abstract class Entity<E extends Entity<E, L>, L>
|
||||||
|
{
|
||||||
|
public abstract Coll<E, L> getColl();
|
||||||
|
|
||||||
|
protected abstract E getThis();
|
||||||
|
|
||||||
|
public L attach()
|
||||||
|
{
|
||||||
|
return this.getColl().attach(getThis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public E detach()
|
||||||
|
{
|
||||||
|
return this.getColl().detach(getThis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean attached()
|
||||||
|
{
|
||||||
|
return this.getColl().getAll().contains(getThis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean detached()
|
||||||
|
{
|
||||||
|
return ! this.attached();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Mark as dirty.
|
||||||
|
// TODO: Perhaps even brute force methods to save or load from remote.
|
||||||
|
|
||||||
|
/*public boolean save()
|
||||||
|
{
|
||||||
|
return this.getColl().saveEntity(getThis());
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public L getId()
|
||||||
|
{
|
||||||
|
return this.getColl().id(getThis());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return this.getClass().getSimpleName()+this.getColl().mplugin().gson.toJson(this, this.getColl().entityClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
public E getDefaultInstance()
|
||||||
|
{
|
||||||
|
return this.getColl().createNewInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public E loadDefaults()
|
||||||
|
{
|
||||||
|
Accessor.get(this.getColl().entityClass()).copy(this.getDefaultInstance(), this.getThis());
|
||||||
|
return this.getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public E load(E entity)
|
||||||
|
{
|
||||||
|
Accessor.get(this.getColl().entityClass()).copy(entity, this.getThis());
|
||||||
|
return this.getThis();
|
||||||
|
}
|
||||||
|
}
|
43
src/com/massivecraft/mcore4/store/ExamineThread.java
Normal file
43
src/com/massivecraft/mcore4/store/ExamineThread.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
public class ExamineThread<E, L> extends Thread
|
||||||
|
{
|
||||||
|
protected Coll<E, L> coll;
|
||||||
|
|
||||||
|
public ExamineThread(Coll<E, L> coll)
|
||||||
|
{
|
||||||
|
this.coll = coll;
|
||||||
|
this.setName("ExamineThread for "+coll.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement logging and/or auto adjusting system for how long the sleep should be?
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//long before = System.currentTimeMillis();
|
||||||
|
|
||||||
|
coll.findSuspects();
|
||||||
|
|
||||||
|
//long after = System.currentTimeMillis();
|
||||||
|
|
||||||
|
//XCore.p.log(this.getName()+ " complete. Took "+ (after-before) +"ms.");
|
||||||
|
|
||||||
|
Thread.sleep(5000);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
// We've been interrupted. Lets bail.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/com/massivecraft/mcore4/store/JsonFileFilter.java
Normal file
25
src/com/massivecraft/mcore4/store/JsonFileFilter.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
|
||||||
|
public class JsonFileFilter implements FileFilter
|
||||||
|
{
|
||||||
|
private final static String DOTJSON = ".json";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File pathname)
|
||||||
|
{
|
||||||
|
return pathname.getName().toLowerCase().endsWith(DOTJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static JsonFileFilter instance = new JsonFileFilter();
|
||||||
|
public static JsonFileFilter get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
49
src/com/massivecraft/mcore4/store/MStore.java
Normal file
49
src/com/massivecraft/mcore4/store/MStore.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class MStore
|
||||||
|
{
|
||||||
|
protected static Map<String, Driver<?>> drivers = new HashMap<String, Driver<?>>();
|
||||||
|
public static boolean registerDriver(Driver<?> driver)
|
||||||
|
{
|
||||||
|
if (drivers.containsKey(driver.name())) return false;
|
||||||
|
drivers.put(driver.name(), driver);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static Driver<?> getDriver(String id)
|
||||||
|
{
|
||||||
|
return drivers.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Db<?> getDb(String uri)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return getDb(new URI(uri));
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Db<?> getDb(URI uri)
|
||||||
|
{
|
||||||
|
String scheme = uri.getScheme();
|
||||||
|
Driver<?> driver = getDriver(scheme);
|
||||||
|
if (driver == null) return null;
|
||||||
|
return driver.db(uri.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
registerDriver(DriverMongo.get());
|
||||||
|
registerDriver(DriverGson.get());
|
||||||
|
}
|
||||||
|
}
|
75
src/com/massivecraft/mcore4/store/MStoreUtil.java
Normal file
75
src/com/massivecraft/mcore4/store/MStoreUtil.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.Predictate;
|
||||||
|
|
||||||
|
public class MStoreUtil
|
||||||
|
{
|
||||||
|
public static <T> ArrayList<T> uglySQL(Collection<T> items, Predictate<T> where, Comparator<T> orderby, Integer limit, Integer offset)
|
||||||
|
{
|
||||||
|
ArrayList<T> ret = new ArrayList<T>(items.size());
|
||||||
|
|
||||||
|
// WHERE
|
||||||
|
if (where == null)
|
||||||
|
{
|
||||||
|
ret.addAll(items);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (T item : items)
|
||||||
|
{
|
||||||
|
if (where.apply(item))
|
||||||
|
{
|
||||||
|
ret.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ORDERBY
|
||||||
|
if (orderby != null)
|
||||||
|
{
|
||||||
|
Collections.sort(ret, orderby);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LIMIT AND OFFSET
|
||||||
|
// Parse args
|
||||||
|
int fromIndex = 0;
|
||||||
|
if (offset != null)
|
||||||
|
{
|
||||||
|
fromIndex = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int toIndex = ret.size()-1;
|
||||||
|
if (limit != null)
|
||||||
|
{
|
||||||
|
toIndex = offset+limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean args
|
||||||
|
if (fromIndex <= 0)
|
||||||
|
{
|
||||||
|
fromIndex = 0;
|
||||||
|
}
|
||||||
|
else if (fromIndex > ret.size()-1)
|
||||||
|
{
|
||||||
|
fromIndex = ret.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toIndex < fromIndex)
|
||||||
|
{
|
||||||
|
toIndex = fromIndex;
|
||||||
|
}
|
||||||
|
else if (toIndex > ret.size()-1)
|
||||||
|
{
|
||||||
|
toIndex = ret.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No limit?
|
||||||
|
if (fromIndex == 0 && toIndex == ret.size()-1) return ret;
|
||||||
|
return new ArrayList<T>(ret.subList(fromIndex, toIndex));
|
||||||
|
}
|
||||||
|
}
|
28
src/com/massivecraft/mcore4/store/ModificationState.java
Normal file
28
src/com/massivecraft/mcore4/store/ModificationState.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
public enum ModificationState
|
||||||
|
{
|
||||||
|
LOCAL_ALTER (true, true),
|
||||||
|
LOCAL_ATTACH (true, true),
|
||||||
|
LOCAL_DETACH (true, true),
|
||||||
|
REMOTE_ALTER (true, false),
|
||||||
|
REMOTE_ATTACH (true, false),
|
||||||
|
REMOTE_DETACH (true, false),
|
||||||
|
NONE (false, false),
|
||||||
|
UNKNOWN (false, false),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final boolean modified;
|
||||||
|
public boolean modified() { return this.modified; }
|
||||||
|
|
||||||
|
private final boolean local;
|
||||||
|
public boolean local() { return this.local; }
|
||||||
|
public boolean remote() { return this.local == false; }
|
||||||
|
|
||||||
|
private ModificationState(boolean modified, boolean local)
|
||||||
|
{
|
||||||
|
this.modified = modified;
|
||||||
|
this.local = local;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
src/com/massivecraft/mcore4/store/PlayerColl.java
Normal file
51
src/com/massivecraft/mcore4/store/PlayerColl.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.player.PlayerEvent;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.MPlugin;
|
||||||
|
import com.massivecraft.mcore4.Predictate;
|
||||||
|
|
||||||
|
public class PlayerColl<E extends PlayerEntity<E>> extends Coll<E, String>
|
||||||
|
{
|
||||||
|
public PlayerColl(MPlugin mplugin, Db<?> db, String name, Class<E> entityClass)
|
||||||
|
{
|
||||||
|
super(mplugin, db, "ai", name, entityClass, String.class, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String idFix(Object oid)
|
||||||
|
{
|
||||||
|
if (oid == null) return null;
|
||||||
|
if (oid instanceof String) return (String) oid;
|
||||||
|
if (oid instanceof Player) return ((Player)oid).getName();
|
||||||
|
if (oid instanceof PlayerEvent) return ((PlayerEvent)oid).getPlayer().getName();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<E> getAllOnline()
|
||||||
|
{
|
||||||
|
// TODO: Reverse the order since we have fewer players online than offline?
|
||||||
|
return this.getAll(new Predictate<E>()
|
||||||
|
{
|
||||||
|
public boolean apply(E entity)
|
||||||
|
{
|
||||||
|
return entity.isOnline();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<E> getAllOffline()
|
||||||
|
{
|
||||||
|
return this.getAll(new Predictate<E>()
|
||||||
|
{
|
||||||
|
public boolean apply(E entity)
|
||||||
|
{
|
||||||
|
return entity.isOffline();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
79
src/com/massivecraft/mcore4/store/PlayerEntity.java
Normal file
79
src/com/massivecraft/mcore4/store/PlayerEntity.java
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package com.massivecraft.mcore4.store;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.util.PlayerUtil;
|
||||||
|
import com.massivecraft.mcore4.util.Txt;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class PlayerEntity<E extends PlayerEntity<E>> extends Entity<E, String>
|
||||||
|
{
|
||||||
|
public Player getPlayer()
|
||||||
|
{
|
||||||
|
return PlayerUtil.getPlayerExact(this.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnline()
|
||||||
|
{
|
||||||
|
return this.getPlayer() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOffline()
|
||||||
|
{
|
||||||
|
return ! isOnline();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// CHECKER UTILS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
public boolean isGameMode(GameMode gm, boolean defaultIfOffline)
|
||||||
|
{
|
||||||
|
Player player = this.getPlayer();
|
||||||
|
if (player == null || ! player.isOnline()) return defaultIfOffline;
|
||||||
|
return player.getGameMode() == gm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// Message Sending Helpers
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public void sendMessage(String msg)
|
||||||
|
{
|
||||||
|
Player player = this.getPlayer();
|
||||||
|
if (player == null) return;
|
||||||
|
player.sendMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMessage(Collection<String> msgs)
|
||||||
|
{
|
||||||
|
Player player = this.getPlayer();
|
||||||
|
if (player == null) return;
|
||||||
|
for(String msg : msgs)
|
||||||
|
{
|
||||||
|
player.sendMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void msg(String msg)
|
||||||
|
{
|
||||||
|
this.sendMessage(Txt.parse(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void msg(String msg, Object... args)
|
||||||
|
{
|
||||||
|
this.sendMessage(Txt.parse(msg, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void msg(Collection<String> msgs)
|
||||||
|
{
|
||||||
|
Player player = this.getPlayer();
|
||||||
|
if (player == null) return;
|
||||||
|
for(String msg : msgs)
|
||||||
|
{
|
||||||
|
player.sendMessage(Txt.parse(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/com/massivecraft/mcore4/store/accessor/Accessor.java
Normal file
20
src/com/massivecraft/mcore4/store/accessor/Accessor.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class Accessor
|
||||||
|
{
|
||||||
|
private static Map<Class<?>, EntityAccessor> class2EntityAccessor = new HashMap<Class<?>, EntityAccessor>();
|
||||||
|
|
||||||
|
public static EntityAccessor get(Class<?> clazz)
|
||||||
|
{
|
||||||
|
EntityAccessor ret = class2EntityAccessor.get(clazz);
|
||||||
|
if (ret == null)
|
||||||
|
{
|
||||||
|
ret = new EntityAccessorPerProperty(clazz);
|
||||||
|
class2EntityAccessor.put(clazz, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
175
src/com/massivecraft/mcore4/store/accessor/AccessorUtil.java
Normal file
175
src/com/massivecraft/mcore4/store/accessor/AccessorUtil.java
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class AccessorUtil
|
||||||
|
{
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// MAKE ACCESSIBLE
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
public static void makeAccessible(Field field)
|
||||||
|
{
|
||||||
|
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
|
||||||
|
{
|
||||||
|
field.setAccessible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void makeAccessible(Method method)
|
||||||
|
{
|
||||||
|
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible())
|
||||||
|
{
|
||||||
|
method.setAccessible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// FIND
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
public static List<Method> findMethod(Class<?> clazz, String name)
|
||||||
|
{
|
||||||
|
List<Method> ret = new ArrayList<Method>();
|
||||||
|
for (Class<?> c = clazz; c != null; c = c.getSuperclass())
|
||||||
|
{
|
||||||
|
Method[] methods = (c.isInterface() ? c.getMethods() : c.getDeclaredMethods());
|
||||||
|
for (Method method : methods)
|
||||||
|
{
|
||||||
|
if (name.equals(method.getName())) ret.add(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field findField(Class<?> clazz, String name)
|
||||||
|
{
|
||||||
|
for (Class<?> c = clazz; c != null && !Object.class.equals(c); c = c.getSuperclass())
|
||||||
|
{
|
||||||
|
Field[] fields = c.getDeclaredFields();
|
||||||
|
for (Field field : fields)
|
||||||
|
{
|
||||||
|
if (name.equals(field.getName())) return field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Field> findAllFields(Class<?> clazz)
|
||||||
|
{
|
||||||
|
List<Field> fields = new ArrayList<Field>();
|
||||||
|
for (Class<?> c = clazz; c != null; c = c.getSuperclass())
|
||||||
|
{
|
||||||
|
fields.addAll(Arrays.asList(c.getDeclaredFields()));
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be non-transient.
|
||||||
|
* Must be non-final.
|
||||||
|
* Will be set accessible if possible.
|
||||||
|
*/
|
||||||
|
public static Map<String, Field> getFieldMap(Class<?> clazz)
|
||||||
|
{
|
||||||
|
Map<String, Field> ret = new HashMap<String, Field>();
|
||||||
|
|
||||||
|
for (Field field : findAllFields(clazz))
|
||||||
|
{
|
||||||
|
if (Modifier.isTransient(field.getModifiers())) continue;
|
||||||
|
if (Modifier.isFinal(field.getModifiers())) continue;
|
||||||
|
makeAccessible(field);
|
||||||
|
ret.put(field.getName(), field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// FIND GETTERS AND SETTERS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
public static String ucfirst(String str)
|
||||||
|
{
|
||||||
|
return Character.toUpperCase(str.charAt(0))+str.substring(1);
|
||||||
|
}
|
||||||
|
public static String calcGetterNameBool(String fieldName) { return "is"+ucfirst(fieldName); }
|
||||||
|
public static String calcGetterName(String fieldName) { return "get"+ucfirst(fieldName); }
|
||||||
|
public static String calcSetterName(String fieldName) { return "set"+ucfirst(fieldName); }
|
||||||
|
|
||||||
|
// TODO: Use a predictate?
|
||||||
|
public static Method findGetter(Class<?> clazz, String fieldName)
|
||||||
|
{
|
||||||
|
for (Method method : findMethod(clazz, calcGetterName(fieldName)))
|
||||||
|
{
|
||||||
|
if (method.getParameterTypes().length == 0 && method.getReturnType() != null) return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Method method : findMethod(clazz, calcGetterNameBool(fieldName)))
|
||||||
|
{
|
||||||
|
if (method.getParameterTypes().length == 0 && method.getReturnType() == Boolean.class) return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Method findSetter(Class<?> clazz, String fieldName)
|
||||||
|
{
|
||||||
|
List<Method> methods = findMethod(clazz, calcSetterName(fieldName));
|
||||||
|
methods.addAll(findMethod(clazz, fieldName));
|
||||||
|
|
||||||
|
for (Method method : methods)
|
||||||
|
{
|
||||||
|
if (method != null && method.getParameterTypes().length == 1) return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CREATE PROPERTY ACCESS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
public static PropertyGetter createPropertyGetter(Class<?> clazz, String name)
|
||||||
|
{
|
||||||
|
Method method = findGetter(clazz, name);
|
||||||
|
if (method != null) return new PropertyGetterMethodReflection(method);
|
||||||
|
|
||||||
|
Field field = findField(clazz, name);
|
||||||
|
if (field != null) return new PropertyGetterFieldReflection(field);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertySetter createPropertySetter(Class<?> clazz, String name)
|
||||||
|
{
|
||||||
|
Method method = findSetter(clazz, name);
|
||||||
|
if (method != null) return new PropertySetterMethodReflection(method);
|
||||||
|
|
||||||
|
Field field = findField(clazz, name);
|
||||||
|
if (field != null) return new PropertySetterFieldReflection(field);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertyAccessor createPropertyAccessor(Class<?> clazz, String name)
|
||||||
|
{
|
||||||
|
PropertyGetter getter = createPropertyGetter(clazz, name);
|
||||||
|
if (getter == null) return null;
|
||||||
|
|
||||||
|
PropertySetter setter = createPropertySetter(clazz, name);
|
||||||
|
if (setter == null) return null;
|
||||||
|
|
||||||
|
return new PropertyAccessorComposite(getter, setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
public interface EntityAccessor extends EntitySetter, EntityGetter, EntityGlue
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public abstract class EntityAccessorAbstract implements EntityAccessor
|
||||||
|
{
|
||||||
|
protected final Class<?> clazz;
|
||||||
|
public Class<?> clazz() { return this.clazz; }
|
||||||
|
|
||||||
|
public EntityAccessorAbstract(Class<?> clazz)
|
||||||
|
{
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final boolean DEFAULT_TRANSPARENT = false;
|
||||||
|
@Override
|
||||||
|
public void copy(Object from, Object to, String property, boolean transparent)
|
||||||
|
{
|
||||||
|
Object val = this.get(from, property);
|
||||||
|
if (transparent && val == null) return;
|
||||||
|
this.set(to, property, val);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void copy(Object from, Object to, String property)
|
||||||
|
{
|
||||||
|
this.copy(from, to, property, DEFAULT_TRANSPARENT);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void copy(Object from, Object to, Collection<String> properties, boolean transparent)
|
||||||
|
{
|
||||||
|
for (String property : properties)
|
||||||
|
{
|
||||||
|
this.copy(from, to, property, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void copy(Object from, Object to, Collection<String> properties)
|
||||||
|
{
|
||||||
|
this.copy(from, to, properties, DEFAULT_TRANSPARENT);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void copy(Object from, Object to, boolean transparent)
|
||||||
|
{
|
||||||
|
this.copy(from, to, this.getPropertyNames(), transparent);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void copy(Object from, Object to)
|
||||||
|
{
|
||||||
|
this.copy(from, to, DEFAULT_TRANSPARENT);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class EntityAccessorPerProperty extends EntityAccessorAbstract
|
||||||
|
{
|
||||||
|
protected Map<String, PropertyAccessor> propertyAccessors;
|
||||||
|
public Map<String, PropertyAccessor> propertyAccessors() { return this.propertyAccessors; }
|
||||||
|
public PropertyAccessor propertyAccessor(String name)
|
||||||
|
{
|
||||||
|
PropertyAccessor ret = this.propertyAccessors.get(name);
|
||||||
|
if (ret == null)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("The property \""+name+"\" is not supported.");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
public void propertyAccessor(String name, PropertyAccessor val)
|
||||||
|
{
|
||||||
|
this.propertyAccessors.put(name, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CONSTRUCTORS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
public EntityAccessorPerProperty(Class<?> clazz)
|
||||||
|
{
|
||||||
|
super(clazz);
|
||||||
|
this.propertyAccessors = new HashMap<String, PropertyAccessor>();
|
||||||
|
this.populateAI1();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// AI
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
public void populateAI1()
|
||||||
|
{
|
||||||
|
this.propertyAccessorAI(AccessorUtil.getFieldMap(this.clazz).keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void propertyAccessorAI(String name)
|
||||||
|
{
|
||||||
|
this.propertyAccessors.put(name, AccessorUtil.createPropertyAccessor(this.clazz, name));
|
||||||
|
}
|
||||||
|
public void propertyAccessorAI(String... names)
|
||||||
|
{
|
||||||
|
for (String name : names)
|
||||||
|
{
|
||||||
|
this.propertyAccessorAI(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void propertyAccessorAI(Collection<? extends String> names)
|
||||||
|
{
|
||||||
|
this.propertyAccessorAI(names.toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// IMPLEMENTATION
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Object entity, String property, Object val)
|
||||||
|
{
|
||||||
|
PropertyAccessor pa = this.propertyAccessor(property);
|
||||||
|
pa.set(entity, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Object entity, String property)
|
||||||
|
{
|
||||||
|
PropertyAccessor pa = this.propertyAccessor(property);
|
||||||
|
return pa.get(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getPropertyNames()
|
||||||
|
{
|
||||||
|
return this.propertyAccessors.keySet();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
public interface EntityGetter
|
||||||
|
{
|
||||||
|
public Object get(Object entity, String property);
|
||||||
|
}
|
17
src/com/massivecraft/mcore4/store/accessor/EntityGlue.java
Normal file
17
src/com/massivecraft/mcore4/store/accessor/EntityGlue.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public interface EntityGlue
|
||||||
|
{
|
||||||
|
public void copy(Object from, Object to, String property, boolean transparent);
|
||||||
|
public void copy(Object from, Object to, String property);
|
||||||
|
|
||||||
|
public void copy(Object from, Object to, Collection<String> properties, boolean transparent);
|
||||||
|
public void copy(Object from, Object to, Collection<String> properties);
|
||||||
|
|
||||||
|
public void copy(Object from, Object to, boolean transparent);
|
||||||
|
public void copy(Object from, Object to);
|
||||||
|
|
||||||
|
public Collection<String> getPropertyNames();
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
public interface EntitySetter
|
||||||
|
{
|
||||||
|
public void set(Object entity, String property, Object val);
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
public interface PropertyAccessor extends PropertySetter, PropertyGetter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
public class PropertyAccessorComposite implements PropertyAccessor
|
||||||
|
{
|
||||||
|
private final PropertyGetter getter;
|
||||||
|
private final PropertySetter setter;
|
||||||
|
|
||||||
|
public PropertyAccessorComposite(PropertyGetter getter, PropertySetter setter)
|
||||||
|
{
|
||||||
|
this.getter = getter;
|
||||||
|
this.setter = setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Object entity, Object val)
|
||||||
|
{
|
||||||
|
this.setter.set(entity, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Object entity)
|
||||||
|
{
|
||||||
|
return this.getter.get(entity);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
public interface PropertyGetter
|
||||||
|
{
|
||||||
|
public Object get(Object entity);
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class PropertyGetterFieldReflection implements PropertyGetter
|
||||||
|
{
|
||||||
|
private final Field field;
|
||||||
|
|
||||||
|
public PropertyGetterFieldReflection(Field field)
|
||||||
|
{
|
||||||
|
AccessorUtil.makeAccessible(field);
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Object entity)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return this.field.get(entity);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class PropertyGetterMethodReflection implements PropertyGetter
|
||||||
|
{
|
||||||
|
private final Method method;
|
||||||
|
|
||||||
|
public PropertyGetterMethodReflection(Method method)
|
||||||
|
{
|
||||||
|
AccessorUtil.makeAccessible(method);
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Object entity)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return this.method.invoke(entity, new Object[0]);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
public interface PropertySetter
|
||||||
|
{
|
||||||
|
public void set(Object entity, Object val);
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class PropertySetterFieldReflection implements PropertySetter
|
||||||
|
{
|
||||||
|
private final Field field;
|
||||||
|
|
||||||
|
public PropertySetterFieldReflection(Field field)
|
||||||
|
{
|
||||||
|
AccessorUtil.makeAccessible(field);
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Object entity, Object val)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.field.set(entity, val);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.massivecraft.mcore4.store.accessor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class PropertySetterMethodReflection implements PropertySetter
|
||||||
|
{
|
||||||
|
private final Method method;
|
||||||
|
|
||||||
|
public PropertySetterMethodReflection(Method method)
|
||||||
|
{
|
||||||
|
AccessorUtil.makeAccessible(method);
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Object entity, Object val)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.method.invoke(entity, val);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
src/com/massivecraft/mcore4/store/idstrategy/IdStrategy.java
Normal file
29
src/com/massivecraft/mcore4/store/idstrategy/IdStrategy.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package com.massivecraft.mcore4.store.idstrategy;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.store.CollInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tasks of the IdStrategy:
|
||||||
|
* 1. Generate a new free id that has not yet been used.
|
||||||
|
* 2. Convert the id between java object and raw database data.
|
||||||
|
*
|
||||||
|
* To complete the tasks the IdStrategy must be tailored for a specific database-driver.
|
||||||
|
* Thus you will find multiple implementations with the name "ai" (auto increment).
|
||||||
|
* There must be one implementation per driver.
|
||||||
|
*/
|
||||||
|
public interface IdStrategy<L, R>
|
||||||
|
{
|
||||||
|
// The name of the strategy (such as "auto_increment")
|
||||||
|
public String getName();
|
||||||
|
|
||||||
|
// The id classes
|
||||||
|
public Class<L> getLocalClass();
|
||||||
|
public Class<R> getRemoteClass();
|
||||||
|
|
||||||
|
// Convert
|
||||||
|
public R localToRemote(Object local);
|
||||||
|
public L remoteToLocal(Object remote);
|
||||||
|
|
||||||
|
// Generate
|
||||||
|
public L generate(CollInterface<?, L> coll);
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.massivecraft.mcore4.store.idstrategy;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.store.CollInterface;
|
||||||
|
|
||||||
|
public abstract class IdStrategyAbstract<L, R> implements IdStrategy<L, R>
|
||||||
|
{
|
||||||
|
public IdStrategyAbstract(String name, Class<L> localClass, Class<R> remoteClass)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.localClass = localClass;
|
||||||
|
this.remoteClass = remoteClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String name;
|
||||||
|
@Override public String getName() { return this.name; }
|
||||||
|
|
||||||
|
protected Class<L> localClass;
|
||||||
|
@Override public Class<L> getLocalClass() { return this.localClass; }
|
||||||
|
|
||||||
|
protected Class<R> remoteClass;
|
||||||
|
@Override public Class<R> getRemoteClass() { return this.remoteClass; }
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public L generate(CollInterface<?, L> coll)
|
||||||
|
{
|
||||||
|
Collection<L> alreadyInUse = coll.ids();
|
||||||
|
L ret = null;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = this.generateAttempt(coll);
|
||||||
|
if (ret == null) return null;
|
||||||
|
}
|
||||||
|
while (alreadyInUse.contains(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract L generateAttempt(CollInterface<?, L> coll);
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.massivecraft.mcore4.store.idstrategy;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.store.CollInterface;
|
||||||
|
import com.massivecraft.mcore4.store.DbGson;
|
||||||
|
import com.massivecraft.mcore4.util.DiscUtil;
|
||||||
|
|
||||||
|
public class IdStrategyAiGson extends IdStrategyAbstract<String, String>
|
||||||
|
{
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CONSTRUCTORS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
private IdStrategyAiGson()
|
||||||
|
{
|
||||||
|
super("ai", String.class, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override public String localToRemote(Object local) { return (String)local; }
|
||||||
|
@Override public String remoteToLocal(Object remote) { return (String)remote; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateAttempt(CollInterface<?, String> coll)
|
||||||
|
{
|
||||||
|
File file = getAiFile(coll);
|
||||||
|
|
||||||
|
// Ensure the file exists
|
||||||
|
if (this.ensureFileExists(file) == false)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String content = DiscUtil.readCatch(file);
|
||||||
|
if (content == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer current = 0;
|
||||||
|
if (content.length() > 0)
|
||||||
|
{
|
||||||
|
current = Integer.valueOf(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer next = current + 1;
|
||||||
|
if (DiscUtil.writeCatch(file, next.toString()) == false)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected File getAiFile(CollInterface<?, String> coll)
|
||||||
|
{
|
||||||
|
DbGson cdb = (DbGson)coll.db();
|
||||||
|
return new File(cdb.dir, coll.name() + "_ai.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean ensureFileExists(File file)
|
||||||
|
{
|
||||||
|
if (file.isFile()) return true;
|
||||||
|
if (file.isDirectory()) return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return file.createNewFile();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static IdStrategyAiGson instance = new IdStrategyAiGson();
|
||||||
|
public static IdStrategyAiGson get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package com.massivecraft.mcore4.store.idstrategy;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.BasicDBObject;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.DBCollection;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.DBObject;
|
||||||
|
import com.massivecraft.mcore4.store.CollInterface;
|
||||||
|
import com.massivecraft.mcore4.store.DbMongo;
|
||||||
|
|
||||||
|
public class IdStrategyAiMongo extends IdStrategyAbstract<String, String>
|
||||||
|
{
|
||||||
|
private static String SEC = "seq";
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// FIELDS
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
// String sequenseName;
|
||||||
|
// DBCollection sequenseCollection;
|
||||||
|
String sequenseField;
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CONSTRUCTORS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
private IdStrategyAiMongo()
|
||||||
|
{
|
||||||
|
super("ai", String.class, String.class);
|
||||||
|
this.sequenseField = SEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override public String localToRemote(Object local) { return (String)local; }
|
||||||
|
@Override public String remoteToLocal(Object remote) { return (String)remote; }
|
||||||
|
|
||||||
|
// http://dev.bubblemix.net/blog/2011/04/auto-increment-for-mongodb-with-the-java-driver/
|
||||||
|
@Override
|
||||||
|
public String generateAttempt(CollInterface<?, String> coll)
|
||||||
|
{
|
||||||
|
String sequenseName = coll.name();
|
||||||
|
DBCollection sequenseCollection = ((DbMongo)coll.db()).db.getCollection(SEC);
|
||||||
|
|
||||||
|
// this object represents your "query", its analogous to a WHERE clause in SQL
|
||||||
|
DBObject query = new BasicDBObject();
|
||||||
|
query.put("_id", sequenseName); // where _id = the input sequence name
|
||||||
|
|
||||||
|
// this object represents the "update" or the SET blah=blah in SQL
|
||||||
|
DBObject change = new BasicDBObject(this.sequenseField, 1);
|
||||||
|
DBObject update = new BasicDBObject("$inc", change); // the $inc here is a mongodb command for increment
|
||||||
|
|
||||||
|
// Atomically updates the sequence field and returns the value for you
|
||||||
|
DBObject res = sequenseCollection.findAndModify(query, new BasicDBObject(), new BasicDBObject(), false, update, true, true);
|
||||||
|
return res.get(this.sequenseField).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static IdStrategyAiMongo instance = new IdStrategyAiMongo();
|
||||||
|
public static IdStrategyAiMongo get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.massivecraft.mcore4.store.idstrategy;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.bson.types.ObjectId;
|
||||||
|
import com.massivecraft.mcore4.store.CollInterface;
|
||||||
|
|
||||||
|
public class IdStrategyOidGson extends IdStrategyAbstract<ObjectId, String>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override public String localToRemote(Object local) { return ((ObjectId)local).toStringBabble(); }
|
||||||
|
@Override public ObjectId remoteToLocal(Object remote) { return ObjectId.massageToObjectId((String)remote); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObjectId generateAttempt(CollInterface<?, ObjectId> coll)
|
||||||
|
{
|
||||||
|
return ObjectId.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CONSTRUCTORS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
private IdStrategyOidGson()
|
||||||
|
{
|
||||||
|
super("oid", ObjectId.class, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static IdStrategyOidGson instance = new IdStrategyOidGson();
|
||||||
|
public static IdStrategyOidGson get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.massivecraft.mcore4.store.idstrategy;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.bson.types.ObjectId;
|
||||||
|
import com.massivecraft.mcore4.store.CollInterface;
|
||||||
|
|
||||||
|
public class IdStrategyOidMongo extends IdStrategyAbstract<ObjectId, ObjectId>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override public ObjectId localToRemote(Object local) { return (ObjectId)local; }
|
||||||
|
@Override public ObjectId remoteToLocal(Object remote) { return (ObjectId)remote; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObjectId generateAttempt(CollInterface<?, ObjectId> coll)
|
||||||
|
{
|
||||||
|
return ObjectId.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CONSTRUCTORS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
private IdStrategyOidMongo()
|
||||||
|
{
|
||||||
|
super("oid", ObjectId.class, ObjectId.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static IdStrategyOidMongo instance = new IdStrategyOidMongo();
|
||||||
|
public static IdStrategyOidMongo get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.massivecraft.mcore4.store.idstrategy;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.store.CollInterface;
|
||||||
|
|
||||||
|
public class IdStrategyUuidMongoAndGson extends IdStrategyAbstract<UUID, String>
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// IMPLEMENTATION
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
@Override public String localToRemote(Object local) { return ((UUID)local).toString(); }
|
||||||
|
@Override public UUID remoteToLocal(Object remote) { return UUID.fromString((String)remote); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID generateAttempt(CollInterface<?, UUID> coll)
|
||||||
|
{
|
||||||
|
return UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------//
|
||||||
|
// CONSTRUCTORS
|
||||||
|
//----------------------------------------------//
|
||||||
|
|
||||||
|
private IdStrategyUuidMongoAndGson()
|
||||||
|
{
|
||||||
|
super("uuid", UUID.class, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static IdStrategyUuidMongoAndGson instance = new IdStrategyUuidMongoAndGson();
|
||||||
|
public static IdStrategyUuidMongoAndGson get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
package com.massivecraft.mcore4.store.storeadapter;
|
||||||
|
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.bson.types.ObjectId;
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonArray;
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonElement;
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonNull;
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonObject;
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonPrimitive;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.BasicDBList;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.BasicDBObject;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.DBObject;
|
||||||
|
|
||||||
|
public final class MongoGsonConverter
|
||||||
|
{
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// GSON 2 MONGO
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public static BasicDBObject gson2MongoObject(JsonElement inElement)
|
||||||
|
{
|
||||||
|
JsonObject in = inElement.getAsJsonObject();
|
||||||
|
BasicDBObject out = new BasicDBObject();
|
||||||
|
for (Entry<String, JsonElement> entry : in.entrySet())
|
||||||
|
{
|
||||||
|
String key = entry.getKey();
|
||||||
|
JsonElement val = entry.getValue();
|
||||||
|
if (val.isJsonArray())
|
||||||
|
{
|
||||||
|
out.put(key, gson2MongoArray(val));
|
||||||
|
}
|
||||||
|
else if (val.isJsonObject())
|
||||||
|
{
|
||||||
|
out.put(key, gson2MongoObject(val));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.put(key, gson2MongoPrimitive(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BasicDBList gson2MongoArray(JsonElement inElement)
|
||||||
|
{
|
||||||
|
JsonArray in = inElement.getAsJsonArray();
|
||||||
|
BasicDBList out = new BasicDBList();
|
||||||
|
for (int i = 0; i < in.size(); i++)
|
||||||
|
{
|
||||||
|
JsonElement element = in.get(i);
|
||||||
|
if (element.isJsonArray())
|
||||||
|
{
|
||||||
|
out.add(gson2MongoArray(element));
|
||||||
|
}
|
||||||
|
else if (element.isJsonObject())
|
||||||
|
{
|
||||||
|
out.add(gson2MongoObject(element));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.add(gson2MongoPrimitive(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object gson2MongoPrimitive(JsonElement inElement)
|
||||||
|
{
|
||||||
|
if (inElement.isJsonNull()) return null;
|
||||||
|
JsonPrimitive in = inElement.getAsJsonPrimitive();
|
||||||
|
if (in.isBoolean()) return in.getAsBoolean();
|
||||||
|
if (in.isNumber()) return in.getAsNumber();
|
||||||
|
if (in.isString()) return in.getAsString();
|
||||||
|
throw new IllegalArgumentException("Unsupported value type for: " + in);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// MONGO 2 GSON
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
public static JsonObject mongo2GsonObject(DBObject inObject)
|
||||||
|
{
|
||||||
|
if (!(inObject instanceof BasicDBObject)) throw new IllegalArgumentException("Expected BasicDBObject as argument type!");
|
||||||
|
BasicDBObject in = (BasicDBObject)inObject;
|
||||||
|
|
||||||
|
JsonObject jsonObject = new JsonObject();
|
||||||
|
for (Entry<String, Object> entry : in.entrySet())
|
||||||
|
{
|
||||||
|
String key = entry.getKey();
|
||||||
|
Object val = entry.getValue();
|
||||||
|
if (val instanceof BasicDBList)
|
||||||
|
{
|
||||||
|
jsonObject.add(key, mongo2GsonArray((BasicDBList)val));
|
||||||
|
}
|
||||||
|
else if (val instanceof BasicDBObject)
|
||||||
|
{
|
||||||
|
jsonObject.add(key, mongo2GsonObject((BasicDBObject)val));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jsonObject.add(key, mongo2GsonPrimitive(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonArray mongo2GsonArray(DBObject inObject)
|
||||||
|
{
|
||||||
|
if (!(inObject instanceof BasicDBList)) throw new IllegalArgumentException("Expected BasicDBList as argument type!");
|
||||||
|
BasicDBList in = (BasicDBList)inObject;
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
for (int i = 0; i < in.size(); i++)
|
||||||
|
{
|
||||||
|
Object object = in.get(i);
|
||||||
|
if (object instanceof BasicDBList)
|
||||||
|
{
|
||||||
|
jsonArray.add(mongo2GsonArray((BasicDBList) object));
|
||||||
|
}
|
||||||
|
else if (object instanceof BasicDBObject)
|
||||||
|
{
|
||||||
|
jsonArray.add(mongo2GsonObject((BasicDBObject) object));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jsonArray.add(mongo2GsonPrimitive(object));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jsonArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonElement mongo2GsonPrimitive(Object inObject)
|
||||||
|
{
|
||||||
|
if (inObject == null) return JsonNull.INSTANCE;
|
||||||
|
if (inObject instanceof Boolean) return new JsonPrimitive((Boolean) inObject);
|
||||||
|
if (inObject instanceof Number) return new JsonPrimitive((Number) inObject);
|
||||||
|
if (inObject instanceof String) return new JsonPrimitive((String) inObject);
|
||||||
|
if (inObject instanceof Character) return new JsonPrimitive((Character) inObject);
|
||||||
|
if (inObject instanceof ObjectId) return new JsonPrimitive(inObject.toString());
|
||||||
|
throw new IllegalArgumentException("Unsupported value type for: " + inObject);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.massivecraft.mcore4.store.storeadapter;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.store.Coll;
|
||||||
|
|
||||||
|
public interface StoreAdapter
|
||||||
|
{
|
||||||
|
// A store adapter is supposed to be used with a certain driver.
|
||||||
|
// This method returns the name of that driver.
|
||||||
|
public String forDriverName();
|
||||||
|
|
||||||
|
public Object read(Coll<?, ?> coll, Object entity);
|
||||||
|
public void write(Coll<?, ?> coll, Object raw, Object entity); // (This is an opaque/complete write)
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.massivecraft.mcore4.store.storeadapter;
|
||||||
|
|
||||||
|
public abstract class StoreAdapterAbstract implements StoreAdapter
|
||||||
|
{
|
||||||
|
protected String forDriverName;
|
||||||
|
@Override public String forDriverName() { return this.forDriverName; }
|
||||||
|
|
||||||
|
public StoreAdapterAbstract(String forDriverName)
|
||||||
|
{
|
||||||
|
this.forDriverName = forDriverName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.massivecraft.mcore4.store.storeadapter;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonElement;
|
||||||
|
import com.massivecraft.mcore4.store.Coll;
|
||||||
|
import com.massivecraft.mcore4.store.accessor.Accessor;
|
||||||
|
|
||||||
|
public class StoreAdapterGson extends StoreAdapterAbstract
|
||||||
|
{
|
||||||
|
public StoreAdapterGson()
|
||||||
|
{
|
||||||
|
super("gson");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object read(Coll<?, ?> coll, Object entity)
|
||||||
|
{
|
||||||
|
return coll.mplugin().gson.toJsonTree(entity, coll.entityClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(Coll<?, ?> coll, Object raw, Object entity)
|
||||||
|
{
|
||||||
|
Object temp = coll.mplugin().gson.fromJson((JsonElement)raw, coll.entityClass());
|
||||||
|
Accessor.get(coll.entityClass()).copy(temp, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static StoreAdapterGson instance = new StoreAdapterGson();
|
||||||
|
public static StoreAdapterGson get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.massivecraft.mcore4.store.storeadapter;
|
||||||
|
|
||||||
|
import com.massivecraft.mcore4.lib.gson.JsonElement;
|
||||||
|
import com.massivecraft.mcore4.lib.mongodb.DBObject;
|
||||||
|
import com.massivecraft.mcore4.store.Coll;
|
||||||
|
|
||||||
|
public class StoreAdapterMongo extends StoreAdapterAbstract
|
||||||
|
{
|
||||||
|
public StoreAdapterMongo()
|
||||||
|
{
|
||||||
|
super("mongo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object read(Coll<?, ?> coll, Object entity)
|
||||||
|
{
|
||||||
|
return MongoGsonConverter.gson2MongoObject((JsonElement)StoreAdapterGson.get().read(coll, entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(Coll<?, ?> coll, Object raw, Object entity)
|
||||||
|
{
|
||||||
|
StoreAdapterGson.get().write(coll, MongoGsonConverter.mongo2GsonObject((DBObject) raw), entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------- //
|
||||||
|
// INSTANCE
|
||||||
|
// -------------------------------------------- //
|
||||||
|
|
||||||
|
protected static StoreAdapterMongo instance = new StoreAdapterMongo();
|
||||||
|
public static StoreAdapterMongo get()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user