package com.sap.furcas.ide.editor.document;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.ui.internal.editors.text.WorkspaceOperationRunner;
import org.eclipse.ui.texteditor.AbstractDocumentProvider;
import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
import com.sap.furcas.ide.editor.CtsActivator;
import com.sap.furcas.ide.editor.imp.services.FurcasSourcePositionLocator;
import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock;
import com.sap.furcas.runtime.parser.PartitionAssignmentHandler;
/**
* A factory for {@link CtsDocument CtsDocuments}.
* It is furthermore responsible to save the content of documents to disk.
* Such workspace access are secured using the correct set of
* {@link ISchedulingRule scheduling rules}.
*
* @author Stephan Erb
*
*/
@SuppressWarnings("restriction")
public class CtsDocumentProvider extends AbstractDocumentProvider {
/**
* EMF Validation adds error markes to resources. These markers don't have a valid
* position. This class reads these markers and sets a valid possition according
* to the content of the document attached to the annotation model.
*
* @author Stephan Erb
*
*/
private final class TextAwareResourceMarkerAnnotationModel extends ResourceMarkerAnnotationModel {
private TextAwareResourceMarkerAnnotationModel(IResource resource) {
super(resource);
}
@Override
protected Position createPositionFromMarker(IMarker marker) {
Position position = super.createPositionFromMarker(marker);
if (position != null) {
return position;
}
try {
if (!marker.getType().equals(EValidator.MARKER)) {
return null; // not a marker we do care about.
}
} catch (CoreException e) {
// happens if the marker we try to receive is invalid
return null;
}
final Position[] positions = new Position[1];
final FurcasSourcePositionLocator locator = new FurcasSourcePositionLocator();
final String uriAttribute = marker.getAttribute(EValidator.URI_ATTRIBUTE, null);
if (uriAttribute != null) {
try {
editingDomain.runExclusive(new Runnable() {
@Override
public void run() {
EObject modelElementWithMarker = editingDomain.getResourceSet().getEObject(URI.createURI(uriAttribute), true);
if (modelElementWithMarker != null) {
TextBlock rootBlock = ((CtsDocument) fDocument).getRootBlock();
TextBlock tb = locator.findTextBlockOf(rootBlock, modelElementWithMarker,
editingDomain.getResourceSet());
if (tb != null) {
int offset = locator.getStartOffset(tb);
int length = locator.getLength(tb);
positions[0] = new Position(offset, length);
} else {
// element not represented in this view
}
}
}
});
} catch (InterruptedException e) {
return null;
}
}
return positions[0]; // the one calculated or null
}
}
private final IRunnableContext operationRunner = new WorkspaceOperationRunner();
private final TransactionalEditingDomain editingDomain;
private final PartitionAssignmentHandler partitionHandler;
private final ModelEditorInput editorInput;
public CtsDocumentProvider(ModelEditorInput editorInput, TransactionalEditingDomain editingDomain, PartitionAssignmentHandler partitionHandler) {
super();
this.editorInput = editorInput;
this.editingDomain = editingDomain;
this.partitionHandler = partitionHandler;
}
@Override
public boolean canSaveDocument(Object element) {
ElementInfo info = getElementInfo(element);
if (info == null) {
return false;
}
return ((BasicCommandStack) editingDomain.getCommandStack()).isSaveNeeded();
}
@Override
public void doSaveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite) throws CoreException {
try {
editingDomain.runExclusive(new Runnable() {
@Override
public void run() {
try {
saveResourcesIfDirty();
} catch (IOException e) {
CtsActivator.logger.logError("Failed to save resources", e);
throw new RuntimeException(e);
}
}
});
((BasicCommandStack) editingDomain.getCommandStack()).saveIsDone();
} catch (Exception e) {
throw new CoreException(new Status(Status.ERROR, CtsActivator.PLUGIN_ID, e.getMessage(), e));
}
}
private void saveResourcesIfDirty() throws IOException {
final Map<Object, Object> saveOptions = new HashMap<Object, Object>();
saveOptions.put(Resource.OPTION_SAVE_ONLY_IF_CHANGED, Resource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER);
partitionHandler.saveAllPartitions(saveOptions);
}
@Override
protected IDocument createDocument(Object element) {
assert element.equals(editorInput.asLightWeightEditorInput());
return new CtsDocument(editorInput);
}
@Override
public CtsDocument getDocument(Object element) {
return (CtsDocument) super.getDocument(element);
}
@Override
protected IAnnotationModel createAnnotationModel(Object element) throws CoreException {
assert element.equals(editorInput.asLightWeightEditorInput());
return new TextAwareResourceMarkerAnnotationModel(WorkspaceSynchronizer.getFile(
editorInput.getRootObject().eResource()));
}
@Override
protected ISchedulingRule getResetRule(Object element) {
Collection<ISchedulingRule> rules = new ArrayList<ISchedulingRule>();
for (Resource resource : getWorkspaceResources()) {
IFile file = WorkspaceSynchronizer.getFile(resource);
if (file != null) {
rules.add(ResourcesPlugin.getWorkspace().getRuleFactory().modifyRule(file));
}
}
return new MultiRule(rules.toArray(new ISchedulingRule[rules.size()]));
}
@Override
protected ISchedulingRule getSynchronizeRule(Object element) {
Collection<ISchedulingRule> rules = new ArrayList<ISchedulingRule>();
for (Resource resource : getWorkspaceResources()) {
IFile file = WorkspaceSynchronizer.getFile(resource);
if (file != null) {
rules.add(ResourcesPlugin.getWorkspace().getRuleFactory().refreshRule(file));
}
}
return new MultiRule(rules.toArray(new ISchedulingRule[rules.size()]));
}
@Override
protected ISchedulingRule getValidateStateRule(Object element) {
Collection<ISchedulingRule> rules = new ArrayList<ISchedulingRule>();
for (Resource resource : getWorkspaceResources()) {
IFile file = WorkspaceSynchronizer.getFile(resource);
if (file != null) {
rules.add(file);
}
}
return ResourcesPlugin.getWorkspace().getRuleFactory().validateEditRule(rules.toArray(new IFile[rules.size()]));
}
@Override
protected ISchedulingRule getSaveRule(Object element) {
Collection<ISchedulingRule> rules = new ArrayList<ISchedulingRule>();
for (Resource resource : getWorkspaceResources()) {
IFile file = WorkspaceSynchronizer.getFile(resource);
if (file != null) {
rules.add(computeSchedulingRule(file));
}
}
return new MultiRule(rules.toArray(new ISchedulingRule[rules.size()]));
}
private ISchedulingRule computeSchedulingRule(IResource toCreateOrModify) {
if (toCreateOrModify.exists()) {
return ResourcesPlugin.getWorkspace().getRuleFactory().modifyRule(toCreateOrModify);
}
IResource parent = toCreateOrModify;
do {
toCreateOrModify = parent;
parent = toCreateOrModify.getParent();
} while (parent != null && !parent.exists());
return ResourcesPlugin.getWorkspace().getRuleFactory().createRule(toCreateOrModify);
}
/**
* Filter plugin resources. Those cannot be changed and thus don't need and synchronization.
*/
private Collection<Resource> getWorkspaceResources() {
ArrayList<Resource> resources = new ArrayList<Resource>();
for (Resource r : editingDomain.getResourceSet().getResources()) {
if (!r.getURI().isPlatformPlugin()) {
resources.add(r);
}
}
return resources;
}
@Override
protected IRunnableContext getOperationRunner(IProgressMonitor monitor) {
return operationRunner;
}
@Override
public boolean isReadOnly(Object element) {
return false;
}
@Override
public boolean isModifiable(Object element) {
return true;
}
/**
* @see ModelEditorInput
*/
public void consumeModelEditorInput() {
editorInput.consume();
}
}