/* 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; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceFactoryImpl; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl; import org.flowerplatform.blazeds.custom_serialization.CustomSerializationDescriptor; import org.flowerplatform.codesync.changes_processor.CodeSyncTypeCriterionDispatcherProcessor; import org.flowerplatform.codesync.config.extension.AddNewExtension; import org.flowerplatform.codesync.config.extension.AddNewExtension_Note; import org.flowerplatform.codesync.config.extension.AddNewExtension_TopLevelElement; import org.flowerplatform.codesync.config.extension.FeatureAccessExtension; import org.flowerplatform.codesync.config.extension.InplaceEditorExtension; import org.flowerplatform.codesync.config.extension.InplaceEditorExtension_Default; import org.flowerplatform.codesync.config.extension.InplaceEditorExtension_Note; import org.flowerplatform.codesync.config.extension.NamedElementFeatureAccessExtension; import org.flowerplatform.codesync.processor.RelationDiagramProcessor; import org.flowerplatform.codesync.projects.IProjectAccessController; import org.flowerplatform.codesync.remote.CodeSyncElementDescriptor; import org.flowerplatform.codesync.remote.CodeSyncOperationsService; import org.flowerplatform.codesync.remote.RelationDescriptor; import org.flowerplatform.common.plugin.AbstractFlowerJavaPlugin; import org.flowerplatform.communication.CommunicationPlugin; import org.flowerplatform.editor.EditorPlugin; import org.flowerplatform.editor.model.EditorModelPlugin; import org.flowerplatform.editor.model.remote.DiagramEditableResource; import org.flowerplatform.editor.model.remote.DiagramEditorStatefulService; import org.flowerplatform.editor.remote.EditableResource; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.crispico.flower.mp.model.codesync.CodeSyncElement; import com.crispico.flower.mp.model.codesync.CodeSyncPackage; import com.crispico.flower.mp.model.codesync.CodeSyncRoot; /** * @author Mariana Gheorghe * @author Cristian Spiescu */ public class CodeSyncPlugin extends AbstractFlowerJavaPlugin { protected static CodeSyncPlugin INSTANCE; public static final String CONTEXT_INITIALIZATION_TYPE = "initializationType"; /** * The location of the CSE mapping file, relative to the project. May be * configurable in the future. * * @author Mariana */ public String CSE_MAPPING_FILE_LOCATION = "/CSE.notation"; /** * The location of the ACE file, relative to the project. May be configurable * in the future. * * @author Mariana */ public String ACE_FILE_LOCATION = "/ACE.notation"; protected List<String> srcDirs = null; public static final String FOLDER = "Folder"; public static final String FILE = "File"; private final static Logger logger = LoggerFactory.getLogger(CodeSyncPlugin.class); protected ComposedFullyQualifiedNameProvider fullyQualifiedNameProvider; protected ComposedCodeSyncAlgorithmRunner codeSyncAlgorithmRunner; protected List<CodeSyncElementDescriptor> codeSyncElementDescriptors; protected List<RelationDescriptor> relationDescriptors; protected List<FeatureAccessExtension> featureAccessExtensions; protected List<AddNewExtension> addNewExtensions; /** * @author Cristina Constantinescu */ protected List<InplaceEditorExtension> inplaceEditorExtensions; protected CodeSyncTypeCriterionDispatcherProcessor codeSyncTypeCriterionDispatcherProcessor; /** * Runnables that create and add descriptors. * * @author Mircea Negreanu */ protected List<Runnable> runnablesThatLoadDescriptors; /** * @see #getProjectAccessController() */ private IProjectAccessController projectAccessController; protected boolean useUIDs = true; public static CodeSyncPlugin getInstance() { return INSTANCE; } public CodeSyncTypeCriterionDispatcherProcessor getCodeSyncTypeCriterionDispatcherProcessor() { return codeSyncTypeCriterionDispatcherProcessor; } public ComposedFullyQualifiedNameProvider getFullyQualifiedNameProvider() { return fullyQualifiedNameProvider; } public ComposedCodeSyncAlgorithmRunner getCodeSyncAlgorithmRunner() { return codeSyncAlgorithmRunner; } /** * Platform-dependent. * * @author Mariana Gheorghe */ public IProjectAccessController getProjectAccessController() { return projectAccessController; } public void setProjectsProvider(IProjectAccessController projectAccessController) { this.projectAccessController = projectAccessController; } public List<CodeSyncElementDescriptor> getCodeSyncElementDescriptors() { return codeSyncElementDescriptors; } public List<RelationDescriptor> getRelationDescriptors() { return relationDescriptors; } public CodeSyncElementDescriptor getCodeSyncElementDescriptor(String codeSyncType) { // TODO CS/JS we should have a mapping; maybe send it to flex as a map; for quick access; idem for relations for (CodeSyncElementDescriptor descriptor : getCodeSyncElementDescriptors()) { if (descriptor.getCodeSyncType().equals(codeSyncType)) { return descriptor; } } return null; } // TODO CS/JS we should unify the descriptors. And have them in Model? public RelationDescriptor getRelationDescriptor(String type) { for (RelationDescriptor descriptor : getRelationDescriptors()) { if (descriptor.getType().equals(type)) { return descriptor; } } return null; } public List<FeatureAccessExtension> getFeatureAccessExtensions() { return featureAccessExtensions; } public List<AddNewExtension> getAddNewExtensions() { return addNewExtensions; } /** * @author Cristina Constantinescu */ public List<InplaceEditorExtension> getInplaceEditorExtensions() { return inplaceEditorExtensions; } public boolean useUIDs() { return useUIDs; } /** * @author Mariana Gheorge * @author Mircea Negreanu * @author Cristina Constantinescu */ @Override public void start(BundleContext context) throws Exception { super.start(context); INSTANCE = this; // initialize the list of code that will regenerate the descriptors runnablesThatLoadDescriptors = new ArrayList<>(); // initialize codesyncplugin internals codeSyncTypeCriterionDispatcherProcessor = new CodeSyncTypeCriterionDispatcherProcessor(); codeSyncElementDescriptors = new ArrayList<CodeSyncElementDescriptor>(); relationDescriptors = new ArrayList<RelationDescriptor>(); addNewExtensions = new ArrayList<AddNewExtension>(); inplaceEditorExtensions = new ArrayList<InplaceEditorExtension>(); featureAccessExtensions = new ArrayList<FeatureAccessExtension>(); fullyQualifiedNameProvider = new ComposedFullyQualifiedNameProvider(); // main list of code sync descriptors addRunnablesForLoadDescriptors(new Runnable() { @Override public void run() { // descriptors List<CodeSyncElementDescriptor> descriptors = new ArrayList<>(); descriptors.add( new CodeSyncElementDescriptor() .setCodeSyncType(FOLDER) .setLabel(FOLDER) .addChildrenCodeSyncTypeCategory(FILE) .addFeature(NamedElementFeatureAccessExtension.NAME) .setKeyFeature(NamedElementFeatureAccessExtension.NAME)); descriptors.add( new CodeSyncElementDescriptor() .setCodeSyncType(FILE) .setLabel(FILE) .addCodeSyncTypeCategory(FILE) .addFeature(NamedElementFeatureAccessExtension.NAME) .setKeyFeature(NamedElementFeatureAccessExtension.NAME)); getCodeSyncElementDescriptors().addAll(descriptors); EditorModelPlugin.getInstance().getMainChangesDispatcher().addProcessor(codeSyncTypeCriterionDispatcherProcessor); // extensions getFeatureAccessExtensions().add(new NamedElementFeatureAccessExtension(descriptors)); getAddNewExtensions().add(new AddNewExtension_Note()); getAddNewExtensions().add(new AddNewExtension_TopLevelElement()); getInplaceEditorExtensions().add(new InplaceEditorExtension_Default()); getInplaceEditorExtensions().add(new InplaceEditorExtension_Note()); // processors EditorModelPlugin.getInstance().getDiagramUpdaterChangeProcessor().addDiagrammableElementFeatureChangeProcessor("edge", new RelationDiagramProcessor()); } }); // give the codeSyncExtension the signal to register their own runnable descriptors initializeExtensionPoint_codeSyncAlgorithmRunner(); // needs custom descriptor because it uses the builder template (i.e. setters return the instance) new CustomSerializationDescriptor(CodeSyncElementDescriptor.class) .addDeclaredProperty("codeSyncType") .addDeclaredProperty("initializationTypes") .addDeclaredProperty("initializationTypesLabels") .addDeclaredProperty("initializationTypesOrderIndexes") .addDeclaredProperty("label") .addDeclaredProperty("iconUrl") .addDeclaredProperty("defaultName") .addDeclaredProperty("extension") .addDeclaredProperty("codeSyncTypeCategories") .addDeclaredProperty("childrenCodeSyncTypeCategories") .addDeclaredProperty("category") .addDeclaredProperty("features") .addDeclaredProperty("keyFeature") .addDeclaredProperty("standardDiagramControllerProviderFactory") .addDeclaredProperty("orderIndex") .register(); new CustomSerializationDescriptor(RelationDescriptor.class) .addDeclaredProperty("type") .addDeclaredProperty("label") .addDeclaredProperty("iconUrl") .addDeclaredProperty("sourceCodeSyncTypes") .addDeclaredProperty("targetCodeSyncTypes") .addDeclaredProperty("sourceCodeSyncTypeCategories") .addDeclaredProperty("targetCodeSyncTypeCategories") .register(); } private void initializeExtensionPoint_codeSyncAlgorithmRunner() throws CoreException { codeSyncAlgorithmRunner = new ComposedCodeSyncAlgorithmRunner(); IConfigurationElement[] configurationElements = Platform.getExtensionRegistry().getConfigurationElementsFor("org.flowerplatform.codesync.codeSyncAlgorithmRunner"); for (IConfigurationElement configurationElement : configurationElements) { String id = configurationElement.getAttribute("id"); String technology = configurationElement.getAttribute("technology"); Object instance = configurationElement.createExecutableExtension("codeSyncAlgorithmRunnerClass"); codeSyncAlgorithmRunner.addRunner(technology, (ICodeSyncAlgorithmRunner) instance); logger.debug("Added CodeSync algorithm runner with id = {} with class = {}", id, instance.getClass()); } } /** * @author Mariana Gheorghe */ public String getFileExtension(File file) { String name = file.getName(); int index = name.lastIndexOf("."); if (index >= 0) { return name.substring(index + 1); } return ""; } /** * Important: the code sync mapping and cache resources <b>must</b> be loaded through the same {@link ResourceSet}. */ public ResourceSet getOrCreateResourceSet(Object file, String diagramEditorStatefulServiceId) { Object project = getProjectAccessController().getContainingProjectForFile(file); DiagramEditorStatefulService service = (DiagramEditorStatefulService) CommunicationPlugin.getInstance() .getServiceRegistry().getService(diagramEditorStatefulServiceId); DiagramEditableResource diagramEditableResource = null; if (project != null) { String path = EditorPlugin.getInstance().getFileAccessController().getAbsolutePath(project); for (EditableResource er : service.getEditableResources().values()) { DiagramEditableResource der = (DiagramEditableResource) er; if (EditorPlugin.getInstance().getFileAccessController().getAbsolutePath(der.getFile()).startsWith(path)) { diagramEditableResource = der; break; } } } if (diagramEditableResource != null) { return diagramEditableResource.getResourceSet(); } ResourceSet resourceSet = new ResourceSetImpl(); resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION, new ResourceFactoryImpl() { @Override public Resource createResource(URI uri) { return new XMIResourceImpl(uri) { protected boolean useUUIDs() { return true; } }; } }); return resourceSet; } /** * @author Mariana * @author Sebastian Solomon */ public Resource getResource(ResourceSet resourceSet, Object file) { URI uri = EditorModelPlugin.getInstance().getModelAccessController().getURIFromFile(file); boolean fileExists = EditorPlugin.getInstance().getFileAccessController().exists(file); return getResource(resourceSet, uri, fileExists); } /** * @author Mariana */ public Resource getResource(ResourceSet resourceSet, URI uri, boolean fileExists) { if (fileExists) { return resourceSet.getResource(uri, true); } else { Resource resource = resourceSet.getResource(uri, false); if (resource == null) { resource = resourceSet.createResource(uri); } resource.unload(); return resource; } } /** * Saves all the resources from the {@link ResourceSet} where <code>resourceToSave</code> * is contained. * * @author Mariana */ public void saveResource(Resource resourceToSave) { if (resourceToSave != null) { List<Resource> resources = Collections.singletonList(resourceToSave); if (resourceToSave.getResourceSet() != null) { resources = resourceToSave.getResourceSet().getResources(); } for (Resource resource : resources) { try { Map<Object, Object> options = EditorModelPlugin.getInstance().getLoadSaveOptions(); resource.save(options); } catch (IOException e) { throw new RuntimeException(e); } } } } /** * Discards the modifications from all the resources from the {@link ResourceSet} * where <code>resourceToDiscard</code> is contained. * * @author Mariana */ public void discardResource(Resource resourceToDiscard) { if (resourceToDiscard != null) { List<Resource> resources = Collections.singletonList(resourceToDiscard); if (resourceToDiscard.getResourceSet() != null) { resources = resourceToDiscard.getResourceSet().getResources(); } for (Resource resource : resources) { resource.unload(); } } } /** * @author Mariana */ public Resource getCodeSyncMapping(Object project, ResourceSet resourceSet) { Object codeSyncElementMappingFile = CodeSyncPlugin.getInstance().getProjectAccessController().getFile(project, CSE_MAPPING_FILE_LOCATION); Resource cseResource = CodeSyncPlugin.getInstance().getResource(resourceSet, codeSyncElementMappingFile); if (!EditorPlugin.getInstance().getFileAccessController().exists(codeSyncElementMappingFile)) { // first clear the resource in case the mapping file was deleted // after it has been loaded at a previous moment cseResource.getContents().clear(); for (String srcDir : getSrcDirs()) { CodeSyncRoot cseRoot = (CodeSyncRoot) getRoot(cseResource, srcDir); if (cseRoot == null) { // create the CSE for the SrcDir cseRoot = CodeSyncPackage.eINSTANCE.getCodeSyncFactory().createCodeSyncRoot(); cseRoot.setName(srcDir); cseRoot.setType(FOLDER); } cseResource.getContents().add(cseRoot); } CodeSyncPlugin.getInstance().saveResource(cseResource); } return cseResource; } /** * @author Mariana */ public Resource getAstCache(Object project, ResourceSet resourceSet) { Object astCacheElementFile = CodeSyncPlugin.getInstance().getProjectAccessController().getFile(project, ACE_FILE_LOCATION); Resource resource = CodeSyncPlugin.getInstance().getResource(resourceSet, astCacheElementFile); if (!EditorPlugin.getInstance().getFileAccessController().exists(astCacheElementFile)) { resource.getContents().clear(); CodeSyncPlugin.getInstance().saveResource(resource); } return resource; } /** * @author Mariana */ protected CodeSyncRoot getRoot(Resource resource, String srcDir) { for (EObject eObj : resource.getContents()) { if (eObj instanceof CodeSyncRoot) { CodeSyncRoot root = (CodeSyncRoot) eObj; if (CodeSyncOperationsService.getInstance() .getKeyFeatureValue(root).equals(srcDir)) return root; } } return null; } /** * @author Mariana */ public CodeSyncElement getSrcDir(Resource resource, String name) { CodeSyncElement srcDir = null; for (EObject member : resource.getContents()) { if ((CodeSyncOperationsService.getInstance().getKeyFeatureValue((CodeSyncElement) member)).equals(name)) { srcDir = (CodeSyncElement) member; break; } } return srcDir; } /** * @author Mariana */ public List<String> getSrcDirs() { if (srcDirs == null) { // TODO Mariana : get user input return Collections.singletonList("src"); } return srcDirs; } public void addSrcDir(String srcDir) { if (srcDirs == null) { srcDirs = new ArrayList<String>(); } if (!srcDirs.contains(srcDir)) { srcDirs.add(srcDir); } } /** * Executes the runnable and keeps it around to be executed * when the descriptors need to be refreshed. * * @param runnable * * @author Mircea Negreanu */ public void addRunnablesForLoadDescriptors(Runnable runnable) { runnable.run(); runnablesThatLoadDescriptors.add(runnable); } /** * Reruns all the registered runnable to regenerate descriptors, * processors, .. * * @return String containing errors thrown during run (if any) */ public String regenerateDescriptors() { // clear the descriptors getCodeSyncElementDescriptors().clear(); getRelationDescriptors().clear(); getFeatureAccessExtensions().clear(); getAddNewExtensions().clear(); getInplaceEditorExtensions().clear(); getCodeSyncTypeCriterionDispatcherProcessor().clear(); StringBuilder errorsCollected = new StringBuilder(); for (Runnable run: runnablesThatLoadDescriptors) { try { run.run(); } catch (Exception ex) { errorsCollected.append(ex.toString()); errorsCollected.append("\n"); } } return errorsCollected.toString(); } }