/**
* Copyright (c) 2011-2012 Eclipse contributors and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.emf.ecore.xcore.ui.quickfix;
import org.eclipse.emf.codegen.ecore.genmodel.GenClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreValidator;
import org.eclipse.emf.ecore.xcore.XAttribute;
import org.eclipse.emf.ecore.xcore.XGenericType;
import org.eclipse.emf.ecore.xcore.formatting.XcoreImportOrganizer;
import org.eclipse.emf.ecore.xcore.validation.XcoreIssueCodes;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;
import org.eclipse.xtext.ui.editor.model.edit.IModification;
import org.eclipse.xtext.ui.editor.model.edit.IModificationContext;
import org.eclipse.xtext.ui.editor.quickfix.DefaultQuickfixProvider;
import org.eclipse.xtext.ui.editor.quickfix.Fix;
import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionAcceptor;
import org.eclipse.xtext.util.TextRegion;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import org.eclipse.xtext.validation.Issue;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class XcoreQuickfixProvider extends DefaultQuickfixProvider
{
@Inject
protected Provider<XcoreImportOrganizer> xcoreImportOrganizerProvider;
@Fix(EcoreValidator.DIAGNOSTIC_SOURCE + '.' + EcoreValidator.CONSISTENT_TYPE_CLASS_NOT_PERMITTED)
public void convertToReference(final Issue issue, final IssueResolutionAcceptor acceptor)
{
IModificationContext modificationContext = getModificationContextFactory().createModificationContext(issue);
final IXtextDocument xtextDocument = modificationContext.getXtextDocument();
xtextDocument.readOnly
(new IUnitOfWork.Void<XtextResource>()
{
@Override
public void process(XtextResource xtextResource) throws Exception
{
EObject cause = xtextResource.getResourceSet().getEObject(issue.getUriToProblem(), false);
if (cause instanceof XGenericType)
{
XGenericType xGenericType = (XGenericType)cause;
if (xGenericType.eContainer() instanceof XAttribute && xGenericType.getType() instanceof GenClass)
{
ICompositeNode node = NodeModelUtils.getNode(xGenericType.eContainer());
String range = node == null ? xtextDocument.get(issue.getOffset(), issue.getLength()) : xtextDocument.get(node.getOffset(), node.getLength());
quickFix(issue, "Convert to cross reference", "refers ", acceptor, range);
quickFix(issue, "Convert to containment reference", "contains ", acceptor, range);
quickFix(issue, "Convert to container reference", "container ", acceptor, range);
}
}
}
private void quickFix(final Issue issue, String description, final String replacement, final IssueResolutionAcceptor acceptor, String range)
{
acceptor.accept
(issue,
description,
replacement + range,
"full/obj16/correction_change.gif",
new IModification()
{
public void apply(IModificationContext context) throws BadLocationException
{
IXtextDocument xtextDocument = context.getXtextDocument();
xtextDocument.replace(issue.getOffset(), 0, replacement);
}
});
}
});
}
public static class RemovalRegion
{
protected IXtextDocument xtextDocument;
protected int begin;
protected int end;
protected int deleteBegin;
protected int deleteEnd;
protected String replacement;
public RemovalRegion(IXtextDocument xtextDocument, EObject eObject) throws BadLocationException
{
this.xtextDocument = xtextDocument;
// Determine the associated node.
//
ICompositeNode node = NodeModelUtils.getNode(eObject);
INode previous = node.getPreviousSibling();
begin = getLineBeginOffsetOfOffset(previous == null ? node.getOffset() : previous.getOffset() + previous.getLength());
INode next = node.getNextSibling();
end = getLineEndOffsetOfOffset(next == null ? node.getOffset() + node.getLength() : next.getOffset());
deleteBegin = getBeginOffset(node);
deleteEnd = getEndOffset(node);
replacement =
deleteBegin > 0 && !Character.isWhitespace(xtextDocument.getChar(deleteBegin - 1)) && deleteEnd + 1 < xtextDocument.getLength() && !Character.isWhitespace(xtextDocument.getChar(deleteEnd + 1)) ?
" " :
"";
}
public int getBegin()
{
return begin;
}
public int getEnd()
{
return end;
}
public int getDeleteBegin()
{
return deleteBegin;
}
public int getDeleteEnd()
{
return deleteEnd;
}
public String getReplacement()
{
return replacement;
}
protected int getLineBeginOffsetOfOffset(int offset) throws BadLocationException
{
return xtextDocument.getLineOffset(xtextDocument.getLineOfOffset(offset));
}
protected int getLineEndOffsetOfOffset(int offset) throws BadLocationException
{
int lineOfOffset = xtextDocument.getLineOfOffset(offset);
return
lineOfOffset + 1 < xtextDocument.getNumberOfLines() ?
xtextDocument.getLineOffset(lineOfOffset + 1) :
xtextDocument.getLength();
}
protected int getBeginOffset(INode node) throws BadLocationException
{
int result = node.getOffset();
INode previous = node.getPreviousSibling();
if (previous != null)
{
int nodeLine = xtextDocument.getLineOfOffset(result);
int previousLine = xtextDocument.getLineOfOffset(previous.getOffset() + previous.getLength());
if (nodeLine == previousLine)
{
result = previous.getOffset() + previous.getLength();
}
else
{
while (--nodeLine > previousLine)
{
boolean isWhite = true;
for (int j = xtextDocument.getLineOffset(nodeLine), end = j + xtextDocument.getLineLength(nodeLine); j < end; ++j)
{
if (!Character.isWhitespace(xtextDocument.getChar(j)))
{
isWhite = false;
break;
}
}
if (isWhite)
{
break;
}
}
result = xtextDocument.getLineOffset(nodeLine + 1);
}
}
else
{
result = getLineBeginOffsetOfOffset(result);
}
return result;
}
protected int getEndOffset(INode node) throws BadLocationException
{
int result = node.getOffset() + node.getLength();
INode next = node.getNextSibling();
if (next != null)
{
int nodeLine = xtextDocument.getLineOfOffset(result);
int nextLine = xtextDocument.getLineOfOffset(next.getOffset() + next.getLength());
if (nodeLine == nextLine)
{
result = next.getOffset();
}
else
{
result = getLineEndOffsetOfOffset(result);
}
}
else
{
result = getLineEndOffsetOfOffset(result);
}
return result;
}
}
@Fix(XcoreIssueCodes.COLLIDING_IMPORT)
public void collidingImport(final Issue issue, final IssueResolutionAcceptor acceptor)
{
removeImport(issue, acceptor);
oranizeImports(issue, acceptor);
}
@Fix(XcoreIssueCodes.DUPLICATE_IMPORT)
public void duplicateImport(final Issue issue, final IssueResolutionAcceptor acceptor)
{
removeImport(issue, acceptor);
oranizeImports(issue, acceptor);
}
@Fix(XcoreIssueCodes.UNUSED_IMPORT)
public void unusedImport(final Issue issue, final IssueResolutionAcceptor acceptor)
{
removeImport(issue, acceptor);
oranizeImports(issue, acceptor);
}
public void removeImport(final Issue issue, final IssueResolutionAcceptor acceptor)
{
IModificationContext modificationContext = getModificationContextFactory().createModificationContext(issue);
final IXtextDocument xtextDocument = modificationContext.getXtextDocument();
xtextDocument.readOnly
(new IUnitOfWork.Void<XtextResource>()
{
@Override
public void process(XtextResource xtextResource) throws Exception
{
// The cause is expected to be an import directive.
//
EObject cause = xtextResource.getResourceSet().getEObject(issue.getUriToProblem(), false);
final RemovalRegion removalRegion = new RemovalRegion(xtextDocument, cause);
acceptor.accept
(issue,
"Remove import",
"...\n" +
xtextDocument.get(removalRegion.begin, removalRegion.deleteBegin - removalRegion.begin) +
removalRegion.replacement +
xtextDocument.get(removalRegion.deleteEnd, removalRegion.end - removalRegion.deleteEnd) +
"\n...",
"full/obj16/delete_obj.gif",
new IModification()
{
public void apply(IModificationContext context) throws BadLocationException
{
IXtextDocument xtextDocument = context.getXtextDocument();
xtextDocument.replace(removalRegion.deleteBegin, removalRegion.deleteEnd- removalRegion.deleteBegin, removalRegion.replacement);
}
});
}
});
}
@Fix(XcoreIssueCodes.WILDCARD_IMPORT)
public void oranizeImports(final Issue issue, final IssueResolutionAcceptor acceptor)
{
IModificationContext modificationContext = getModificationContextFactory().createModificationContext(issue);
final IXtextDocument xtextDocument = modificationContext.getXtextDocument();
xtextDocument.readOnly
(new IUnitOfWork.Void<XtextResource>()
{
@Override
public void process(XtextResource xtextResource) throws Exception
{
XcoreImportOrganizer xcoreImportOrganizer = xcoreImportOrganizerProvider.get();
final TextRegion importRegion = xcoreImportOrganizer.getImportRegion(xtextResource);
final String importSection = xcoreImportOrganizer.getOrganizedImportSection(xtextResource);
acceptor.accept
(issue,
"Organize all imports",
importSection.trim(),
"full/obj16/correction_change.gif",
new IModification()
{
public void apply(IModificationContext context) throws BadLocationException
{
IXtextDocument xtextDocument = context.getXtextDocument();
int offset = importRegion.getOffset();
int length = importRegion.getLength();
String string = xtextDocument.get(offset, length);
if (!string.equals(importSection))
{
xtextDocument.replace(offset, length, importSection);
}
}
});
}
});
}
}