package com.plectix.simulator.staticanalysis.stories.storage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
import com.plectix.simulator.staticanalysis.stories.ActionOfAEvent;
import com.plectix.simulator.staticanalysis.stories.MarkOfEvent;
import com.plectix.simulator.staticanalysis.stories.TypeOfWire;
import com.plectix.simulator.staticanalysis.stories.compressions.CompressionPassport;
import com.plectix.simulator.staticanalysis.stories.graphs.StoriesGraphs;
public final class StoragePassport implements CompressionPassport {
private final WireStorageInterface storage;
private final Map<String, Set<Long>> iDsByType;
private final Map<Long, ArrayList<WireHashKey>> wiresByIdAgent;
private final TreeMap<Long, AtomicEvent<?>> allEventsByNumber;
private final LinkedHashMap<Long, String> typeById;
private SwapRecord swap;
public StoragePassport(WireStorageInterface abstractStorage) {
storage = abstractStorage;
iDsByType = new LinkedHashMap<String, Set<Long>>();
wiresByIdAgent = new LinkedHashMap<Long, ArrayList<WireHashKey>>();
allEventsByNumber = new TreeMap<Long, AtomicEvent<?>>();
typeById = new LinkedHashMap<Long, String>();
swap = null;
prepareForStrong();
}
public final void prepareForStrong() {
for (WireHashKey wk : storage.getStorageWires().keySet()) {
long id = wk.getAgentId();
String type = storage.getStoriesAgentTypesStorage().getType(
storage.getIteration(), id);
if (iDsByType.get(type) == null) {
iDsByType.put(type, new LinkedHashSet<Long>());
}
iDsByType.get(type).add(id);
typeById.put(id, type);
if (wiresByIdAgent.get(id) == null) {
ArrayList<WireHashKey> wires = new ArrayList<WireHashKey>();
wiresByIdAgent.put(id, wires);
}
wiresByIdAgent.get(id).add(wk);
for (Entry<Long, AtomicEvent<?>> entry : storage.getStorageWires()
.get(wk).entrySet()) {
if (allEventsByNumber.get(entry.getKey()) == null) {
allEventsByNumber.put(entry.getKey(), entry.getValue());
}
}
}
// trouble with -1 initial event TODO
storage.getStoriesAgentTypesStorage().update(storage.getIteration(),
typeById);
}
public final void removeEventWithMarkDelete() throws StoryStorageException {
LinkedHashSet<Long> mayBeEmptyAgents = new LinkedHashSet<Long>();
LinkedList<Long> deletedEvents = new LinkedList<Long>();
// fill deletedAgents and mayBeEmptyWires
// correct storageWires
for (Event event : storage.getEvents()) {
if (event.getMark() == MarkOfEvent.DELETED) {
deletedEvents.add(event.getStepId());
for (WireHashKey wk : event.getAtomicEvents().keySet()) {
storage.getStorageWires().get(wk).remove(event.getStepId());
mayBeEmptyAgents.add(wk.getAgentId());
}
}
}
// TODO ?
if (deletedEvents.isEmpty()) {
return;
}
for (Long i : deletedEvents) {
storage.getEvents().remove(allEventsByNumber.get(i).getContainer());
allEventsByNumber.remove(i);
}
clearEmptyWires(mayBeEmptyAgents);
}
private void clearEmptyWires(LinkedHashSet<Long> mayBeEmptyAgents)
throws StoryStorageException {
boolean tryRemove;
for (Long id : mayBeEmptyAgents) {
tryRemove = true;
for (WireHashKey wk : wiresByIdAgent.get(id)) {
if (storage.getInformationAboutWires()
.getUnresolvedModifyCount(wk) != 0) {
tryRemove = false;
break;
}
}
if (tryRemove) {
if (storage.removeWire(wiresByIdAgent.get(id))) {
wiresByIdAgent.remove(id);
String type = typeById.remove(id);
iDsByType.get(type).remove(id);
if (iDsByType.get(type).isEmpty()) {
iDsByType.remove(type);
}
}
if (storage.initialEvent().getAtomicEvents().isEmpty()) {
storage.getEvents().remove(storage.initialEvent());
allEventsByNumber.remove(Long.valueOf(-1));
storage.initialEvent().onlySetMark(null);
}
}
}
}
public final EventInterface swapAgents(List<Long> agents1,
List<Long> agents2, Long firstEventId, boolean swapTop)
throws StoryStorageException {
SwapRecord sw = new SwapRecord();
sw.setAgents1(agents1);
sw.setAgents2(agents2);
sw.setFirstEventId(firstEventId);
sw.setSwapTop(swapTop);
// System.out.println("swap ++++++++++++++++++++++++++");
// print(sw);
// StoryCorrectness.testOfStates(this.getStorage());
doSwap(sw);
swap = sw;
// System.out.println("after");
// StoryCorrectness.testLinks(this.getStorage());
return sw.getOtherSide();
}
private void print(SwapRecord sw) {
System.out.println(sw.getFirstEventId() + " " + sw.isSwapTop());
for (int i = 0; i < sw.getAgents1().size(); i++) {
System.out.println(sw.getAgents1().get(i) + " "
+ sw.getAgents2().get(i));
}
}
public final void undoSwap() throws StoryStorageException {
// System.out.println("undo ++++++++++++++++++++++++++");
// print(swap);
doSwap(swap);
// StoryCorrectness.testLinks(this.getStorage());
}
public final String getAgentType(long agentId) throws StoryStorageException {
if (typeById.get(agentId) == null) {
throw new StoryStorageException("no exist this agent " + agentId);
}
return typeById.get(agentId);
}
public final boolean isAbleToSwap(long agentId1, long agentId2) {
LinkedHashSet<Integer> ag1 = new LinkedHashSet<Integer>();
LinkedHashSet<Integer> ag2 = new LinkedHashSet<Integer>();
for (WireHashKey wk : wiresByIdAgent.get(agentId1)) {
ag1.add(wk.getSmallHash());
if (!storage.tryToSwap(agentId2, wk)) {
return false;
}
}
for (WireHashKey wk : wiresByIdAgent.get(agentId2)) {
ag2.add(wk.getSmallHash());
if (!storage.tryToSwap(agentId1, wk)) {
return false;
}
}
for (Integer t : ag1) {
if (!ag2.contains(t)) {
return false;
}
}
for (Integer t : ag2) {
if (!ag1.contains(t)) {
return false;
}
}
return true;
}
private final void doSwap(SwapRecord sw) throws StoryStorageException {
int size = sw.getAgents1().size();
if (size != sw.getAgents2().size()) {
throw new StoryStorageException("different size of swapping list");
}
setOtherSide(sw);
setMapWire(sw);
// StoryCorrectness.testLinks(storage);
for (int i = 0; i < size; i++) {
storage.replaceWireToWire(sw.mapWire.get(i), sw.getFirstEventId(),
sw.isSwapTop(), allEventsByNumber);
}
updateInformationAboutWires(sw, size);
}
private void updateInformationAboutWires(SwapRecord sw, int size)
throws StoryStorageException {
// StoryCorrectness.testOfStates(this.getStorage());
correctLinkStates(sw, size);
// StoryCorrectness.testOfStates(this.getStorage());
Set<WireHashKey> sets = collectWires(sw, size, true);
// StoryCorrectness.testOfStates(this.getStorage());
storage.updateWires(sets);
}
private Set<WireHashKey> correctLinkStates(SwapRecord sw, int size)
throws StoryStorageException {
HashSet<WireHashKey> touchedWires = collectWires(sw, size, false);
NavigableMap<Long, AtomicEvent<?>> map;
Map<WireHashKey, TreeMap<Long, AtomicEvent<?>>> storageWires = this.storage
.getStorageWires();
Map<Long, Long> numbers = new LinkedHashMap<Long, Long>();
for (int i = 0; i < sw.getAgents1().size(); i++) {
numbers.put(sw.getAgents2().get(i), sw.getAgents1().get(i));
numbers.put(sw.getAgents1().get(i), sw.getAgents2().get(i));
}
for (WireHashKey wk : touchedWires) {
if (storageWires.get(wk) == null) {
throw new StoryStorageException("empty wire");
}
if (!sw.isSwapTop()) {
map = storageWires.get(wk).tailMap(sw.getFirstEventId(), true);
} else {
map = storageWires.get(wk).headMap(sw.getFirstEventId(), true);
}
if (map != null) {
correctWire(wk, map, numbers);
}
// StoryCorrectness.testOfStates(storage);
}
// StoryCorrectness.testLinks(storage);
return touchedWires;
}
private void correctWire(WireHashKey wk,
NavigableMap<Long, AtomicEvent<?>> map, Map<Long, Long> numbers)
throws StoryStorageException {
Entry<Long, AtomicEvent<?>> runner = map.firstEntry();
while (runner != null) {
AtomicEvent<?> value = runner.getValue();
Long key = runner.getKey();
Object beforeState = value.getState().getBeforeState();
Object afterState = value.getState().getAfterState();
StateOfLink sl;
Long long1;
if (beforeState != null) {
sl = (StateOfLink) beforeState;
long1 = numbers.get(sl.getAgentId());
if (long1 != null) {
String siteName = sl.getSiteName();
((AbstractState<StateOfLink>) value.getState())
.setBeforeStateOver(new StateOfLink(long1, siteName));
}
}
if (afterState != null) {
sl = (StateOfLink) afterState;
long1 = numbers.get(sl.getAgentId());
if (long1 != null) {
String siteName = sl.getSiteName();
((AbstractState<StateOfLink>) value.getState())
.setAfterState(new StateOfLink(long1, siteName));
}
}
// if (value.getType() == ActionOfAEvent.TEST) {
// runner = map.lowerEntry(key);
// continue;
// }
// Object afterState = value.getState().getAfterState();
// if (afterState == null) {
// throw new StoryStorageException("empty state in mod point");
// }
// StateOfLink state = (StateOfLink) afterState;
// if (state.isFree()) {
// // for debag
// checkFree(map, key);
// } else {
//
// if (value.getType() == ActionOfAEvent.MODIFICATION
// && afterState == null) {
//
// } else {
// boolean b = true;
// WireHashKey w = createWireByStateOfLink(state);
// AtomicEvent<?> atomicEvent = this.storage.getStorageWires()
// .get(w).get(key);
// if (w.equals(wk)||atomicEvent == null) {
// b = false;
// } else {
// Object afterState2 = atomicEvent.getState()
// .getAfterState();
// if (afterState2 == null) {
// b = false;
// } else {
// StateOfLink state2 = (StateOfLink) afterState2;
// if (state2.isFree()||!createWireByStateOfLink(state2).equals(wk))
// {
// b = false;
// }
// }
//
// }
// if (!b) {
// String siteName = state.getSiteName();
// long agentId = state.getAgentId();
// int size = sw.getAgents1().size();
// long newAgentId = -1;
// for (int i = 0; i < size; i++) {
// if (sw.getAgents1().get(i).equals(agentId)) {
// newAgentId = sw.getAgents2().get(i);
// }
// if (sw.getAgents2().get(i).equals(agentId)) {
// newAgentId = sw.getAgents1().get(i);
// }
// }
// if (newAgentId != -1) {
// // throw new StoryStorageException();
// StateOfLink newState = new StateOfLink(newAgentId,
// siteName);
// changeState(map, key, newState,state);
// state.setState(newState);
// }
// }
// }
// }
runner = map.higherEntry(key);
}
}
private void changeState(NavigableMap<Long, AtomicEvent<?>> map, Long key,
StateOfLink newState, StateOfLink oldState)
throws StoryStorageException {
Entry<Long, AtomicEvent<?>> runner = map.higherEntry(key);
while (runner != null) {
AtomicEvent<?> value = runner.getValue();
Object beforeState = value.getState().getBeforeState();
StateOfLink link;
if (value.getType() == ActionOfAEvent.TEST) {
if (beforeState == null) {
throw new StoryStorageException(
"empty before state in TEST event");
}
link = (StateOfLink) beforeState;
if (!link.equals(oldState)) {
throw new StoryStorageException();
}
((AbstractState<StateOfLink>) value.getState())
.setBeforeStateOver(newState);
}
if (value.getType() == ActionOfAEvent.TEST_AND_MODIFICATION) {
if (beforeState == null) {
throw new StoryStorageException(
"empty before state in TEST event");
}
link = (StateOfLink) beforeState;
if (!link.equals(oldState)) {
throw new StoryStorageException();
}
((AbstractState<StateOfLink>) value.getState())
.setBeforeStateOver(newState);
return;
}
if (value.getType() == ActionOfAEvent.MODIFICATION) {
if (beforeState != null) {
link = (StateOfLink) beforeState;
if (!link.equals(oldState)) {
throw new StoryStorageException();
}
((AbstractState<StateOfLink>) value.getState())
.setBeforeStateOver(newState);
}
return;
}
runner = map.lowerEntry(key);
}
}
private void checkFree(NavigableMap<Long, AtomicEvent<?>> map, Long key)
throws StoryStorageException {
Entry<Long, AtomicEvent<?>> runner = map.ceilingEntry(key);
while (runner != null) {
AtomicEvent<?> value = runner.getValue();
Object beforeState = value.getState().getBeforeState();
StateOfLink link;
if (value.getType() == ActionOfAEvent.TEST) {
if (beforeState == null) {
throw new StoryStorageException(
"empty before state in TEST event");
}
link = (StateOfLink) beforeState;
if (!link.isFree()) {
throw new StoryStorageException("busy state without free!");
}
}
if (value.getType() == ActionOfAEvent.TEST_AND_MODIFICATION) {
if (beforeState == null) {
throw new StoryStorageException(
"empty before state in TEST event");
}
link = (StateOfLink) beforeState;
if (!link.isFree()) {
throw new StoryStorageException("busy state without free!");
}
return;
}
if (value.getType() == ActionOfAEvent.MODIFICATION) {
if (beforeState != null) {
link = (StateOfLink) beforeState;
if (!link.isFree()) {
throw new StoryStorageException(
"busy state without free!");
}
}
return;
}
runner = map.lowerEntry(key);
}
}
private HashSet<WireHashKey> collectWires(SwapRecord sw, int size, boolean b)
throws StoryStorageException {
HashSet<WireHashKey> set = new LinkedHashSet<WireHashKey>();
HashSet<WireHashKey> set2 = new LinkedHashSet<WireHashKey>();
for (int i = 0; i < size; i++) {
if (b) {
set.addAll(sw.mapWire.get(i).values());
set.addAll(sw.mapWire.get(i).keySet());
} else {
for (WireHashKey wk : sw.mapWire.get(i).values()) {
if (wk.getTypeOfWire() == TypeOfWire.LINK_STATE) {
set.add(wk);
}
}
for (WireHashKey wk : sw.mapWire.get(i).keySet()) {
if (wk.getTypeOfWire() == TypeOfWire.LINK_STATE) {
set.add(wk);
}
}
}
}
Map<WireHashKey, TreeMap<Long, AtomicEvent<?>>> storageWires = this.storage
.getStorageWires();
HashSet<AtomicEvent<?>> bigset = new LinkedHashSet<AtomicEvent<?>>();
if (sw.isSwapTop()) {
for (WireHashKey wk : set) {
if (!b && wk.getTypeOfWire() != TypeOfWire.LINK_STATE)
continue;
Map<Long, AtomicEvent<?>> map = storageWires.get(wk).headMap(
sw.getFirstEventId());
bigset.addAll(map.values());
}
} else {
for (WireHashKey wk : set) {
if (!b && wk.getTypeOfWire() != TypeOfWire.LINK_STATE)
continue;
Map<Long, AtomicEvent<?>> map = storageWires.get(wk).tailMap(
sw.getFirstEventId(), true);
bigset.addAll(map.values());
}
}
for (AtomicEvent<?> ae : bigset) {
Object beforeState = ae.getState().getBeforeState();
Object afterState = ae.getState().getAfterState();
if (beforeState != null) {
if (!b && !(beforeState instanceof StateOfLink)) {
throw new StoryStorageException();
}
if (beforeState instanceof StateOfLink) {
StateOfLink sl = (StateOfLink) beforeState;
if (!sl.isFree()) {
WireHashKey createWireByStateOfLink = createWireByStateOfLink(sl);
set2.add(createWireByStateOfLink);
if (storageWires.get(createWireByStateOfLink) == null) {
throw new StoryStorageException("miss wire!");
}
}
}
}
if (afterState != null) {
if (!b && !(afterState instanceof StateOfLink)) {
throw new StoryStorageException();
}
if (afterState instanceof StateOfLink) {
StateOfLink sl = (StateOfLink) afterState;
if (!sl.isFree()) {
WireHashKey createWireByStateOfLink = createWireByStateOfLink(sl);
set2.add(createWireByStateOfLink);
if (storageWires.get(createWireByStateOfLink) == null) {
throw new StoryStorageException("miss wire!");
}
}
}
}
}
set2.addAll(set);
// TODO test
for (WireHashKey wk : set2) {
if (storageWires.get(wk) == null) {
throw new StoryStorageException("miss wire!");
}
}
return set2;
}
private static WireHashKey createWireByStateOfLink(StateOfLink sl) {
if (sl.getAgentId() != -1) {
return new WireHashKey(sl.getAgentId(), sl.getSiteName(),
TypeOfWire.LINK_STATE);
}
return null;
}
private void setMapWire(SwapRecord sw) throws StoryStorageException {
if (sw.mapWire.isEmpty()) {
for (int i = 0; i < sw.getAgents1().size(); i++) {
Map<WireHashKey, WireHashKey> newmap = new LinkedHashMap<WireHashKey, WireHashKey>();
Utils.buildCorrespondence(newmap, wiresByIdAgent.get(sw
.getAgents1().get(i)), wiresByIdAgent.get(sw
.getAgents2().get(i)));
sw.mapWire.add(newmap);
}
}
}
private void setOtherSide(SwapRecord sw) {
if (sw.getOtherSide() == null) {
if (sw.isSwapTop()) {
sw.setOtherSide(allEventsByNumber.get(
allEventsByNumber.higherKey(sw.getFirstEventId()))
.getContainer());
} else {
if (allEventsByNumber.lowerKey(sw.getFirstEventId()) != null) {
sw.setOtherSide(allEventsByNumber.get(
allEventsByNumber.lowerKey(sw.getFirstEventId()))
.getContainer());
} else {
sw.setOtherSide(null);
}
}
}
}
// private final void correctLinkOneSwap(long agentId1, long agentId2,
// Long firstEventId, boolean swapTop) throws StoryStorageException {
//
// for (WireHashKey wk : wiresByIdAgent.get(agentId1)) {
// if (wk.getTypeOfWire() != TypeOfWire.LINK_STATE)
// continue;
//
// StateOfLink oldState = new StateOfLink(agentId2, wk.getSiteName());
// StateOfLink newState = new StateOfLink(agentId1, wk.getSiteName());
// if (swapTop) {
// ReLinker.top(firstEventId, oldState,
// newState,wk,storage.getStorageWires());
// } else {
// ReLinker.bottom(firstEventId, oldState, newState,
// wk,storage.getStorageWires());
// }
// }
// }
// =================================================================
// getters
public final int eventCount() {
return allEventsByNumber.size();
}
public final Iterator<Long> agentIterator(String type) {
return iDsByType.get(type).iterator();
}
public final Iterator<String> agentTypeIterator() {
return iDsByType.keySet().iterator();
}
public final EventIteratorInterface eventIterator(boolean reverse)
throws StoryStorageException {
if (reverse) {
return new EventIteratorOnWire(allEventsByNumber, allEventsByNumber
.lastKey(), reverse);
} else {
return new EventIteratorOnWire(allEventsByNumber, allEventsByNumber
.firstKey(), reverse);
}
}
public final ArrayList<WireHashKey> getAgentWires(long agentId) {
return wiresByIdAgent.get(agentId);
}
public final WireStorageInterface getStorage() {
return storage;
}
public final TreeMap<Long, AtomicEvent<?>> getAllEventsByNumber() {
return allEventsByNumber;
}
public final Map<Long, ArrayList<WireHashKey>> getWiresByIdAgent() {
return wiresByIdAgent;
}
public final StoriesGraphs extractGraph() {
return new StoriesGraphs(this);
}
}