
de.uniks.networkparser.HistoryIdMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of SDMLib Show documentation
Show all versions of SDMLib Show documentation
SDMLib is a light weight modeling library. SDMLib intentionally comes without any tool or editor.
package de.uniks.networkparser;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sdmlib.replication.ChangeEvent;
import de.uniks.networkparser.interfaces.ObjectCondition;
import de.uniks.networkparser.interfaces.SendableEntityCreator;
import de.uniks.networkparser.json.JsonArray;
import de.uniks.networkparser.json.JsonObject;
import de.uniks.networkparser.json.JsonTokener;
import de.uniks.networkparser.list.SimpleList;
import de.uniks.networkparser.list.SimpleSet;
public class HistoryIdMap extends IdMap
{
private static final String LIFE = "life";
public static final String POINTER = "pointer";
public static final String ADD = "add";
public static final String PLAIN = "plain";
private ObjectCondition updateListenerFromUser;
public long decodeChanges(String value)
{
long noOfChangesApplied = 0;
// read all changes into single json array
JsonArray result = new JsonArray();
result.clear();
JsonTokener tokener = new JsonTokener();
tokener.withBuffer(value);
while (!tokener.isEnd())
{
tokener.parseToEntity(result);
}
// apply changes if valid
for (int i = 0; i < result.size(); i++)
{
JsonObject jsonChange = (JsonObject) result.get(i);
noOfChangesApplied += applyJsonChange(jsonChange);
}
// System.out.println(result.toString(3));
return noOfChangesApplied;
}
public static class SimpleAttrMap extends LinkedHashMap
{
public AttrTimeStampMap getOrCreate(String id)
{
AttrTimeStampMap attrMap = this.get(id);
if (attrMap == null)
{
attrMap = new AttrTimeStampMap();
this.put(id, attrMap);
}
return attrMap;
}
}
public static class AttrTimeStampMap extends LinkedHashMap
{
public void dumpHistory(StringBuffer buf)
{
for (java.util.Map.Entry e : this.entrySet())
{
String format = String.format(" %12s %d\n", e.getKey(), e.getValue());
buf.append(format);
}
}
}
public class ToManyRefsMap extends LinkedHashMap
{
public RefTimeStampsMap getOrCreate(String id)
{
RefTimeStampsMap refTimeStampsMap = this.get(id);
if (refTimeStampsMap == null)
{
refTimeStampsMap = new RefTimeStampsMap();
this.put(id, refTimeStampsMap);
}
return refTimeStampsMap;
}
}
public class RefTimeStampsMap extends LinkedHashMap
{
public TimeStampMap getOrCreate(String prop)
{
TimeStampMap timeStampMap = this.get(prop);
if (timeStampMap == null)
{
timeStampMap = new TimeStampMap();
this.put(prop, timeStampMap);
}
return timeStampMap;
}
public RefTime getById(String id)
{
String owner = ownerId(id);
TimeStampMap timeStampMap = this.get(owner);
if (timeStampMap == null)
{
return null;
}
String baseId = baseId(id);
return timeStampMap.get(baseId);
}
public void putById(String id, RefTime refTime)
{
String owner = ownerId(id);
TimeStampMap timeStampMap = removedObjects.getOrCreate(owner);
String baseId = baseId(id);
timeStampMap.put(baseId, refTime);
}
public void dumpHistory(StringBuffer buf)
{
for (java.util.Map.Entry e : this.entrySet())
{
buf.append(String.format(" %12s\n", e.getKey()));
TimeStampMap timeStampMap = e.getValue();
for (java.util.Map.Entry t : timeStampMap.entrySet())
{
buf.append(String.format(" %12s %s %d\n", " ",
smallId(t.getKey()),
t.getValue().timeStamp));
}
buf.append(String.format(" %12s\n", "rem"));
for (java.util.Map.Entry r : timeStampMap.removedTimeStamps.entrySet())
{
String key = r.getKey().substring(r.getKey().lastIndexOf('.') + 1);
buf.append(String.format(" %12s %s %d\n", " ",
key,
r.getValue().timeStamp));
}
}
}
}
public static class TimeStampMap extends LinkedHashMap
{
private ArrayList sortedTimeStamps = new ArrayList<>();
private LinkedHashMap removedTimeStamps = null;
public ArrayList getSortedTimeStamps()
{
return sortedTimeStamps;
}
public LinkedHashMap getRemovedTimeStamps()
{
if (removedTimeStamps == null)
{
removedTimeStamps = new LinkedHashMap<>();
}
return removedTimeStamps;
}
public void put(String key, Long value)
{
String baseId = baseId(key);
RefTime removalTime = getRemovedTimeStamps().remove(baseId);
if (removalTime != null)
{
if (removalTime.timeStamp >= value)
{
throw new UnsupportedOperationException();
}
}
RefTime refTime = this.get(key);
if (refTime != null)
{
sortedTimeStamps.remove(refTime);
}
else
{
refTime = new RefTime(key, value);
this.put(key, refTime);
}
// add time stamp to sortedTimeStamps
Long lastValue = 0L;
if (sortedTimeStamps.size() > 0)
{
lastValue = sortedTimeStamps.get(sortedTimeStamps.size() - 1).timeStamp;
}
sortedTimeStamps.add(refTime);
if (value < lastValue)
{
sortedTimeStamps.sort(null);
}
return;
}
@Override
public boolean remove(Object key, Object value)
{
RefTime oldRefTime = this.remove(key);
if (oldRefTime == null)
{
oldRefTime = new RefTime((String)key, (Long)value);
}
sortedTimeStamps.remove(oldRefTime);
getRemovedTimeStamps().put(baseId((String) key), oldRefTime);
return true;
}
}
public static class RefTime implements Comparable
{
public String objId;
public Long timeStamp;
public RefTime(String key, Long value)
{
this.objId = key;
this.timeStamp = value;
}
@Override
public int compareTo(RefTime o)
{
int compareTo = timeStamp.compareTo(o.timeStamp);
if (compareTo == 0)
{
compareTo = this.objId.compareTo(o.objId);
}
return compareTo;
}
@Override
public String toString()
{
return this.objId + "#" + this.timeStamp;
}
}
public SimpleAttrMap simpleAttrChanges = new SimpleAttrMap();
public ToManyRefsMap toManyChanges = new ToManyRefsMap();
private LinkedHashMap rebirthCounters = new LinkedHashMap<>();
public LinkedHashMap getRebirthCounters()
{
return rebirthCounters;
}
private RefTimeStampsMap removedObjects = new RefTimeStampsMap();
public RefTimeStampsMap getRemovedObjects()
{
return removedObjects;
}
public HistoryIdMap(String owner)
{
this.withSession(owner);
}
@Override
public IdMap withListener(ObjectCondition updateListener)
{
updateListenerFromUser = updateListener;
return super.withListener((ObjectCondition) update -> wrapUpdate(update));
}
private long changeTime = 0;
public long getChangeTime()
{
long newChangeTime = System.nanoTime();
if (newChangeTime > changeTime)
{
changeTime = newChangeTime;
}
else
{
changeTime++;
}
return changeTime;
}
private boolean wrapUpdate(Object update)
{
// create history json changes
SimpleEvent evt = (SimpleEvent) update;
JsonArray jsonArray = new JsonArray();
JsonObject oldJson = (JsonObject) evt.getEntity();
recodeChanges(jsonArray, oldJson);
boolean result = updateListenerFromUser.update(jsonArray);
return result;
}
public Object getOrCreateObject(String id)
{
Object obj = this.getObject(id);
if (obj == null)
{
// create a new one
String className = classNameFromId(id);
SimpleList candidates = new SimpleList();
SendableEntityCreator creator = this.getCreator(className, false, candidates);
if(creator == null && candidates.size()>0) {
creator = candidates.first();
for(SendableEntityCreator candidate : candidates) {
String name = candidate.getClass().getName();
if(name.startsWith("de.uniks.networkparser.")) {
continue;
}
creator = candidate;
}
}
obj = creator.getSendableInstance(false);
this.put(id, obj);
// add to re-birth, if necessary
long lifeCountFromId = lifeCountFromId(id);
if (lifeCountFromId > 1)
{
String baseId = baseId(id);
rebirthCounters.put(baseId, lifeCountFromId);
}
}
return obj;
}
private String classNameFromId(String id)
{
int pos = id.indexOf(':');
String className = id.substring(pos + 1);
return className;
}
private long applyJsonChange(JsonObject jsonChange)
{
String id = jsonChange.getString("id");
String opCode = jsonChange.getString("opCode");
String prop = jsonChange.getString("prop");
Object newValue = jsonChange.get("newValue");
Object oldValue = jsonChange.get("oldValue");
long newChangeNanos = jsonChange.getLong("timeStamp");
Logger.getGlobal().setLevel(Level.SEVERE);
Logger.getGlobal().info(dumpHistory());
// the object might have been removed meanwhile
if (idIsDead(id))
{
return 0;
}
// look up old timestamps
if (opCode.equals(PLAIN) || opCode.equals(POINTER))
{
AttrTimeStampMap attrTimeStampMap = simpleAttrChanges.getOrCreate(id);
Long oldTimeStamp = attrTimeStampMap.get(prop);
if (oldTimeStamp != null && oldTimeStamp >= newChangeNanos)
{
// reject new change
return 0;
}
// its new, apply and record it
Object object = this.getOrCreateObject(id);
SendableEntityCreator creator = this.getCreatorClass(object);
if (opCode.equals(POINTER))
{
if (newValue != null)
{
newValue = this.getOrCreateObject((String) newValue);
}
else
{
newValue = null;
}
}
creator.setValue(object, prop, newValue, SendableEntityCreator.UPDATE);
attrTimeStampMap.put(prop, newChangeNanos);
Logger.getGlobal().info(dumpHistory() + "");
}
else if (opCode.equals(ADD))
{
String addId = (String) newValue;
if (idIsDead(addId))
{
return 0;
}
// check for removal time stamp
RefTimeStampsMap refTimeStampsMap = toManyChanges.getOrCreate(id);
TimeStampMap timeStampMap = refTimeStampsMap.getOrCreate(prop);
RefTime refTime = timeStampMap.get(addId);
if (refTime != null && refTime.timeStamp >= newChangeNanos)
{
return 0;
}
if (timeStampMap.removedTimeStamps != null)
{
refTime = timeStampMap.removedTimeStamps.get(addId);
if (refTime != null && refTime.timeStamp >= newChangeNanos)
{
return 0;
}
}
timeStampMap.put((String) newValue, newChangeNanos);
// apply change
Object object = this.getOrCreateObject(id);
SendableEntityCreator creator = this.getCreatorClass(object);
Object addObject = this.getOrCreateObject(addId);
creator.setValue(object, prop, addObject, ADD);
adjustPosition(id, prop, addId, object, addObject, creator, timeStampMap, newChangeNanos);
Logger.getGlobal().info(dumpHistory() + "");
}
else if (opCode.equals(SendableEntityCreator.REMOVE))
{
String remId = (String) oldValue;
RefTimeStampsMap refTimeStampsMap = toManyChanges.getOrCreate(id);
TimeStampMap timeStampMap = refTimeStampsMap.getOrCreate(prop);
RefTime refTime = timeStampMap.get(remId);
if (refTime != null && refTime.timeStamp >= newChangeNanos)
{
return 0;
}
if (timeStampMap.removedTimeStamps != null)
{
refTime = timeStampMap.removedTimeStamps.get(remId);
if (refTime != null && refTime.timeStamp >= newChangeNanos)
{
return 0;
}
}
timeStampMap.remove((String) oldValue, newChangeNanos);
//apply change
Object object = this.getObject(id);
SendableEntityCreator creator = this.getCreatorClass(object);
Object remObject = this.getObject(remId);
if (remObject == null)
{
return 0;
}
creator.setValue(object, prop, remObject, SendableEntityCreator.REMOVE);
Logger.getGlobal().info(dumpHistory() + "");
}
else if (opCode.equals(SendableEntityCreator.REMOVE_YOU))
{
// store removed id
// split object number without life counter from id
String baseId = baseId(id);
RefTime refTime = new RefTime(id, newChangeNanos);
String owner = ownerId(id);
TimeStampMap timeStampMap = removedObjects.getOrCreate(owner);
timeStampMap.put(baseId, refTime);
// remove old timestamps
simpleAttrChanges.remove(id);
toManyChanges.remove(id);
// actually remove the object
Object object = this.getObject(id);
if (object != null)
{
// this.removeObj(object, true);
// SendableEntityCreator creator = this.getCreatorClass(object);
// creator.removeObject(object);
SendableEntityCreator creator = this.getCreatorClass(object);
creator.setValue(object, "this", null, SendableEntityCreator.REMOVE_YOU);
}
Logger.getGlobal().info(dumpHistory() + "");
}
else
{
throw new NotYetImplementedException();
}
return 1;
}
public String smallId(String id)
{
int pos = id.indexOf('.');
pos = id.indexOf('.', pos + 1);
int colonPos = id.indexOf(':');
if(colonPos<0) {
return id;
}
String result = id.substring(pos + 1, colonPos);
return result;
}
public JsonArray getCurrentChanges()
{
Logger.getGlobal().setLevel(Level.SEVERE);
Logger.getGlobal().info(dumpHistory());
JsonArray result = new JsonArray();
// plain and pointer
for (Entry ac : simpleAttrChanges.entrySet())
{
String id = ac.getKey();
String opCode = null;
AttrTimeStampMap timeStampMap = ac.getValue();
for (Entry ts : timeStampMap.entrySet())
{
String prop = ts.getKey();
long nanoTime = ts.getValue();
Object object = this.getObject(id);
SendableEntityCreator creator = this.getCreatorClass(object);
Object newValue = creator.getValue(object, prop);
if (newValue == null)
{
opCode = POINTER;
}
else if (this.getCreatorClass(newValue) != null)
{
opCode = POINTER;
newValue = this.getId(newValue);
}
else
{
opCode = PLAIN;
}
JsonObject jsonChange = plainCreateJsonChange(id, opCode, prop, newValue, null, nanoTime);
result.add(jsonChange);
}
}
// add
for (Entry ac : toManyChanges.entrySet())
{
String id = ac.getKey();
RefTimeStampsMap refTimeStampsMap = ac.getValue();
for ( Entry rt : refTimeStampsMap.entrySet())
{
String prop = rt.getKey();
TimeStampMap timeStampMap = rt.getValue();
for ( Entry rc : timeStampMap.entrySet())
{
String otherId = rc.getKey();
RefTime refTime = rc.getValue();
long timeStamp = refTime.timeStamp;
JsonObject jsonChange = plainCreateJsonChange(id, ADD, prop, otherId, null, timeStamp);
result.add(jsonChange);
}
// remove
for ( Entry rr : timeStampMap.removedTimeStamps.entrySet())
{
RefTime refTime = rr.getValue();
String otherId = refTime.objId;
long timeStamp = refTime.timeStamp;
JsonObject jsonChange = plainCreateJsonChange(id, SendableEntityCreator.REMOVE, prop, null, otherId, timeStamp);
result.add(jsonChange);
}
}
}
// removeYou
for ( Entry rts : removedObjects.entrySet())
{
String owner = rts.getKey();
TimeStampMap timeStampMap = rts.getValue();
for ( Entry ro : timeStampMap.entrySet())
{
RefTime refTime = ro.getValue();
String id = refTime.objId;
long timeStamp = refTime.timeStamp;
JsonObject jsonChange = plainCreateJsonChange(id, SendableEntityCreator.REMOVE_YOU, LIFE, -1, 1, timeStamp);
result.add(jsonChange);
}
}
return result;
}
public String dumpHistory()
{
StringBuffer buf = new StringBuffer("\n");
for (Entry ac : simpleAttrChanges.entrySet())
{
buf.append(smallId(ac.getKey())).append("\n");
// add attrs
AttrTimeStampMap attrMap = ac.getValue();
attrMap.dumpHistory(buf);
}
buf.append("\n");
for (Entry rc : toManyChanges.entrySet())
{
buf.append(smallId(rc.getKey())).append("\n");
// add refs
RefTimeStampsMap refMap = rc.getValue();
refMap.dumpHistory(buf);
}
buf.append("re-birth\n");
for (Entry bo : rebirthCounters.entrySet())
{
String string = String.format(" %12s %d\n", bo.getKey(), bo.getValue());
buf.append(string);
}
buf.append("removeYou\n");
for (Entry ro : removedObjects.entrySet())
{
TimeStampMap timeStampMap = ro.getValue();
for (Entry te : timeStampMap.entrySet())
{
RefTime refTime = te.getValue();
String string = String.format(" %12s %d\n", ro.getKey() + "." + smallId(refTime.objId), refTime.timeStamp);
buf.append(string);
}
}
return buf.toString();
}
private void adjustPosition(String id, String prop, String addId, Object object, Object addObject, SendableEntityCreator creator, TimeStampMap timeStampMap, long newChangeNanos)
{
// find pos
int pos = -1;
for (int i = timeStampMap.sortedTimeStamps.size() - 1; i >= 0; i--)
{
RefTime refTime = timeStampMap.sortedTimeStamps.get(i);
if (refTime.objId.equals(addId) && refTime.timeStamp == newChangeNanos)
{
// found the position; check position in SimpleSet
Object container = creator.getValue(object, prop);
if(container == null) {
return;
}
SimpleSet