/* 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.code;
import java.io.File;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.flowerplatform.codesync.remote.CodeSyncOperationsService;
import org.flowerplatform.common.plugin.AbstractFlowerJavaPlugin;
import org.flowerplatform.communication.CommunicationPlugin;
import org.flowerplatform.communication.channel.CommunicationChannel;
import org.flowerplatform.communication.stateful_service.StatefulServiceInvocationContext;
import org.flowerplatform.editor.EditorPlugin;
import org.flowerplatform.editor.file.IFileAccessController;
import org.flowerplatform.editor.remote.EditorStatefulClientLocalState;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.crispico.flower.mp.codesync.base.CodeSyncAlgorithm;
import com.crispico.flower.mp.codesync.base.CodeSyncEditableResource;
import com.crispico.flower.mp.codesync.base.CodeSyncPlugin;
import com.crispico.flower.mp.codesync.base.Match;
import com.crispico.flower.mp.codesync.base.ModelAdapterFactorySet;
import com.crispico.flower.mp.codesync.base.communication.CodeSyncEditorStatefulService;
import com.crispico.flower.mp.model.astcache.code.Annotation;
import com.crispico.flower.mp.model.astcache.code.AnnotationValue;
import com.crispico.flower.mp.model.astcache.code.Class;
import com.crispico.flower.mp.model.astcache.code.ExtendedModifier;
import com.crispico.flower.mp.model.astcache.code.ModifiableElement;
import com.crispico.flower.mp.model.astcache.code.Operation;
import com.crispico.flower.mp.model.astcache.code.Parameter;
import com.crispico.flower.mp.model.codesync.AstCacheElement;
import com.crispico.flower.mp.model.codesync.CodeSyncElement;
import com.crispico.flower.mp.model.codesync.FeatureChange;
/**
* @author Cristi
*/
public class CodeSyncCodePlugin extends AbstractFlowerJavaPlugin {
public static String MARKER_ANNOTATION = "_MARKER";
public static String SINGLE_MEMBER_ANNOTATION = "_SINGLE_MEMBER";
public static String NORMAL_ANNOTATION = "_NORMAL";
public final static String SINGLE_MEMBER_ANNOTATION_VALUE_NAME = "_";
protected static CodeSyncCodePlugin INSTANCE;
private final static Logger logger = LoggerFactory.getLogger(CodeSyncCodePlugin.class);
public static CodeSyncCodePlugin getInstance() {
return INSTANCE;
}
protected ModelAdapterFactorySetProvider modelAdapterFactorySetProvider;
private Utils utils = new Utils();
/**
* @author Mariana
*/
public ModelAdapterFactorySetProvider getModelAdapterFactorySetProvider() {
return modelAdapterFactorySetProvider;
}
/**
* @author Mariana
*/
public Utils getUtils() {
return utils;
}
/**
* @author Cristi
* @author Mariana
*/
public void start(BundleContext bundleContext) throws Exception {
super.start(bundleContext);
INSTANCE = this;
initializeExtensionPoint_modelAdapterFactorySet();
}
private void initializeExtensionPoint_modelAdapterFactorySet() throws CoreException {
modelAdapterFactorySetProvider = new ModelAdapterFactorySetProvider();
IConfigurationElement[] configurationElements = Platform.getExtensionRegistry().getConfigurationElementsFor("org.flowerplatform.codesync.code.modelAdapterFactorySet");
for (IConfigurationElement configurationElement : configurationElements) {
String id = configurationElement.getAttribute("id");
String technology = configurationElement.getAttribute("technology");
Object instance = configurationElement.createExecutableExtension("modelAdapterFactorySetClass");
modelAdapterFactorySetProvider.getFactorieSets().put(technology, (ModelAdapterFactorySet) instance);
logger.debug("Added CodeSync ModelAdapterFactorySet with id = {} with class = {}", id, instance.getClass());
}
}
public void stop(BundleContext bundleContext) throws Exception {
super.stop(bundleContext);
INSTANCE = null;
}
@Override
public void registerMessageBundle() throws Exception {
// nothing to do yet
}
/**
* @author Cristi
* @author Mariana
* @author Sebastian Solomon
*/
public CodeSyncElement getCodeSyncElement(Object project, Object file, String technology, CommunicationChannel communicationChannel, boolean showDialog) {
IFileAccessController fileAccessController = EditorPlugin.getInstance().getFileAccessController();
// find model files
ResourceSet resourceSet = CodeSyncPlugin.getInstance().getOrCreateResourceSet(project, "diagramEditorStatefulService");
Resource cseResource = CodeSyncPlugin.getInstance().getCodeSyncMapping(project, resourceSet);
// find containing SrcDir
CodeSyncElement srcDir = null;
Object srcDirFile = null;
Object parent = file;
do {
srcDir = CodeSyncPlugin.getInstance().getSrcDir(cseResource, fileAccessController.getName(parent));
srcDirFile = parent;
parent = fileAccessController.getParentFile(parent);
} while (srcDir == null && !parent.equals(project));
if (srcDir == null) {
throw new RuntimeException("File " + file + " is not contained in a SrcDir!");
}
// find the CodeSyncElement in the SrcDir
String relativeToSrcDir = EditorPlugin.getInstance().getFileAccessController().getPathRelativeToFile(file, srcDirFile);
// there are cases when path format is a\b\c and the split method will not return correctly.
// so replace \ with /
relativeToSrcDir = relativeToSrcDir.replaceAll("\\\\", "/");
if (relativeToSrcDir.startsWith("/")) {
relativeToSrcDir = relativeToSrcDir.substring(1);
}
String[] fragments = relativeToSrcDir.length() > 0 ? relativeToSrcDir.split("/") : new String[0];
CodeSyncElement codeSyncElement = getCodeSyncElement(srcDir, fragments);
String srcDirPath = EditorPlugin.getInstance().getFileAccessController().getPathRelativeToFile(srcDirFile, project);
String relativeToProject =EditorPlugin.getInstance().getFileAccessController().getPathRelativeToFile(file, project);
if (codeSyncElement == null || showDialog) {
runCodeSyncAlgorithm(srcDir, project, resourceSet, srcDirPath, relativeToProject, technology, communicationChannel, showDialog);
} else {
if (showDialog) {
runCodeSyncAlgorithm(srcDir, project, resourceSet, srcDirPath, relativeToProject, technology, communicationChannel, showDialog);
}
return codeSyncElement;
}
return getCodeSyncElement(srcDir, fragments);
}
/**
* Finds the {@link CodeSyncElement} corresponding to the <code>path</code> by traversing the CSE tree
* rooted at the <code>srcDir</code>.
*
* @author Mariana
*/
public CodeSyncElement getCodeSyncElement(CodeSyncElement srcDir, String[] path) {
CodeSyncElement codeSyncElement = srcDir;
boolean foundForPath = true;
for (int i = 0; i < path.length; i++) {
boolean foundChild = false;
for (CodeSyncElement cse : codeSyncElement.getChildren()) {
String name = (String) CodeSyncOperationsService.getInstance().getKeyFeatureValue(cse);
if (name.equals(path[i])) {
codeSyncElement = cse;
foundChild = true;
break;
}
}
if (!foundChild) {
foundForPath = false;
break;
}
}
if (foundForPath) {
return codeSyncElement;
}
return null;
}
/**
* @author Mariana
* @author Sebastian Solomon
*/
public CodeSyncEditableResource runCodeSyncAlgorithm(CodeSyncElement model, Object project, ResourceSet resourceSet, String path, String limitedPath, String technology, CommunicationChannel communicationChannel, boolean showDialog) {
// public CodeSyncEditableResource runCodeSyncAlgorithm(CodeSyncElement model, IProject project, IResource file, String technology, CommunicationChannel communicationChannel) {
CodeSyncEditorStatefulService service = (CodeSyncEditorStatefulService) CommunicationPlugin.getInstance().getServiceRegistry().getService(CodeSyncEditorStatefulService.SERVICE_ID);
String editableResourcePath = EditorPlugin.getInstance().getFileAccessController().getPath(project);
CodeSyncEditableResource editableResource; // = (CodeSyncEditableResource) service.getEditableResource(project.getFullPath().toString());
if (showDialog) {
editableResource = (CodeSyncEditableResource) service.subscribeClientForcefully(communicationChannel, editableResourcePath);
} else {
service.subscribe(new StatefulServiceInvocationContext(communicationChannel), new EditorStatefulClientLocalState(editableResourcePath));
editableResource = (CodeSyncEditableResource) service.getEditableResource(editableResourcePath);
}
// CodeSyncEditableResource editableResource = new CodeSyncEditableResource();
Match match = new Match();
match.setAncestor(model);
match.setLeft(model);
Object ast;
if (model.getType().equals(CodeSyncPlugin.FOLDER)) {
ast = CodeSyncPlugin.getInstance().getProjectAccessController().getFolder(project, path);
}else {
ast = CodeSyncPlugin.getInstance().getProjectAccessController().getFile(project, path);
}
// ast = file.getParent();
match.setRight(ast);
match.setEditableResource(editableResource);
editableResource.setMatch(match);
ModelAdapterFactorySet modelAdapterFactorySet = getModelAdapterFactorySetProvider().getFactorieSets().get(technology);
if (!limitedPath.startsWith("/")) {
limitedPath = "/" + limitedPath;
}
modelAdapterFactorySet.initialize(CodeSyncPlugin.getInstance().getAstCache(project, resourceSet), null, CodeSyncPlugin.getInstance().useUIDs());
editableResource.setModelAdapterFactorySet(modelAdapterFactorySet);
new CodeSyncAlgorithm(editableResource.getModelAdapterFactorySet()).generateDiff(match);
// StatefulServiceInvocationContext context = new StatefulServiceInvocationContext(communicationChannel);
// service.attemptUpdateEditableResourceContent(context, project.getFullPath().toString(), null);
if (!showDialog) {
// we're not showing the dialog => perform sync
StatefulServiceInvocationContext context = new StatefulServiceInvocationContext(communicationChannel);
service.synchronize(context, editableResourcePath);
// and unsubscribe
service.unsubscribeAllClientsForcefully(editableResourcePath, false);
}
return editableResource;
// return null;
}
/**
* @author Mariana
*/
public class Utils {
public void addFeatureChange(CodeSyncElement cse, EStructuralFeature feature, FeatureChange featureChange) {
cse.getFeatureChanges().put(feature, featureChange);
}
public void removeFeatureChange(CodeSyncElement cse, Object feature) {
cse.getFeatureChanges().removeKey(feature);
}
public void addChild(CodeSyncElement parent, CodeSyncElement child) {
parent.getChildren().add(child);
}
public void addAnnotationValue(Annotation annotation, AnnotationValue value) {
annotation.getValues().add(value);
}
public void setSuperClasses(Class cls, List<String> superClasses) {
cls.getSuperClasses().clear();
cls.getSuperClasses().addAll(superClasses);
}
public void setSuperInterfaces(Class cls, List<String> superInterfaces) {
cls.getSuperInterfaces().clear();
cls.getSuperInterfaces().addAll(superInterfaces);
}
public void setModifiers(ModifiableElement op, List<ExtendedModifier> modifiers) {
op.getModifiers().clear();
op.getModifiers().addAll(modifiers);
}
public void setParams(Operation op, List<Parameter> params) {
op.getParameters().clear();
op.getParameters().addAll(params);
}
public void addToResource(File project, ResourceSet resourceSet, AstCacheElement ace) {
CodeSyncPlugin.getInstance().getAstCache(project, resourceSet).getContents().add(ace);
}
public boolean testEquality(Resource expected, Resource actual, String name) {
return EcoreUtil.equals(CodeSyncPlugin.getInstance().getSrcDir(expected, name), CodeSyncPlugin.getInstance().getSrcDir(actual, name));
}
}
}