/* 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.Collection;
import java.util.List;
import java.util.Map;
import org.flowerplatform.common.util.Pair;
import org.flowerplatform.communication.CommunicationPlugin;
import org.flowerplatform.communication.channel.CommunicationChannel;
import org.flowerplatform.communication.channel.ICommunicationChannelLifecycleListener;
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.GenericTreeStatefulService;
import org.flowerplatform.communication.tree.remote.PathFragment;
import org.flowerplatform.communication.tree.remote.TreeNode;
import com.crispico.flower.mp.codesync.base.Diff;
import com.crispico.flower.mp.codesync.base.IModelAdapter;
import com.crispico.flower.mp.codesync.base.IModelAdapterUI;
import com.crispico.flower.mp.codesync.base.Match;
import com.crispico.flower.mp.codesync.base.MatchModelAdapterUI;
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.DiffActionRegistry;
import com.crispico.flower.mp.codesync.base.action.MatchActionRemoveAbstract;
import com.crispico.flower.mp.codesync.base.action.MatchActionRemoveLeftRight;
/**
* @author Mariana
*/
public class DiffTreeStatefulService extends GenericTreeStatefulService implements ICommunicationChannelLifecycleListener {
public static final String SERVICE_ID = "DiffTreeStatefulService";
public static final int TREE_TYPE_DIFF = 0;
public static final int TREE_TYPE_ANCESTOR = 1;
public static final int TREE_TYPE_LEFT = 2;
public static final int TREE_TYPE_RIGHT = 3;
public static final String TREE_TYPE = "treeType";
public static final String PROJECT_PATH = "projectPath";
private MatchModelAdapterUI matchModelAdapterUI = new MatchModelAdapterUI();
@Override
public String getStatefulClientPrefixId() {
return "Diff Tree";
}
@Override
public String getLabelForLog(Object node, String nodeType) {
return node.toString();
}
public void openNode(StatefulServiceInvocationContext context, DiffTreeNode node, Map<Object, Object> treeContext) {
openNode(context, getPathForNode(node), treeContext);
}
public List<PathFragment> getPathForNode(DiffTreeNode node) {
List<PathFragment> path = new ArrayList<PathFragment>();
TreeNode crtNode = node;
while (crtNode != null) {
path.add(0, crtNode.getPathFragment());
crtNode = crtNode.getParent();
}
return path;
}
@Override
public Collection<Pair<Object, String>> getChildrenForNode(Object node, TreeNode treeNode, GenericTreeContext context) {
IModelAdapterUI adapter = getModelAdapterUI(node, getTreeTypeFromContext(context), getProjectPathFromContext(context));
Collection<Pair<Object, String>> result = new ArrayList<Pair<Object, String>>();
for (Object child : adapter.getChildren(node)) {
result.add(new Pair<Object, String>(child, String.valueOf(getTreeTypeFromContext(context))));
}
return result;
}
@Override
public Boolean nodeHasChildren(Object node, TreeNode treeNode, GenericTreeContext context) {
IModelAdapterUI adapter = getModelAdapterUI(node, getTreeTypeFromContext(context), getProjectPathFromContext(context));
return adapter.hasChildren(node);
}
// public Object getParent(Object node, GenericTreeContext context) {
// if (node instanceof Match) {
// return ((Match) node).getParentMatch();
// }
// return null;
// }
@Override
public boolean populateTreeNode(Object source, TreeNode destination, GenericTreeContext context) {
IModelAdapterUI adapter = getModelAdapterUI(source, getTreeTypeFromContext(context), getProjectPathFromContext(context));
destination.setLabel(adapter.getLabel(source));
// destination.setIcon((adapter.getIconUrls(source)));
matchModelAdapterUI.processDiffTreeNode(source, (DiffTreeNode) destination);
return true;
}
@Override
public Object getNodeByPath(List<PathFragment> fullPath, GenericTreeContext context) {
Object node = null;
for (PathFragment fragment : fullPath) {
node = getNodeByPathFragment(node, fragment, context);
}
return node;
}
public Object getNodeByPathFragment(Object parent, PathFragment pathFragment, GenericTreeContext context) {
Object node = null;
if (parent == null) {
Match match = getMatch(getProjectPathFromContext(context));
switch (getTreeTypeFromContext(context)) {
case TREE_TYPE_DIFF:
node = match;
break;
case TREE_TYPE_ANCESTOR:
node = match.getAncestor();
break;
case TREE_TYPE_LEFT:
node = match.getLeft();
break;
case TREE_TYPE_RIGHT:
node = match.getRight();
break;
default:
return null;
}
} else {
IModelAdapterUI adapter = getModelAdapterUI(parent, getTreeTypeFromContext(context), getProjectPathFromContext(context));
for (Object child : adapter.getChildren(parent)) {
if (getPathFragmentForNode(child, null, context).equals(pathFragment)) {
node = child;
break;
}
}
}
return node;
}
@Override
public PathFragment getPathFragmentForNode(Object node, String nodeType, GenericTreeContext context) {
String name = getModelAdapterUI(node, getTreeTypeFromContext(context), getProjectPathFromContext(context)).getLabel(node);
return new PathFragment(name, String.valueOf(getTreeTypeFromContext(context)));
}
private IModelAdapterUI getModelAdapterUI(Object modelElement, int treeType, String path) {
if (modelElement instanceof Match)
return matchModelAdapterUI;
else {
switch (treeType) {
case TREE_TYPE_DIFF:
return matchModelAdapterUI;
case TREE_TYPE_ANCESTOR:
return getModelAdapterFactorySet(path).getAncestorFactory().getModelAdapter(modelElement);
case TREE_TYPE_LEFT:
return getModelAdapterFactorySet(path).getLeftFactory().getModelAdapter(modelElement);
case TREE_TYPE_RIGHT:
return getModelAdapterFactorySet(path).getRightFactory().getModelAdapter(modelElement);
default:
return null;
}
}
}
@Override
public String getInplaceEditorText(StatefulServiceInvocationContext context, List<PathFragment> fullPath) {
return null;
}
@Override
public boolean setInplaceEditorText(StatefulServiceInvocationContext context, List<PathFragment> path, String text) {
return false;
}
@Override
protected TreeNode createTreeNode() {
return new DiffTreeNode();
}
private int getTreeTypeFromContext(GenericTreeContext context) {
if (context == null)
return TREE_TYPE_DIFF;
return (Integer) context.getClientContext().get(TREE_TYPE);
}
private String getProjectPathFromContext(GenericTreeContext context) {
return (String) context.getClientContext().get(PROJECT_PATH);
}
@RemoteInvocation
public void executeDiffAction(StatefulServiceInvocationContext context, int actionType, int diffIndex, TreeNode node, Map<Object, Object> treeContext) {
List<PathFragment> fullPath = getFullPath(node);
GenericTreeContext genericTreeContext = getTreeContext(context.getCommunicationChannel(), context.getStatefulClientId());
Match match = (Match) getNodeByPath(fullPath, genericTreeContext);
Match parentMatch = match.getParentMatch();
if (actionType == -1) {
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, true, false, feature, results[i], genericTreeContext);
actionPerformed(match, false, false, feature, results[i], genericTreeContext);
}
return;
}
DiffAction diffAction = DiffActionRegistry.ActionType.values()[actionType].diffAction;
ActionResult result = diffAction.execute(match, diffIndex);
if (diffIndex >= 0) {
Diff diff = match.getDiffs().get(diffIndex);
actionPerformed(match, true, false, diff.getFeature(), result, genericTreeContext);
actionPerformed(match, false, false, diff.getFeature(), result, genericTreeContext);
} else {
actionPerformed(parentMatch, true, false, match.getFeature(), result, genericTreeContext);
actionPerformed(parentMatch, false, false, match.getFeature(), result, genericTreeContext);
}
if (diffIndex >= 0) // refresh diff flags only if a "diff" action
match.refreshDiffFlags(result.conflict, result.modifiedLeft, result.modifiedRight);
List<Match> modifiedMatches = new ArrayList<Match>();
if (parentMatch != null) // match.getParentMatch() might be null (i.e. the match was removed)
modifiedMatches.addAll(match.propagateConflictAndModifiedTrueOrFalse(parentMatch, result.conflict, result.modifiedLeft, result.modifiedRight));
if (match.getParentMatch() != null) { // if the match was not deleted ...
// even if the match might be unmodified, it's actions
// need to be recalculated
modifiedMatches.add(match);
}
if (diffAction instanceof MatchActionRemoveAbstract || diffAction instanceof MatchActionRemoveLeftRight) {
Match thoroughUpdateMatch = match.getParentMatch() == null ? parentMatch : match;
// for a delete action, remove it from the "shallow" update list, and do a "thorough" update here (i.e. including children)
modifiedMatches.remove(thoroughUpdateMatch);
// dispatchDiffTreeNodeUpdate(context, thoroughUpdateMatch, treeContext);
}
for (Match modifiedMatch : modifiedMatches) {
// dispatchDiffTreeNodeUpdate(context, modifiedMatch, treeContext);
}
}
private void actionPerformed(Match match, boolean isLeft, boolean fromParent, Object feature, ActionResult result, GenericTreeContext context) {
Match m = fromParent ? match.getParentMatch() : match;
Object lateral = isLeft ? m.getLeft() : m.getRight();
if (lateral != null) {
IModelAdapter adapter = (IModelAdapter) getModelAdapterUI(lateral, isLeft ? TREE_TYPE_LEFT : TREE_TYPE_RIGHT, getProjectPathFromContext(context));
adapter.actionPerformed(lateral, feature, result);
}
}
private void dispatchDiffTreeNodeUpdate(StatefulServiceInvocationContext context, Object source, Map<Object, Object> treeContext) {
List<PathFragment> path = getPathForNode(source, null, getTreeContext(context.getCommunicationChannel(), context.getStatefulClientId()));
TreeNode node = openNodeInternal(context.getCommunicationChannel(), context.getStatefulClientId(), path.size() > 0 ? path : null, treeContext);
updateNode(context.getCommunicationChannel(), context.getStatefulClientId(), path, node, false, false, false, true);
}
@Override
protected void updateNode(CommunicationChannel channel, String statefulClientId, List<PathFragment> path, TreeNode treeNode, boolean expandNode, boolean colapseNode, boolean selectNode, boolean isContentUpdate) {
invokeClientMethod(
channel,
statefulClientId,
"updateNode",
new Object[] {Integer.parseInt(treeNode.getPathFragment().getType()), path, treeNode, expandNode, colapseNode, selectNode});
}
private List<PathFragment> getFullPath(TreeNode node) {
List<PathFragment> path = new ArrayList<PathFragment>();
while (node != null && node.getPathFragment() != null) {
path.add(0, node.getPathFragment());
node = node.getParent();
}
return path;
}
private Match getMatch(String path) {
CodeSyncEditorStatefulService service = (CodeSyncEditorStatefulService) CommunicationPlugin.getInstance().getServiceRegistry().getService(CodeSyncEditorStatefulService.SERVICE_ID);
return service.getMatchForPath(path);
}
private ModelAdapterFactorySet getModelAdapterFactorySet(String path) {
CodeSyncEditorStatefulService service = (CodeSyncEditorStatefulService) CommunicationPlugin.getInstance().getServiceRegistry().getService(CodeSyncEditorStatefulService.SERVICE_ID);
return service.getModelAdapterFactorySetForPath(path);
}
@Override
public void communicationChannelCreated(CommunicationChannel webCommunicationChannel) {
// nothing to do
}
}