/* 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.wiki; import java.io.File; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; 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.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.remote.EditorStatefulClientLocalState; import org.flowerplatform.model.astcache.wiki.FlowerBlock; import org.flowerplatform.model.astcache.wiki.Page; import org.osgi.framework.BundleContext; import com.crispico.flower.mp.codesync.base.CodeSyncEditableResource; import com.crispico.flower.mp.codesync.base.CodeSyncElementFeatureProvider; import com.crispico.flower.mp.codesync.base.CodeSyncPlugin; 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.communication.CodeSyncEditorStatefulService; import com.crispico.flower.mp.codesync.wiki.adapter.WikiNodeModelAdapter; import com.crispico.flower.mp.codesync.wiki.adapter.WikiNodeModelAdapterRight; import com.crispico.flower.mp.codesync.wiki.featureprovider.WikiPageFeatureProvider; import com.crispico.flower.mp.codesync.wiki.github.GithubConfigurationProvider; import com.crispico.flower.mp.model.codesync.AstCacheElement; import com.crispico.flower.mp.model.codesync.CodeSyncElement; import com.crispico.flower.mp.model.codesync.CodeSyncRoot; /** * @author Mariana */ public class WikiPlugin extends AbstractFlowerJavaPlugin { protected static WikiPlugin INSTANCE; public static final String CSE_FILE_LOCATION = "/Wiki.notation"; public static final String ACE_FILE_LOCATION = "/WikiCache.notation"; public static final String FOLDER_CATEGORY = "folder"; public static final String PAGE_CATEGORY = "page"; public static final String HEADING_LEVEL_1_CATEGORY = "heading 1"; public static final String HEADING_LEVEL_2_CATEGORY = "heading 2"; public static final String HEADING_LEVEL_3_CATEGORY = "heading 3"; public static final String HEADING_LEVEL_4_CATEGORY = "heading 4"; public static final String HEADING_LEVEL_5_CATEGORY = "heading 5"; public static final String HEADING_LEVEL_6_CATEGORY = "heading 6"; public static final String ORDERED_LIST_CATEGORY = "ordered list"; public static final String ORDERED_LIST_ITEM_CATEGORY = "ordered list item"; public static final String UNORDERED_LIST_CATEGORY = "unordered list"; public static final String UNORDERED_LIST_ITEM_CATEGORY = "unordered list item"; public static final String BLOCKQUOTE_CATEGORY = "blockquote"; public static final String BLOCKQUOTE_CHILD_CATEGORY = "blockquote child"; public static final String CODE_CATEGORY = "code"; public static final String CODE_LINE_CATEGORY = "code line"; public static final String PARAGRAPH_CATEGORY = "paragraph"; public static final String FLOWER_BLOCK_CATEGORY = "flowerBlock"; protected Map<String, IConfigurationProvider> configurationProviders = new HashMap<String, IConfigurationProvider>(); public void start(BundleContext bundleContext) throws Exception { super.start(bundleContext); INSTANCE = this; // TODO this should be done from a new plugin probably getConfigurationProviders().put(GithubConfigurationProvider.TECHNOLOGY, new GithubConfigurationProvider()); } @Override public void registerMessageBundle() throws Exception { // nothing to do yet } public static WikiPlugin getInstance() { return INSTANCE; } public Map<String, IConfigurationProvider> getConfigurationProviders() { return configurationProviders; } public CodeSyncRoot getWikiRoot(File file, String technology, ResourceSet resourceSet, CommunicationChannel communicationChannel) { Object project = CodeSyncPlugin.getInstance().getProjectAccessController().getContainingProjectForFile(file); String name = EditorPlugin.getInstance().getFileAccessController().getPath(project); // this will be a temporary tree, do not send the project CodeSyncRoot leftRoot = getWikiTree(null, resourceSet, project, name, technology); CodeSyncRoot rightRoot = getWikiTree((File)project, resourceSet, null, name, technology); updateTree(leftRoot, rightRoot, (File)project, resourceSet, technology, communicationChannel, false); return rightRoot; } public void updateTree(CodeSyncElement left, CodeSyncElement right, File project, ResourceSet resourceSet, String technology, CommunicationChannel communicationChannel, boolean showDialog) { CodeSyncEditorStatefulService service = (CodeSyncEditorStatefulService) CommunicationPlugin.getInstance().getServiceRegistry().getService(CodeSyncEditorStatefulService.SERVICE_ID); String editableResourcePath = EditorPlugin.getInstance().getFileAccessController().getPath(project); CodeSyncEditableResource editableResource; if (showDialog) { editableResource = (CodeSyncEditableResource) service.subscribeClientForcefully(communicationChannel, editableResourcePath); } else { service.subscribe(new StatefulServiceInvocationContext(communicationChannel), new EditorStatefulClientLocalState(editableResourcePath)); editableResource = (CodeSyncEditableResource) service.getEditableResource(editableResourcePath); } Match match = new Match(); match.setAncestor(right); match.setLeft(left); match.setRight(right); ModelAdapterFactory rightFactory = new ModelAdapterFactory(); WikiNodeModelAdapterRight rightAdapter = new WikiNodeModelAdapterRight(); rightAdapter.setModelAdapterFactory(rightFactory); rightAdapter.setEObjectConverter(rightFactory); rightAdapter.setResource(getAstCacheResource(project, resourceSet)); rightAdapter.setTechnology(technology); rightFactory.addModelAdapter(CodeSyncElement.class, rightAdapter, ""); ModelAdapterFactory ancestorFactory = new ModelAdapterFactory(); WikiNodeModelAdapter ancestorAdapter = new WikiNodeModelAdapter(); ancestorAdapter.setModelAdapterFactory(ancestorFactory); ancestorAdapter.setEObjectConverter(ancestorFactory); ancestorAdapter.setTechnology(technology); ancestorFactory.addModelAdapter(CodeSyncElement.class, ancestorAdapter, ""); ModelAdapterFactory leftFactory = new ModelAdapterFactory(); WikiNodeModelAdapter leftAdapter = new WikiNodeModelAdapter(); leftAdapter.setModelAdapterFactory(leftFactory); leftAdapter.setEObjectConverter(leftFactory); leftAdapter.setResource(getAstCacheResource(left)); leftAdapter.setTechnology(technology); leftFactory.addModelAdapter(CodeSyncElement.class, leftAdapter, ""); ModelAdapterFactorySet factorySet = new ModelAdapterFactorySet(ancestorFactory, leftFactory, rightFactory); factorySet.addFeatureProvider(CodeSyncElement.class, new CodeSyncElementFeatureProvider()); factorySet.addFeatureProvider(WikiPlugin.PAGE_CATEGORY, new WikiPageFeatureProvider()); match.setEditableResource(editableResource); editableResource.setMatch(match); editableResource.setModelAdapterFactorySet(factorySet); new WikiSyncAlgorithm(editableResource.getModelAdapterFactorySet(), technology).generateDiff(match); if (!showDialog) { // we're not showing the dialog => perform sync StatefulServiceInvocationContext context = new StatefulServiceInvocationContext(communicationChannel); service.synchronize(context, editableResourcePath); service.applySelectedActions(context, editableResourcePath, false); } } public Resource getAstCacheResource(CodeSyncElement root) { if (root.getAstCacheElement() != null) { return root.getAstCacheElement().eResource(); } for (CodeSyncElement child : root.getChildren()) { return getAstCacheResource(child); } return null; } public Resource getAstCacheResource(File project, ResourceSet resourceSet) { Object astCacheElementFile = CodeSyncPlugin.getInstance().getProjectAccessController().getFile(project, ACE_FILE_LOCATION); return CodeSyncPlugin.getInstance().getResource(resourceSet, astCacheElementFile); } public Resource getCodeSyncMappingResource(File project, ResourceSet resourceSet) { Object codeSyncElementMappingFile = CodeSyncPlugin.getInstance().getProjectAccessController().getFile(project, CSE_FILE_LOCATION); return CodeSyncPlugin.getInstance().getResource(resourceSet, codeSyncElementMappingFile); } /** * Creates a wiki tree corresponding to the content from <code>wikiText</code>, rooted at <code>page</code>. * The <code>flowerBlocks</code> will be used to populate the content of Flower blocks found in the content. */ public void getWikiPageTree(String wikiText, CodeSyncElement page, String technology, List<FlowerBlock> flowerBlocks) { WikiRegexConfiguration config = new WikiRegexConfiguration(); IConfigurationProvider configurationProvider = configurationProviders.get(technology); configurationProvider.buildConfiguration(config, page); config.setSessionClass(configurationProvider.getWikiTreeBuilderClass()); config.compile(Pattern.MULTILINE); WikiTreeBuilder session = (WikiTreeBuilder) config.startSession(wikiText); session.setRoot(page); session.setFlowerBlocks(flowerBlocks); session.setResource(getAstCacheResource(page)); while (session.find()) { } } /** * Generates the content from a wiki page tree. */ public String getWikiText(CodeSyncElement tree, String technology) { if (tree == null) { return null; } return configurationProviders.get(technology).getWikiTextBuilder(tree).getWikiText(tree); } /** * Delegates to {@link IConfigurationProvider#getWikiTree(String, Object)}. */ public CodeSyncRoot getWikiTree(File project, ResourceSet resourceSet, Object wiki, String name, String technology) { Resource codeSyncResource = project == null ? resourceSet.createResource(URI.createURI("tempCSE")) : getCodeSyncMappingResource(project, resourceSet); CodeSyncRoot root = getRoot(codeSyncResource, name); if (root == null) { root = configurationProviders.get(technology).getWikiTree(wiki); codeSyncResource.getContents().add(root); if (wiki == null) { root.setName(name); } } Resource astCacheResource = project == null ? resourceSet.createResource(URI.createURI("tempACE")) : getAstCacheResource(project, resourceSet); addToAstCacheResource(root, astCacheResource); return root; } private CodeSyncRoot getRoot(Resource resource, String name) { for (EObject object : resource.getContents()) { if (name.equals(((CodeSyncRoot) object).getName())) { return (CodeSyncRoot) object; } } return null; } public void addToAstCacheResource(CodeSyncElement root, Resource resource) { AstCacheElement ace = root.getAstCacheElement(); if (ace != null) { resource.getContents().add(ace); } for (CodeSyncElement cse : root.getChildren()) { addToAstCacheResource(cse, resource); } } /** * Delegates to {@link IConfigurationProvider#savePage(Page)}. */ public void savePage(Page page, String technology) { configurationProviders.get(technology).savePage(page); } public String getLineDelimiter(String content) { if (content.contains("\r\n")) { return "\r\n"; } if (content.contains("\r")) { return "\r"; } return "\n"; } public int getHeadingLevel(String category) { if (category == null) { return -1; } if (category.startsWith("heading")) { return Integer.parseInt(category.substring(category.length() - 1)); } return -1; } public String getPagePath(Page page, String delimiter, boolean fromRoot) { StringBuilder builder = new StringBuilder(); CodeSyncElement crt = page.getCodeSyncElement(); while (getParent(crt) != null) { builder.insert(0, delimiter + crt.getName()); crt = getParent(crt); } if (fromRoot) { builder.insert(0, crt.getName()); } else { builder.deleteCharAt(0); } return builder.toString(); } private CodeSyncElement getParent(CodeSyncElement cse) { EObject eObj = cse.eContainer(); if (eObj instanceof CodeSyncElement) { return (CodeSyncElement) eObj; } return null; } /** * TODO move to test */ public void replaceWikiPage(CodeSyncElement rightRoot, CodeSyncElement newRightPage) { for (CodeSyncElement child : rightRoot.getChildren()) { if (child.getAstCacheElement() instanceof Page) { rightRoot.getChildren().remove(child); rightRoot.getChildren().add(newRightPage); return; } replaceWikiPage(child, newRightPage); } } /** * TODO move to test */ public Iterator<CodeSyncElement> getChildrenIterator(CodeSyncElement root) { return root.getChildren().iterator(); } }