package models.dataSynchronizationModel;
import designPatternExtensions.DependencyInversion;
import designPatternExtensions.MediatorInsertion;
import designPatternExtensions.PresenterInsertion;
import models.deltaAlgebra.*;
import parser.*;
import parser.exceptions.ExpectedJsonValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* The final data synchronization design containing delta algebra and dependency injection.
*
* @author Nitta
*
*/
public class DataSynchronizationDesign {
private List<DataSynchronizationContext> synchronizationContexts;
private DeltaComplex deltaComplex;
private List<DependencyInversion> dependencyInversions = new ArrayList<>();
private List<MediatorInsertion> mediatorInsertions = new ArrayList<>();
private List<PresenterInsertion> presenterInsertions = new ArrayList<>();
public DataSynchronizationDesign(DataSynchronizationContext synchronizationContexts) throws IllegalRelationException {
this.synchronizationContexts = new ArrayList<>();
this.synchronizationContexts.add(synchronizationContexts);
this.deltaComplex = createDefaultDeltaComplex(synchronizationContexts);
}
public DataSynchronizationDesign(JsonValue jsonValue) throws IllegalRelationException {
this.synchronizationContexts = new ArrayList<>();
for (StringValue keyValue : jsonValue.getJson().keySet()) {
String key = keyValue.getStringValue();
if (key.equals("contexts")) {
ArrayValue arrayValue = (ArrayValue) jsonValue.getValue(keyValue);
setSynchronizationContexts(arrayValue);
}
}
}
public void addDependencyInversion(DependencyInversion dependencyInversion) {
dependencyInversions.add(dependencyInversion);
}
public List<DependencyInversion> getDependencyInversions() {
return dependencyInversions;
}
public void addMediatorInsertion(MediatorInsertion mediatorInsertion) {
mediatorInsertions.add(mediatorInsertion);
}
public List<MediatorInsertion> getMediatorInsertions() {
return mediatorInsertions;
}
public void addPresenterInsertion(PresenterInsertion presenterInsertion) {
presenterInsertions.add(presenterInsertion);
}
public List<PresenterInsertion> getPresenterInsertions() {
return presenterInsertions;
}
public DeltaComplex createDefaultDeltaComplex(DataSynchronizationContext transferContext) throws IllegalRelationException {
Stack<Delta> deltaStack = constructDefaultDeltas(transferContext);
// Add all deltas.
DeltaComplex deltaComplex = null;
while (!deltaStack.isEmpty()) {
if (deltaComplex == null) {
deltaComplex = new DeltaComplex(deltaStack.pop());
} else {
deltaComplex.attach(deltaStack.pop());
}
}
return deltaComplex;
}
private Stack<Delta> constructDefaultDeltas(DataSynchronizationContext transferContext) throws IllegalRelationException {
Stack<Delta> deltaStack = new Stack<>();
PushPullValue transferStyle = transferContext.getTransferStyle();
ObjectNode srcObj = null;
ObjectNode dstObj = null;
if (transferStyle == PushPullValue.PUSH) {
srcObj = transferContext.getSrcTopObject();
dstObj = transferContext.getDstTopObject();
} else {
srcObj = transferContext.getDstTopObject();
dstObj = transferContext.getSrcTopObject();
}
String refName = dstObj.getName().substring(0, 1).toLowerCase() + dstObj.getName().substring(1);
ReferenceEdge prevReference = new ReferenceEdge(srcObj, dstObj, refName);
boolean hasDiagonalRelation = false;
if (!transferContext.isFlat()) {
boolean toOne = true; // potential multiplicity
ObjectHierarchy srcHierarchy = null;
ObjectHierarchy dstHierarchy = null;
List<Relation> allRelations = new ArrayList<>(transferContext.getIntermediateRelations());
for (Relation relation: allRelations) {
if (transferContext.getSrcObjectHierarchy().isPrefixOf(relation.getSrcObjectHierarchy())
|| transferContext.getDstObjectHierarchy().isPrefixOf(relation.getDstObjectHierarchy())) {
hasDiagonalRelation = true;
break;
}
}
if (!hasDiagonalRelation) {
allRelations.add(transferContext.getSyncronizationRelation().getPrevRelation());
} else {
Relation lastRelation = allRelations.getLast();
Relation bottomRelation = null;
if (!transferContext.getSrcObjectHierarchy().isPrefixOf(lastRelation.getSrcObjectHierarchy())) {
bottomRelation = new Relation(transferContext.getSrcObjectHierarchy().getPrevObjectHierarchy(),
transferContext.getSrcObjectHierarchy(),
transferContext.getTransferMultiplicity());
allRelations.add(bottomRelation);
} else if (!transferContext.getDstObjectHierarchy().isPrefixOf(lastRelation.getDstObjectHierarchy())) {
bottomRelation = new Relation(transferContext.getSrcObjectHierarchy(),
transferContext.getDstObjectHierarchy().getPrevObjectHierarchy(),
transferContext.getTransferMultiplicity());
allRelations.add(bottomRelation);
}
}
for (Relation relation: allRelations) {
ObjectHierarchy newSrcHierarchy = null;
ObjectHierarchy newDstHierarchy = null;
if (transferStyle == PushPullValue.PUSH) {
newSrcHierarchy = relation.getSrcObjectHierarchy();
newDstHierarchy = relation.getDstObjectHierarchy();
} else {
newSrcHierarchy = relation.getDstObjectHierarchy();
newDstHierarchy = relation.getSrcObjectHierarchy();
}
if ((dstHierarchy == null || dstHierarchy.getDepth() < newDstHierarchy.getDepth()) || (srcHierarchy == null || srcHierarchy.getDepth() < newSrcHierarchy.getDepth())) {
List<ReferenceEdge> oneDestination = new ArrayList<>();
List<ReferenceEdge> manyDestinations = new ArrayList<>();
boolean nextToOne = toOne;
for (int i = 0; i < newDstHierarchy.getDepth(); i++) {
if (dstHierarchy != null && i < dstHierarchy.getDepth()) {
if (!dstHierarchy.getEdge(i).equals(newDstHierarchy.getEdge(i))) {
throw new IllegalRelationException();
}
} else {
if (newDstHierarchy.getEdge(i).toMany()) {
nextToOne = false;
}
if (nextToOne) {
oneDestination.add(newDstHierarchy.getEdge(i));
} else {
manyDestinations.add(newDstHierarchy.getEdge(i));
}
}
}
// 1) Create container passing deltas.
List<PrimitiveDelta> primitiveDeltaList = new ArrayList<>();
for (ReferenceEdge dstEdge: oneDestination) {
srcObj = (ObjectNode) prevReference.getSource();
dstObj = (ObjectNode) dstEdge.getDestination();
String dataName = dstObj.getName();
dataName = dataName.substring(0, 1).toLowerCase() + dataName.substring(1);
if (!prevReference.isIdentical()) {
primitiveDeltaList.add(new PrimitivePullDelta(prevReference, dstEdge, dataName));
}
prevReference = new ReferenceEdge(srcObj, dstObj, dataName);
}
for (int i = 0; i < newSrcHierarchy.getDepth(); i++) {
ReferenceEdge srcEdge = newSrcHierarchy.getEdge(i);
if (srcHierarchy != null && i < srcHierarchy.getDepth()) {
if (!srcHierarchy.getEdge(i).equals(srcEdge)) {
throw new IllegalRelationException();
}
} else {
srcObj = (ObjectNode) srcEdge.getDestination();
dstObj = (ObjectNode) prevReference.getDestination();
String dataName = dstObj.getName();
dataName = dataName.substring(0, 1).toLowerCase() + dataName.substring(1);
if (!prevReference.isIdentical() && !prevReference.getDestination().equals(srcEdge.getDestination())) {
if (toOne) {
primitiveDeltaList.add(new PrimitivePullPushDelta(prevReference, srcEdge, dataName));
} else {
primitiveDeltaList.add(new PrimitivePullPushDelta(prevReference, srcEdge, dataName, true));
}
Delta delta = null;
for (PrimitiveDelta primDelta : primitiveDeltaList) {
if (delta == null) {
delta = new Delta(primDelta);
} else {
delta = new Delta(primDelta, delta);
}
}
deltaStack.push(delta); // add a container passing delta
primitiveDeltaList.clear();
}
prevReference = new ReferenceEdge(srcObj, dstObj, dataName);
}
}
toOne = nextToOne;
if (!toOne) {
boolean multipleDefinition = false; // destination multiplicity of the defined reference
if ((transferStyle == PushPullValue.PUSH && (relation.getMultiplicity() == MultiplicityValue.ManyToOne || relation.getMultiplicity() == MultiplicityValue.OneToOne))
|| (transferStyle != PushPullValue.PUSH && (relation.getMultiplicity() == MultiplicityValue.OneToMany || relation.getMultiplicity() == MultiplicityValue.OneToOne))) {
// If the specified multiplicity of destination objects does not match to their potential multiplicity,
// then the reference to select one destination object from potential multiple objects is needed.
toOne = true;
} else {
// If the specified multiplicity of destination objects and their potential multiplicity are both many,
// then the reference to select one destination object from potential multiple objects is needed.
multipleDefinition = true;
}
// 2) Create a component selecting delta.
PrimitivePullDelta componentSelectingDelta = null;
for (ReferenceEdge dstEdge : manyDestinations) {
if (componentSelectingDelta == null) {
srcObj = (ObjectNode) prevReference.getSource();
dstObj = (ObjectNode) dstEdge.getDestination();
String dataName = dstObj.getName();
dataName = dataName.substring(0, 1).toLowerCase() + dataName.substring(1);
componentSelectingDelta = new PrimitivePullDelta(prevReference, dstEdge, dataName, multipleDefinition);
primitiveDeltaList.add(componentSelectingDelta);
Delta delta = null;
for (PrimitiveDelta primDelta : primitiveDeltaList) {
if (delta == null) {
delta = new Delta(primDelta);
} else {
delta = new Delta(primDelta, delta);
}
}
deltaStack.push(delta); // add a component identifying delta
primitiveDeltaList.clear();
if (!multipleDefinition) {
prevReference = new ReferenceEdge(srcObj, dstObj, dataName);
} else {
prevReference = new ReferenceEdge(srcObj, dstObj, dataName, dstEdge.getKeyTypeName());
}
} else {
srcObj = (ObjectNode) prevReference.getSource();
dstObj = (ObjectNode) dstEdge.getDestination();
String dataName = dstObj.getName();
dataName = dataName.substring(0, 1).toLowerCase() + dataName.substring(1);
componentSelectingDelta = new PrimitivePullDelta(prevReference, dstEdge, dataName, multipleDefinition);
primitiveDeltaList.add(componentSelectingDelta);
if (!multipleDefinition) {
prevReference = new ReferenceEdge(srcObj, dstObj, dataName);
} else {
prevReference = new ReferenceEdge(srcObj, dstObj, dataName, dstEdge.getKeyTypeName());
}
}
}
}
// 3) Create a component obtaining delta.
if (primitiveDeltaList.size() > 0) {
Delta delta = null;
for (PrimitiveDelta primDelta: primitiveDeltaList) {
if (delta == null) {
delta = new Delta(primDelta);
} else {
delta = new Delta(primDelta, delta);
}
}
deltaStack.push(delta); // add a component obtaining delta
}
} else {
throw new IllegalRelationException();
}
srcHierarchy = newSrcHierarchy;
dstHierarchy = newDstHierarchy;
}
}
if (!hasDiagonalRelation) {
// 4) Create the bottom (data transfer) delta.
Delta dataTransferDelta = null;
if (transferStyle == PushPullValue.PUSH) {
// PUSH transfer
if (transferContext.getTransferMultiplicity() == MultiplicityValue.ManyToOne
|| transferContext.getTransferMultiplicity() == MultiplicityValue.OneToOne) {
// Simple PUSH
// Add the data transfer delta.
srcObj = (ObjectNode) prevReference.getSource();
dstObj = (ObjectNode) transferContext.getSrcObjectHierarchy().getBottom();
String dataName = transferContext.getDataName();
ReferenceEdge dataEdge = new ReferenceEdge(srcObj, dstObj, dataName);
PrimitivePullPushDelta primDelta = new PrimitivePullPushDelta(dataEdge, prevReference, dataName); // PUSH data transfer
if (transferContext.getSyncronizationRelation().getMultiplicity() == MultiplicityValue.OneToMany
|| transferContext.getSyncronizationRelation().getMultiplicity() == MultiplicityValue.ManyToMany) {
primDelta.setMultipleTransfer(true);
}
dataTransferDelta = new Delta(primDelta);
} else {
// Broadcasting
prevReference = new ReferenceEdge(prevReference);
prevReference.setKeyTypeName(transferContext.getDstObjectHierarchy().getEdges().getLast().getKeyTypeName());
// prevReference.setMultiplicity(MultiplicityValue.OneToMany);
// Add the data transfer delta.
srcObj = (ObjectNode) prevReference.getSource();
dstObj = (ObjectNode) transferContext.getSrcObjectHierarchy().getBottom();
String dataName = transferContext.getDataName();
ReferenceEdge dataEdge = new ReferenceEdge(srcObj, dstObj, dataName);
PrimitivePullPushDelta primDelta = new PrimitivePullPushDelta(dataEdge, prevReference, dataName); // PUSH data transfer
if (transferContext.getDstObjectHierarchy().getEdges().getLast().getMultiplicity() == MultiplicityValue.OneToMany
|| transferContext.getDstObjectHierarchy().getEdges().getLast().getMultiplicity() == MultiplicityValue.ManyToMany) {
primDelta.setMultipleDefinition(true);
}
if (transferContext.getSyncronizationRelation().getMultiplicity() == MultiplicityValue.OneToMany
|| transferContext.getSyncronizationRelation().getMultiplicity() == MultiplicityValue.ManyToMany) {
primDelta.setMultipleTransfer(true);
}
dataTransferDelta = new Delta(primDelta);
}
} else {
if (transferContext.getTransferMultiplicity() == MultiplicityValue.OneToMany
|| transferContext.getTransferMultiplicity() == MultiplicityValue.OneToOne) {
// Simple PULL
// Add the data transfer delta.
srcObj = (ObjectNode) prevReference.getDestination();
dstObj = (ObjectNode) transferContext.getSrcObjectHierarchy().getBottom();
String dataName = transferContext.getDataName();
ReferenceEdge dataEdge = new ReferenceEdge(srcObj, dstObj, dataName);
PrimitivePullDelta primDelta = new PrimitivePullDelta(prevReference, dataEdge, dataName); // PULL data transfer
if (transferContext.getSyncronizationRelation().getMultiplicity() == MultiplicityValue.ManyToOne
|| transferContext.getSyncronizationRelation().getMultiplicity() == MultiplicityValue.ManyToMany) {
primDelta.setMultipleTransfer(true);
}
dataTransferDelta = new Delta(primDelta);
} else {
// Data collecting
prevReference = new ReferenceEdge(prevReference);
prevReference.setKeyTypeName(transferContext.getSrcObjectHierarchy().getEdges().getLast().getKeyTypeName());
// prevReference.setMultiplicity(MultiplicityValue.OneToMany);
// Add the data transfer delta.
srcObj = (ObjectNode) prevReference.getDestination();
dstObj = (ObjectNode) transferContext.getSrcObjectHierarchy().getBottom();
String dataName = transferContext.getDataName();
ReferenceEdge dataEdge = new ReferenceEdge(srcObj, dstObj, dataName);
PrimitivePullDelta primDelta = new PrimitivePullDelta(prevReference, dataEdge, dataName, true); // PULL data transfer
if (transferContext.getSyncronizationRelation().getMultiplicity() == MultiplicityValue.ManyToOne
|| transferContext.getSyncronizationRelation().getMultiplicity() == MultiplicityValue.ManyToMany) {
primDelta.setMultipleTransfer(true);
}
dataTransferDelta = new Delta(primDelta);
}
}
deltaStack.push(dataTransferDelta);
}
return deltaStack;
}
private void setSynchronizationContexts(ArrayValue arrayValue) throws IllegalRelationException {
List<Value> values = arrayValue.getValues();
for (int i = 0; i<values.size(); i++) {
if (!(values.get(i) instanceof JsonValue)) {
throw new ExpectedJsonValue("contextsの中身がJsonではありません。");
}
if (i == 0) {
DataSynchronizationContext transferContext = new DataSynchronizationContext(values.get(i));
this.synchronizationContexts.add(transferContext);
this.deltaComplex = createDefaultDeltaComplex(transferContext);
} else {
DataSynchronizationContext transferContext = new DataSynchronizationContext(values.get(i));
addTransferContext(transferContext);
}
}
}
public DeltaComplex getDeltaComplex() {
return deltaComplex;
}
public void addTransferContext(DataSynchronizationContext transferContext) throws IllegalRelationException {
synchronizationContexts.add(transferContext);
deltaComplex.merge(createDefaultDeltaComplex(transferContext));
}
public List<DataSynchronizationContext> getSynchronizationContexts() {
return synchronizationContexts;
}
}