package org.eclipse.uml2.diagram.common.async;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint;
import org.eclipse.gmf.runtime.diagram.core.services.ViewService;
import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.uml2.diagram.common.genapi.IDiagramUpdater;
import org.eclipse.uml2.diagram.common.genapi.IUpdaterNodeDescriptor;
import org.eclipse.uml2.diagram.common.genapi.IVisualIDRegistry;
public class SyncModelNode implements IAdaptable {
private final SyncModelContext myContext;
private View mySyncModelView;
private List<SyncModelNode> myChildren;
private SyncModelNode myParent;
private View mySyncModelCompartment;
private View myDiagramView;
private boolean myIsAutoSynchronized;
private boolean myIsChecked;
public SyncModelNode(View syncModelRoot, View diagramRoot, SyncModelContext context) {
this(syncModelRoot, (SyncModelNode) null, context);
if (syncModelRoot == null) {
throw new NullPointerException();
}
if (diagramRoot != null) {
if (diagramRoot.getElement() == null) {
throw new IllegalArgumentException("Diagram root should have an semantic element"); //$NON-NLS-1$
}
if (!diagramRoot.getElement().equals(syncModelRoot.getElement())) {
throw new IllegalArgumentException("Diagram root element : " + diagramRoot.getElement() + ", while SyncModel root element: " + syncModelRoot.getElement()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
initWithDiagramView(diagramRoot);
}
protected SyncModelNode(View syncModelView, SyncModelNode parent) {
this(syncModelView, parent, parent.getContext());
}
private SyncModelNode(View syncModelView, SyncModelNode parent, SyncModelContext context) {
mySyncModelView = syncModelView;
myParent = parent;
if (myParent != null) {
myParent.addChild(this);
}
myContext = context;
}
public boolean isRoot() {
return getParent() == null;
}
public void setChecked(boolean isChecked) {
myIsChecked = isChecked;
}
public boolean isChecked() {
return myIsChecked;
}
public View getSyncModelView() {
return mySyncModelView;
}
public View getDiagramView() {
return myDiagramView;
}
public boolean isKnownLeaf() {
int visualID = getContext().getRegistry().getVisualID(mySyncModelView);
return getContext().getRegistry().isSemanticLeafVisualID(visualID);
}
public boolean isInCompartment() {
return mySyncModelCompartment != null;
}
public View getSyncModelCompartment() {
return mySyncModelCompartment;
}
public boolean isAutoSynchronized() {
return myIsAutoSynchronized;
}
public void setAutoSynchronized(boolean isAutoSynchronized) {
myIsAutoSynchronized = isAutoSynchronized;
}
public List<SyncModelNode> getChildren() {
if (myChildren == null) {
myChildren = new LinkedList<SyncModelNode>();
createSyncModelChildren();
if (myChildren.isEmpty()) {
myChildren = Collections.emptyList();
}
}
return myChildren;
}
public SyncModelNode getParent() {
return myParent;
}
private void setCompartment(View compartment) {
mySyncModelCompartment = compartment;
}
public SyncModelContext getContext() {
return myContext;
}
private IDiagramUpdater getUpdater() {
return myContext.getUpdater();
}
private PreferencesHint getPreferencesHint() {
return myContext.getPreferencesHint();
}
private void addChild(SyncModelNode child) {
myChildren.add(child);
}
private void createSyncModelChildren() {
myContext.runCommand(new Runnable() {
public void run() {
doCreateSyncModelChildren();
}
});
}
private void doCreateSyncModelChildren() {
List<View> directChildren = createChildViews(mySyncModelView, myDiagramView);
for (View nextDirect : directChildren) {
SyncModelNode child = doCreateNodeView(nextDirect, this);
View diagramCounterpart = findCounterpart(nextDirect, myDiagramView);
child.initWithDiagramView(diagramCounterpart);
}
for (Object next : mySyncModelView.getChildren()) {
View nextSyncCompartment = (View) next;
if (isCompartment(nextSyncCompartment)) {
View diagramCompartment = findCounterpart(nextSyncCompartment, myDiagramView);
List<View> syncCompartmentChildren = createChildViews(nextSyncCompartment, diagramCompartment);
for (View nextCompartmentChild : syncCompartmentChildren) {
SyncModelNode nextResult = doCreateNodeView(nextCompartmentChild, this);
nextResult.setCompartment(nextSyncCompartment);
View diagramCounterpart = findCounterpart(nextCompartmentChild, diagramCompartment);
nextResult.initWithDiagramView(diagramCounterpart);
}
}
}
}
protected SyncModelNode doCreateNodeView(View syncModelView, SyncModelNode parent) {
return new SyncModelNode(syncModelView, parent);
}
private boolean isCompartment(View view) {
IVisualIDRegistry registry = getContext().getRegistry();
int visualId = registry.getVisualID(view);
return registry.isCompartmentVisualID(visualId);
}
private List<View> createChildViews(View syncModelParent, View diagramViewParent) {
List<? extends IUpdaterNodeDescriptor> descriptors = getUpdater().getSemanticChildren(syncModelParent);
List<View> result = new LinkedList<View>();
for (Object next : descriptors) {
IUpdaterNodeDescriptor nextDescriptor = (IUpdaterNodeDescriptor) next;
EObject nextSemanticChild = nextDescriptor.getModelElement();
View nextView = null;
if (diagramViewParent != null) {
List<?> children = diagramViewParent.getChildren();
boolean found = false;
for (Iterator<?> iter = children.iterator(); !found && iter.hasNext();) {
View nextDiagramChild = (View) iter.next();
if (nextDiagramChild.getElement() != null && //
nextDiagramChild.getElement().equals(nextSemanticChild)) {
nextView = ViewService.createNode(syncModelParent,//
nextSemanticChild, nextDiagramChild.getType(), getPreferencesHint());
found = true;
}
}
}
if (nextView == null) {
nextView = ViewService.getInstance().createView(Node.class, //
new EObjectAdapter(nextSemanticChild), syncModelParent, null, //
ViewUtil.APPEND, true, getPreferencesHint());
}
if (nextView != null) {
result.add(nextView);
}
}
return result;
}
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if (adapter.equals(View.class)) {
return mySyncModelView;
}
if (adapter.isInstance(this)) {
return this;
}
return null;
}
public static View findCounterpart(View syncChild, View diagramParent) {
if (diagramParent == null) {
return null;
}
for (Object nextChild : diagramParent.getChildren()) {
View nextDiagramChild = (View) nextChild;
if (isCounterparts(syncChild, nextDiagramChild)) {
return nextDiagramChild;
}
}
return null;
}
public static boolean isCounterparts(View syncView, View diagramView) {
if (syncView.getType() == null || diagramView.getType() == null) {
return false;
}
if (!syncView.getType().equals(diagramView.getType())) {
return false;
}
if (syncView.getElement() == null) {
//compartment
return diagramView.getElement() == null;
}
return syncView.getElement().equals(diagramView.getElement());
}
public void applyCanonicalStyle() {
checkHasDiagramView();
applyCanonicalStyle(getDiagramView());
}
private void applyCanonicalStyle(View view) {
if (isKnownLeaf()) {
return;
}
doApplyCanonicalStyle(view);
for (Object nextChild : view.getChildren()) {
View nextChildView = (View) nextChild;
if (isCompartment(nextChildView)) {
doApplyCanonicalStyle(nextChildView);
}
}
}
private void doApplyCanonicalStyle(View target) {
ICanonicalHelper.IMPLEMENTATION.setAutoSynchronized(target, isAutoSynchronized());
}
protected void initWithDiagramView(View diagramView) {
myDiagramView = diagramView;
if (myDiagramView != null) {
setChecked(true);
myIsAutoSynchronized = ICanonicalHelper.IMPLEMENTATION.isAutoSynchronized(myDiagramView);
} else {
myIsAutoSynchronized = getContext().isDiagramInitialization();
setChecked(getContext().isDiagramInitialization());
}
if (isKnownLeaf()) {
myIsAutoSynchronized = false;
}
}
private void checkHasDiagramView() {
if (getDiagramView() == null) {
throw new IllegalStateException("I am not associated with diagram view:" + getSyncModelView()); //$NON-NLS-1$
}
}
/*package*/void associateWithDiagramView(View diagramView) {
myDiagramView = diagramView;
}
/*package*/void setChosenSyncModelViewType(String type) {
if (type == null || mySyncModelView.getType().equals(type)) {
return;
}
myParent.setChildTypeHint(this, type);
}
private void forseChildrenRecreation() {
myChildren = null;
getChildren();
}
private void correctChildrenStatus(NodeInfo nodeInfo) {
List<SyncModelNode> newChildren = getChildren();
for (SyncModelNode newChild : newChildren) {
boolean found = false;
for (NodeInfo oldChild : nodeInfo.getChildren()) {
if (newChild.getSyncModelView().getElement().equals(oldChild.getSemanticElement())) {
newChild.setAutoSynchronized(oldChild.isAutoSynchronized());
newChild.setChecked(oldChild.isChecked());
newChild.correctChildrenStatus(oldChild);
found = true;
}
}
if (!found) {
newChild.setAutoSynchronized(false);
newChild.setChecked(false);
}
}
}
private void setChildTypeHint(final SyncModelNode childNode, final String type) {
final NodeInfo nodeInfo = collectNodeInfo(childNode);
final EObject semanticElement = childNode.getSyncModelView().getElement();
ViewUtil.destroy(childNode.getSyncModelView());
myContext.runCommand(new Runnable() {
public void run() {
try {
childNode.mySyncModelView = ViewService.createNode(mySyncModelView, semanticElement, type, getPreferencesHint());
childNode.forseChildrenRecreation();
childNode.correctChildrenStatus(nodeInfo);
} catch (Throwable e) {
e.printStackTrace();
}
}
});
}
private NodeInfo collectNodeInfo(SyncModelNode node) {
NodeInfo result = new NodeInfo(node.mySyncModelView.getElement(), node.isChecked(), node.isAutoSynchronized(), new LinkedList<NodeInfo>());
for (SyncModelNode child : node.getChildren()) {
NodeInfo childNodeInfo = collectNodeInfo(child);
result.getChildren().add(childNodeInfo);
}
return result;
}
private static class NodeInfo {
private final EObject mySemanticElement;
private final boolean myIsChecked;
private final boolean myIsAutoSynchronized;
private final List<NodeInfo> myChildren;
public NodeInfo(EObject mySemanticElement, boolean isChecked, boolean isAutoSynchronized, List<NodeInfo> myChildren) {
super();
this.mySemanticElement = mySemanticElement;
this.myIsChecked = isChecked;
this.myIsAutoSynchronized = isAutoSynchronized;
this.myChildren = myChildren;
}
public EObject getSemanticElement() {
return mySemanticElement;
}
public boolean isChecked() {
return myIsChecked;
}
public boolean isAutoSynchronized() {
return myIsAutoSynchronized;
}
public List<NodeInfo> getChildren() {
return myChildren;
}
}
}