
de.uniks.networkparser.HistoryIdMap Maven / Gradle / Ivy
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
© 2015 - 2025 Weber Informatics LLC | Privacy Policy