package jane.core;
import java.util.ArrayList;
import java.util.List;
/**
* 安全修改的上下文类
* <p>
* 管理当前线程的回滚和提交<br>
* 由ProcThread管理上下文
*/
public final class SContext
{
public static abstract class Safe<B extends Bean<B>> implements Comparable<B>, Cloneable
{
protected final B _bean;
private final Safe<?> _parent;
protected SContext _sctx;
private Rec _rec;
private Runnable _onDirty;
protected boolean _fullUndo;
private boolean _dirty;
protected Safe(B bean, Safe<?> parent)
{
_bean = bean;
_parent = (parent != null ? parent : this);
}
@Deprecated
public B unsafe()
{
return _bean;
}
public Safe<?> parent()
{
return _parent;
}
public Safe<?> owner()
{
for(Safe<?> parent = _parent;;)
{
Safe<?> o = parent._parent;
if(o == parent) return parent;
parent = o;
}
}
public Rec record()
{
return _rec;
}
public final void checkLock()
{
if(_rec != null) _rec.checkLock();
}
void record(Rec rec)
{
_rec = rec;
}
public boolean isDirty()
{
return _dirty;
}
public boolean isDirtyAndClear()
{
boolean r = _dirty;
_dirty = false;
return r;
}
public void onDirty(Runnable onDirty)
{
_onDirty = onDirty;
}
public void dirty()
{
if(_parent == this)
_dirty = true;
else
_parent.dirty();
if(_onDirty != null)
{
_onDirty.run();
_onDirty = null;
}
}
protected boolean initSContext()
{
if(_rec != null) _rec.checkLock();
if(_fullUndo) return false;
if(_sctx == null)
{
if(_onDirty != null)
{
_onDirty.run();
_onDirty = null;
}
_parent.dirty();
_sctx = current();
}
return true;
}
public void addFullUndo()
{
if(!initSContext()) return;
_sctx.addOnRollback(new Runnable()
{
private final B _saved = _bean.clone();
@Override
public void run()
{
_bean.assign(_saved);
}
});
_fullUndo = true;
}
public void reset()
{
addFullUndo();
_bean.reset();
}
public void assign(B b)
{
if(b == _bean) return;
addFullUndo();
_bean.assign(b);
}
public void assign(Safe<B> s)
{
assign(s.unsafe());
}
public OctetsStream marshal(OctetsStream s)
{
return _bean.marshal(s);
}
public OctetsStream unmarshal(OctetsStream s) throws MarshalException
{
addFullUndo();
return _bean.unmarshal(s);
}
@Override
public B clone()
{
return _bean.clone();
}
@Override
public int hashCode()
{
return _bean.hashCode();
}
@Override
public boolean equals(Object o)
{
return _bean.equals(o instanceof Safe<?> ? ((Safe<?>)o)._bean : o);
}
@Override
public int compareTo(B b)
{
return _bean.compareTo(b);
}
@Override
public String toString()
{
return _bean.toString();
}
public StringBuilder toJson(StringBuilder s)
{
return _bean.toJson(s);
}
public StringBuilder toLua(StringBuilder s)
{
return _bean.toLua(s);
}
}
public interface Rec
{
TableBase<?> getTable();
Object getKey();
long getKeyLong();
Object getValue();
void checkLock();
}
static final class Record<K, V extends Bean<V>, S extends Safe<V>> implements Rec
{
private final Table<K, V, S> _table;
private final K _key;
private final S _value;
private final int _lockId;
Record(Table<K, V, S> table, K key, S value)
{
_table = table;
_key = key;
_value = value;
_lockId = table.lockId(key);
}
@Override
public TableBase<V> getTable()
{
return _table;
}
@Override
public K getKey()
{
return _key;
}
@Override
public long getKeyLong()
{
return _key instanceof Number ? ((Number)_key).longValue() : 0;
}
@Override
public S getValue()
{
return _value;
}
@Override
public void checkLock()
{
if(!Procedure.isLockedByCurrentThread(_lockId))
throw new IllegalAccessError("write unlocked record! table=" + _table.getTableName() + ",key=" + _key);
}
}
static final class RecordLong<V extends Bean<V>, S extends Safe<V>> implements Rec
{
private final TableLong<V, S> _table;
private final long _key;
private final S _value;
private final int _lockId;
RecordLong(TableLong<V, S> table, long key, S value)
{
_table = table;
_key = key;
_value = value;
_lockId = table.lockId(key);
}
@Override
public TableBase<V> getTable()
{
return _table;
}
@Override
public Long getKey()
{
return _key;
}
@Override
public long getKeyLong()
{
return _key;
}
@Override
public S getValue()
{
return _value;
}
@Override
public void checkLock()
{
if(!Procedure.isLockedByCurrentThread(_lockId))
throw new IllegalAccessError("write unlocked record! table=" + _table.getTableName() + ",key=" + _key);
}
}
private final List<Record<?, ?, ?>> _records = new ArrayList<>();
private final List<RecordLong<?, ?>> _recordLongs = new ArrayList<>();
private final List<Runnable> _onRollbacks = new ArrayList<>();
private final List<Runnable> _onCommits = new ArrayList<>();
private boolean _hasDirty;
public static SContext current()
{
return ((ProcThread)Thread.currentThread()).sctx;
}
<K, V extends Bean<V>, S extends Safe<V>> S addRecord(Table<K, V, S> table, K key, V value)
{
@SuppressWarnings("unchecked")
S s = (S)value.safe(null);
Record<K, V, S> rec = new Record<>(table, key, s);
s.record(rec);
_records.add(rec);
return s;
}
<V extends Bean<V>, S extends Safe<V>> S addRecord(TableLong<V, S> table, long key, V value)
{
@SuppressWarnings("unchecked")
S s = (S)value.safe(null);
RecordLong<V, S> rec = new RecordLong<>(table, key, s);
s.record(rec);
_recordLongs.add(rec);
return s;
}
@SuppressWarnings("unchecked")
<K, V extends Bean<V>, S extends Safe<V>> S getRecord(Table<K, V, S> table, K key)
{
for(Record<?, ?, ?> r : _records)
{
if(r.getKey().equals(key) && r.getTable() == table)
return (S)r.getValue();
}
return null;
}
@SuppressWarnings("unchecked")
<V extends Bean<V>, S extends Safe<V>> S getRecord(TableLong<V, S> table, long key)
{
for(RecordLong<?, ?> r : _recordLongs)
{
if(r.getKeyLong() == key && r.getTable() == table)
return (S)r.getValue();
}
return null;
}
public boolean hasDirty()
{
if(_hasDirty) return true;
for(Record<?, ?, ?> r : _records)
{
if(r._value.isDirty())
return true;
}
for(RecordLong<?, ?> r : _recordLongs)
{
if(r._value.isDirty())
return true;
}
return false;
}
public void addOnCommit(Runnable r)
{
_onCommits.add(r);
}
public void addOnRollback(Runnable r)
{
_onRollbacks.add(r);
}
void addOnRollbackDirty(Runnable r)
{
_onRollbacks.add(r);
_hasDirty = true;
}
void commit()
{
_onRollbacks.clear();
for(Record<?, ?, ?> r : _records)
{
if(r._value.isDirtyAndClear())
r._table.modify(r._key, r._value.unsafe());
}
_records.clear();
for(RecordLong<?, ?> r : _recordLongs)
{
if(r._value.isDirtyAndClear())
r._table.modify(r._key, r._value.unsafe());
}
_recordLongs.clear();
for(Runnable r : _onCommits)
{
try
{
r.run();
}
catch(Throwable e)
{
Log.log.error("oncommit exception:", e);
}
}
_onCommits.clear();
_hasDirty = false;
}
void rollback()
{
_records.clear();
_recordLongs.clear();
_onCommits.clear();
for(int i = _onRollbacks.size(); --i >= 0;)
{
try
{
_onRollbacks.get(i).run();
}
catch(Throwable e)
{
Log.log.error("rollback exception:", e);
}
}
_onRollbacks.clear();
_hasDirty = false;
}
}