/* license-start * * Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details, at <http://www.gnu.org/licenses/>. * * Contributors: * Crispico - Initial API and implementation * * license-end */ package com.crispico.flower.mp.codesync.base.communication; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.flowerplatform.common.util.RunnableWithParam; import org.flowerplatform.communication.CommunicationPlugin; import org.flowerplatform.communication.stateful_service.RemoteInvocation; import org.flowerplatform.communication.stateful_service.StatefulServiceInvocationContext; import org.flowerplatform.communication.tree.GenericTreeContext; import org.flowerplatform.communication.tree.remote.PathFragment; import org.flowerplatform.editor.remote.EditableResource; import org.flowerplatform.editor.remote.EditableResourceClient; import org.flowerplatform.editor.remote.EditorStatefulService; import com.crispico.flower.mp.codesync.base.CodeSyncEditableResource; import com.crispico.flower.mp.codesync.base.Match; import com.crispico.flower.mp.codesync.base.ModelAdapterFactory; import com.crispico.flower.mp.codesync.base.ModelAdapterFactorySet; import com.crispico.flower.mp.codesync.base.action.ActionResult; import com.crispico.flower.mp.codesync.base.action.ActionSynchronize; import com.crispico.flower.mp.codesync.base.action.DiffAction; import com.crispico.flower.mp.codesync.base.action.MatchActionAddLeftToRight; import com.crispico.flower.mp.codesync.base.action.MatchActionAddRightToLeft; import com.crispico.flower.mp.codesync.base.action.MatchActionRemoveLeft; import com.crispico.flower.mp.codesync.base.action.MatchActionRemoveRight; /** * @author Mariana */ public class CodeSyncEditorStatefulService extends EditorStatefulService { public static final String SERVICE_ID = "codeSyncEditorStatefulService"; public static final String CODE_SYNC_EDITOR = "codeSync"; private DiffTreeStatefulService diffTree = new DiffTreeStatefulService(); public CodeSyncEditorStatefulService() { setEditorName("codeSync"); CommunicationPlugin.getInstance().getCommunicationChannelManager().addWebCommunicationLifecycleListener(this); } private String getProjectPath(String editableResourcePath) { return editableResourcePath; } @Override protected boolean areLocalUpdatesAppliedImmediately() { return true; } @Override protected EditableResource createEditableResourceInstance() { return new CodeSyncEditableResource(); } @Override protected void loadEditableResource(StatefulServiceInvocationContext context, EditableResource editableResource) { } @Override protected void disposeEditableResource(EditableResource editableResource) { } @Override protected void doSave(EditableResource editableResource) { } @Override protected void sendFullContentToClient(EditableResource editableResource, EditableResourceClient client) { } @Override protected void updateEditableResourceContentAndDispatchUpdates(StatefulServiceInvocationContext context, EditableResource editableResource, Object updatesToApply) { Runnable runnable = (Runnable) updatesToApply; runnable.run(); List<Object> result = null; if (updatesToApply instanceof DiffTreeNodeRunnable) { DiffTreeNode node = ((DiffTreeNodeRunnable) updatesToApply).nodeToSendToClient; Object source = null; do { List<PathFragment> path = diffTree.getPathForNode(node); GenericTreeContext genericTreeContext = diffTree.getTreeContext(context.getCommunicationChannel(), context.getStatefulClientId()); source = diffTree.getNodeByPath(path, genericTreeContext); if (source != null) { node = (DiffTreeNode) diffTree.openNodeInternal(context.getCommunicationChannel(), context.getStatefulClientId(), path, genericTreeContext.getClientContext()); result = new ArrayList<Object>(); result.add(path); result.add(node); } else { node = (DiffTreeNode) node.getParent(); } } while (source == null && node != null); } for (EditableResourceClient client : editableResource.getClients()) { sendContentUpdateToClient(editableResource, client, result, true); } } // @Override // protected File getEditableResourceFile(EditableResource editableResource) { // return ResourcesPlugin.getWorkspace().getRoot().getProject(editableResource.getEditableResourcePath()).getLocation().toFile(); // } public Match getMatchForPath(String path) { CodeSyncEditableResource er = (CodeSyncEditableResource) getEditableResource(getProjectPath(path)); if (er != null) { return er.getMatch(); } return null; } public ModelAdapterFactorySet getModelAdapterFactorySetForPath(String path) { CodeSyncEditableResource er = (CodeSyncEditableResource) getEditableResource(getProjectPath(path)); if (er != null) { return er.getModelAdapterFactorySet(); } return null; } private void synchronize(Match match, ModelAdapterFactorySet set) { DiffAction action = null; if (Match.MatchType._1MATCH_LEFT.equals(match.getMatchType())) { action = new MatchActionAddLeftToRight(false); } if (Match.MatchType._1MATCH_RIGHT.equals(match.getMatchType())) { action = new MatchActionAddRightToLeft(false); } if (Match.MatchType._2MATCH_ANCESTOR_LEFT.equals(match.getMatchType())) { action = new MatchActionRemoveLeft(); } if (Match.MatchType._2MATCH_ANCESTOR_RIGHT.equals(match.getMatchType())) { action = new MatchActionRemoveRight(); } if (action != null) { ActionResult result = action.execute(match, -1); actionPerformed(match, set.getLeftFactory(), true, true, match.getFeature(), result); actionPerformed(match, set.getRightFactory(), false, true, match.getFeature(), result); } for (Match subMatch : match.getSubMatches()) { synchronize(subMatch, set); } ActionSynchronize syncAction = new ActionSynchronize(); ActionResult[] results = syncAction.execute(match); for (int i = 0; i < results.length; i++) { Object feature = match.getDiffs().get(i).getFeature(); // notify that an action was performed actionPerformed(match, set.getLeftFactory(), true, false, feature, results[i]); actionPerformed(match, set.getRightFactory(), false, false, feature, results[i]); } } private void actionPerformed(Match match, ModelAdapterFactory factory, boolean isLeft, boolean fromParent, Object feature, ActionResult result) { Match m = fromParent ? match.getParentMatch() : match; Object lateral = isLeft ? m.getLeft() : m.getRight(); if (lateral != null) { factory.getModelAdapter(lateral).actionPerformed(lateral, feature, result); } } private void allActionsPerformed(Match match, ModelAdapterFactorySet set) { for (Match subMatch : match.getSubMatches()) { allActionsPerformed(subMatch, set); } // after all the available actions were performed on this element, notify the adapters allActionsPerformed(match, set.getLeftFactory(), true); allActionsPerformed(match, set.getRightFactory(), false); } private void allActionsPerformed(Match match, ModelAdapterFactory factory, boolean isLeft) { Object lateral = isLeft ? match.getLeft() : match.getRight(); Object opposite = isLeft ? match.getRight() : match.getLeft(); if (lateral != null) { factory.getModelAdapter(lateral).allActionsPerformed(lateral, opposite); } } private void saveModifications(CodeSyncEditableResource er) { saveModifications(er.getMatch(), er.getModelAdapterFactorySet().getLeftFactory(), true); saveModifications(er.getMatch(), er.getModelAdapterFactorySet().getRightFactory(), false); } private void saveModifications(Match match, ModelAdapterFactory factory, boolean isLeft) { Object lateral = isLeft ? match.getLeft() : match.getRight(); if (lateral != null) { if (factory.getModelAdapter(lateral).save(lateral)) { for (Match subMatch : match.getSubMatches()) { saveModifications(subMatch, factory, isLeft); } } } } private void discardModifications(CodeSyncEditableResource er) { discardModifications(er.getMatch(), er.getModelAdapterFactorySet().getLeftFactory(), true); discardModifications(er.getMatch(), er.getModelAdapterFactorySet().getRightFactory(), false); } private void discardModifications(Match match, ModelAdapterFactory factory, boolean isLeft) { Object lateral = isLeft ? match.getLeft() : match.getRight(); if (lateral != null) { if (factory.getModelAdapter(lateral).discard(lateral)) { for (Match subMatch : match.getSubMatches()) { discardModifications(subMatch, factory, isLeft); } } } } /////////////////////////////////////////////////////////////// // @RemoteInvocation methods /////////////////////////////////////////////////////////////// @RemoteInvocation public void openNode(StatefulServiceInvocationContext context, DiffTreeNode node, Map<Object, Object> treeContext) { diffTree.openNode(context, node, treeContext); } @RemoteInvocation public void executeDiffAction(final StatefulServiceInvocationContext context, String editableResourcePath, final int actionType, final int diffIndex, final DiffTreeNode node, final Map<Object, Object> treeContext) { attemptUpdateEditableResourceContent(context, getProjectPath(editableResourcePath), new DiffTreeNodeRunnable() { @Override public void run() { this.nodeToSendToClient = node; diffTree.executeDiffAction(context, actionType, diffIndex, node, treeContext); } }); } @RemoteInvocation public void synchronize(StatefulServiceInvocationContext context, String editableResourcePath) { final CodeSyncEditableResource er = (CodeSyncEditableResource) getEditableResource(getProjectPath(editableResourcePath)); if (er != null) { attemptUpdateEditableResourceContent(context, editableResourcePath, new Runnable() { @Override public void run() { synchronize(er.getMatch(), er.getModelAdapterFactorySet()); } }); } } @RemoteInvocation public void applySelectedActions(StatefulServiceInvocationContext context, final String editableResourcePath, final boolean notifyClient) { final CodeSyncEditableResource er = (CodeSyncEditableResource) getEditableResource(getProjectPath(editableResourcePath)); if (er != null) { attemptUpdateEditableResourceContent(context, editableResourcePath, new Runnable() { @Override public void run() { allActionsPerformed(er.getMatch(), er.getModelAdapterFactorySet()); saveModifications(er); unsubscribeAllClientsForcefully(getProjectPath(editableResourcePath), notifyClient); } }); } } @RemoteInvocation public void cancelSelectedActions(String editableResourcePath, final boolean notifyClient) { CodeSyncEditableResource er = (CodeSyncEditableResource) getEditableResource(getProjectPath(editableResourcePath)); if (er != null) { discardModifications(er); unsubscribeAllClientsForcefully(getProjectPath(editableResourcePath), notifyClient); } } }