Newer
Older
DesignCraft / src / main / java / models / dataSynchronizationModel / DataSynchronizationDesign.java
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;
	}
}