/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 the original authors or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.sarl.lang.ui.quickfix; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.base.Predicate; import com.google.inject.Inject; import com.google.inject.Injector; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.jface.text.BadLocationException; import org.eclipse.xtend.core.jvmmodel.IXtendJvmAssociations; import org.eclipse.xtend.core.validation.IssueCodes; import org.eclipse.xtend.core.xtend.XtendMember; import org.eclipse.xtend.core.xtend.XtendPackage; import org.eclipse.xtend.core.xtend.XtendTypeDeclaration; import org.eclipse.xtend.ide.quickfix.XtendQuickfixProvider; import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.common.types.JvmOperation; import org.eclipse.xtext.common.types.util.AnnotationLookup; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.IQualifiedNameProvider; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.nodemodel.ILeafNode; 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.IModificationContext; import org.eclipse.xtext.ui.editor.quickfix.Fix; import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionAcceptor; import org.eclipse.xtext.ui.refactoring.impl.ProjectUtil; import org.eclipse.xtext.ui.resource.IResourceSetProvider; import org.eclipse.xtext.util.Arrays; import org.eclipse.xtext.util.Strings; import org.eclipse.xtext.validation.ConfigurableIssueCodesProvider; import org.eclipse.xtext.validation.Issue; import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices; import org.eclipse.xtext.xbase.ui.contentassist.ReplacingAppendable; import io.sarl.lang.actionprototype.IActionPrototypeProvider; import io.sarl.lang.annotation.DefaultValueUse; import io.sarl.lang.parser.SyntaxIssueCodes; import io.sarl.lang.sarl.SarlAction; import io.sarl.lang.sarl.SarlAgent; import io.sarl.lang.sarl.SarlBehavior; import io.sarl.lang.sarl.SarlCapacity; import io.sarl.lang.sarl.SarlScript; import io.sarl.lang.sarl.SarlSkill; import io.sarl.lang.services.SARLGrammarKeywordAccess; import io.sarl.lang.ui.quickfix.acceptors.ActionAddModification; import io.sarl.lang.ui.quickfix.acceptors.AnnotationRemoveModification; import io.sarl.lang.ui.quickfix.acceptors.BehaviorUnitGuardRemoveModification; import io.sarl.lang.ui.quickfix.acceptors.CapacityReferenceRemoveModification; import io.sarl.lang.ui.quickfix.acceptors.ExtendedTypeRemoveModification; import io.sarl.lang.ui.quickfix.acceptors.FiredEventRemoveModification; import io.sarl.lang.ui.quickfix.acceptors.ImplementedTypeRemoveModification; import io.sarl.lang.ui.quickfix.acceptors.ImplementedTypeRemoveModification.RemovalType; import io.sarl.lang.ui.quickfix.acceptors.MemberRemoveModification; import io.sarl.lang.ui.quickfix.acceptors.MemberRenameModification; import io.sarl.lang.ui.quickfix.acceptors.Messages; import io.sarl.lang.ui.quickfix.acceptors.MissedMethodAddModification; import io.sarl.lang.ui.quickfix.acceptors.MultiModification; import io.sarl.lang.ui.quickfix.acceptors.ProtectKeywordModification; import io.sarl.lang.ui.quickfix.acceptors.ReturnTypeAddModification; import io.sarl.lang.ui.quickfix.acceptors.ReturnTypeReplaceModification; import io.sarl.lang.ui.quickfix.acceptors.SuperTypeRemoveModification; import io.sarl.lang.ui.quickfix.acceptors.SuppressWarningsAddModification; /** * Custom quickfixes. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @see "https://www.eclipse.org/Xtext/documentation/304_ide_concepts.html#quick-fixes" */ @SuppressWarnings({"static-method", "checkstyle:methodcount", "checkstyle:classfanoutcomplexity"}) public class SARLQuickfixProvider extends XtendQuickfixProvider { @Inject private Injector injector; @Inject private ReplacingAppendable.Factory appendableFactory; @Inject private SARLGrammarKeywordAccess grammarAccess; @Inject private IXtendJvmAssociations associations; @Inject private CommonTypeComputationServices services; @Inject private IActionPrototypeProvider prototypeProvider; @Inject private IQualifiedNameProvider qualifiedNameProvider; @Inject private ConfigurableIssueCodesProvider issueCodesProvider; @Inject private ProjectUtil projectUtil; @Inject private IResourceSetProvider resourceSetProvider; @Inject private AnnotationLookup annotationFinder; /** Replies if the given code is for a ignorable warning. * * @param code the code of the warning. * @return <code>true</code> if the warning could be ignored, <code>false</code> otherwise. */ public boolean isIgnorable(String code) { return this.issueCodesProvider.getConfigurableIssueCodes().containsKey(code); } @Override protected Predicate<Method> getFixMethodPredicate(final String issueCode) { return new Predicate<Method>() { @Override public boolean apply(Method input) { final Fix annotation = input.getAnnotation(Fix.class); final boolean result = annotation != null && ("*".equals(annotation.value()) || issueCode.equals(annotation.value())) //$NON-NLS-1$ && input.getParameterTypes().length == 2 && Void.TYPE == input.getReturnType() && input.getParameterTypes()[0].isAssignableFrom(Issue.class) && input.getParameterTypes()[1].isAssignableFrom(IssueResolutionAcceptor.class); return result; } }; } /** Add the fixes with suppress-warning annotations. * * @param issue the issue. * @param acceptor the resolution acceptor. */ @Fix("*") public void fixSuppressWarnings(Issue issue, IssueResolutionAcceptor acceptor) { if (isIgnorable(issue.getCode())) { SuppressWarningsAddModification.accept(this, issue, acceptor); } } /** Replies the JVM operations that correspond to the given URIs. * * @param container the container of the operations. * @param operationUris the URIs. * @return the JVM operations. */ public List<JvmOperation> getJvmOperationsFromURIs(XtendTypeDeclaration container, String... operationUris) { // Collect the JvmOperation prior to any modification for ensuring that // URI are pointing the JvmOperations. final List<JvmOperation> operations = new ArrayList<>(); final ResourceSet resourceSet = container.eResource().getResourceSet(); for (final String operationUriAsString : operationUris) { final URI operationURI = URI.createURI(operationUriAsString); final EObject overridden = resourceSet.getEObject(operationURI, true); if (overridden instanceof JvmOperation) { final JvmOperation operation = (JvmOperation) overridden; if (this.annotationFinder.findAnnotation(operation, DefaultValueUse.class) == null) { operations.add(operation); } } } return operations; } /** Replies the injector used by this object. * * @return the injector. */ public Injector getInjector() { return this.injector; } /** Replies a provider of qualified name. * * @return the provider of qualified name. */ public IQualifiedNameProvider getQualifiedNameProvider() { return this.qualifiedNameProvider; } @Override public IQualifiedNameConverter getQualifiedNameConverter() { return super.getQualifiedNameConverter(); } /** Replies the provider of action prototypes. * * @return the action prototype provider. */ public IActionPrototypeProvider getActionPrototypeProvider() { return this.prototypeProvider; } /** Replies the factory for appendable. * * @return the appendable factory. */ public ReplacingAppendable.Factory getAppendableFactory() { return this.appendableFactory; } /** Replies the project utilities. * * @return the utilities. */ public ProjectUtil getProjectUtil() { return this.projectUtil; } /** Replies the resource set provider. * * @return the provider. */ public IResourceSetProvider getResourceSetProvider() { return this.resourceSetProvider; } /** Replies the type services. * * @return the type serices. */ public CommonTypeComputationServices getTypeServices() { return this.services; } /** Replies the JVM associations. * * @return the JVM associations. */ public IXtendJvmAssociations getJvmAssociations() { return this.associations; } /** Replies the SARL grammar accessor. * * @return the SARL grammar accessor. */ public SARLGrammarKeywordAccess getGrammarAccess() { return this.grammarAccess; } /** Remove the element related to the issue, and the whitespaces before the element until the given separator. * * @param issue - the issue. * @param document - the document. * @param separator - the separator to consider. * @return <code>true</code> if the separator was found, <code>false</code> if not. * @throws BadLocationException if there is a problem with the location of the element. */ public boolean removeToPreviousSeparator(Issue issue, IXtextDocument document, String separator) throws BadLocationException { return removeToPreviousSeparator(issue.getOffset(), issue.getLength(), document, separator); } /** Remove the portion of text, and the whitespaces before the text until the given separator. * * @param offset - the offset where to start to remove. * @param length - the length of the text to remove. * @param document - the document. * @param separator - the separator to consider. * @return <code>true</code> if the separator was found, <code>false</code> if not. * @throws BadLocationException if there is a problem with the location of the element. */ public boolean removeToPreviousSeparator(int offset, int length, IXtextDocument document, String separator) throws BadLocationException { // Skip spaces before the identifier until the separator int index = offset - 1; char c = document.getChar(index); while (Character.isWhitespace(c)) { index--; c = document.getChar(index); } // Test if it previous non-space character is the separator final boolean foundSeparator = document.getChar(index) == separator.charAt(0); if (foundSeparator) { index--; c = document.getChar(index); // Skip the previous spaces while (Character.isWhitespace(c)) { index--; c = document.getChar(index); } final int delta = offset - index - 1; document.replace(index + 1, length + delta, ""); //$NON-NLS-1$ } return foundSeparator; } /** Replies the index where import declaration could be inserted into the given container. * * @param script - the script to consider for the insertion * @return the insertion index. */ public int getImportInsertOffset(SarlScript script) { final ICompositeNode node = NodeModelUtils.findActualNodeFor(script.getImportSection()); if (node == null) { final List<INode> children = NodeModelUtils.findNodesForFeature(script, XtendPackage.eINSTANCE.getXtendFile_Package()); if (children.isEmpty()) { return 0; } return children.get(0).getEndOffset(); } return node.getEndOffset(); } /** Remove the element related to the issue, and the whitespaces after the element until the given separator. * * @param issue - the issue. * @param document - the document. * @param separator - the separator to consider. * @return <code>true</code> if the separator was found, <code>false</code> if not. * @throws BadLocationException if there is a problem with the location of the element. */ public boolean removeToNextSeparator(Issue issue, IXtextDocument document, String separator) throws BadLocationException { // Skip spaces after the identifier until the separator int index = issue.getOffset() + issue.getLength(); char c = document.getChar(index); while (Character.isWhitespace(c)) { index++; c = document.getChar(index); } // Test if it next non-space character is the separator final boolean foundSeparator = document.getChar(index) == separator.charAt(0); if (foundSeparator) { index++; c = document.getChar(index); // Skip the previous spaces while (Character.isWhitespace(c)) { index++; c = document.getChar(index); } final int newLength = index - issue.getOffset(); document.replace(issue.getOffset(), newLength, ""); //$NON-NLS-1$ } return foundSeparator; } /** Remove the element related to the issue, and the whitespaces before the element until one of the given * keywords is encountered. * * @param issue - the issue. * @param document - the document. * @param keyword1 - the first keyword to consider. * @param otherKeywords - other keywords. * @return <code>true</code> if one keyword was found, <code>false</code> if not. * @throws BadLocationException if there is a problem with the location of the element. */ public boolean removeToPreviousKeyword(Issue issue, IXtextDocument document, String keyword1, String... otherKeywords) throws BadLocationException { // Skip spaces before the element int index = issue.getOffset() - 1; char c = document.getChar(index); while (Character.isWhitespace(c)) { index--; c = document.getChar(index); } // Skip non-spaces before the identifier final StringBuffer kw = new StringBuffer(); while (!Character.isWhitespace(c)) { kw.insert(0, c); index--; c = document.getChar(index); } if (kw.toString().equals(keyword1) || Arrays.contains(otherKeywords, kw.toString())) { // Skip spaces before the previous keyword while (Character.isWhitespace(c)) { index--; c = document.getChar(index); } final int delta = issue.getOffset() - index - 1; document.replace(index + 1, issue.getLength() + delta, ""); //$NON-NLS-1$ return true; } return false; } /** Remove the element related to the issue, and the whitespaces before the element until the begin separator, * and the whitespaces after the element until the end separator. * * @param issue - the issue. * @param document - the document. * @param beginSeparator - the separator before the element. * @param endSeparator - the separator after the element. * @return <code>true</code> if the separator was found, <code>false</code> if not. * @throws BadLocationException if there is a problem with the location of the element. */ public boolean removeBetweenSeparators(Issue issue, IXtextDocument document, String beginSeparator, String endSeparator) throws BadLocationException { int offset = issue.getOffset(); int length = issue.getLength(); // Skip spaces before the identifier until the separator int index = offset - 1; char c = document.getChar(index); while (Character.isWhitespace(c)) { index--; c = document.getChar(index); } // Test if it previous non-space character is the separator boolean foundSeparator = document.getChar(index) == beginSeparator.charAt(0); if (foundSeparator) { index--; c = document.getChar(index); // Skip the previous spaces while (Character.isWhitespace(c)) { index--; c = document.getChar(index); } length = length + (offset - index - 1); offset = index + 1; // Skip spaces after the identifier until the separator index = offset + length; c = document.getChar(index); while (Character.isWhitespace(c)) { index++; c = document.getChar(index); } // Test if it next non-space character is the separator foundSeparator = document.getChar(index) == endSeparator.charAt(0); if (foundSeparator) { index++; length = index - offset; document.replace(offset, length, ""); //$NON-NLS-1$ } } return foundSeparator; } /** Replies the index where elements could be inserted into the given container. * * @param container - the container to consider for the insertion * @return the insertion index. */ public int getInsertOffset(XtendTypeDeclaration container) { if (container.getMembers().isEmpty()) { final ICompositeNode node = NodeModelUtils.findActualNodeFor(container); final ILeafNode openingBraceNode = IterableExtensions.findFirst(node.getLeafNodes(), (lnode) -> "{".equals(lnode.getText())); //$NON-NLS-1$ if (openingBraceNode != null) { return openingBraceNode.getOffset() + 1; } return node.getEndOffset(); } final EObject lastFeature = IterableExtensions.last(container.getMembers()); final ICompositeNode node = NodeModelUtils.findActualNodeFor(lastFeature); return node.getEndOffset(); } /** Replies the size of a sequence of whitespaces. * * @param document - the document. * @param offset - the offset of the first character of the sequence. * @return the number of whitespaces at the given offset. * @throws BadLocationException if there is a problem with the location of the element. */ public int getSpaceSize(IXtextDocument document, int offset) throws BadLocationException { int size = 0; char c = document.getChar(offset + size); while (Character.isWhitespace(c)) { size++; c = document.getChar(offset + size); } return size; } /** Replies the offset that corresponds to the given regular expression pattern. * * @param document the document to parse. * @param startOffset the offset in the text at which the pattern must be recognized. * @param pattern the regular expression pattern. * @return the offset (greater or equal to the startOffset), or <code>-1</code> if the pattern * cannot be recognized. */ public int getOffsetForPattern(IXtextDocument document, int startOffset, String pattern) { final Pattern compiledPattern = Pattern.compile(pattern); final Matcher matcher = compiledPattern.matcher(document.get()); if (matcher.find(startOffset)) { final int end = matcher.end(); return end; } return -1; } /** Replies the qualified name for the given name. * * @param name - the name. * @return the qualified name. */ public QualifiedName qualifiedName(String name) { if (!com.google.common.base.Strings.isNullOrEmpty(name)) { final List<String> segments = Strings.split(name, "."); //$NON-NLS-1$ return QualifiedName.create(segments); } return QualifiedName.create(); } /** Remove the exectuable feature. * * @param element - the executable feature to remove. * @param context - the context of the change. * @throws BadLocationException if there is a problem with the location of the element. */ public void removeExecutableFeature(EObject element, IModificationContext context) throws BadLocationException { final ICompositeNode node; final SarlAction action = EcoreUtil2.getContainerOfType(element, SarlAction.class); if (action == null) { final XtendMember feature = EcoreUtil2.getContainerOfType(element, XtendMember.class); node = NodeModelUtils.findActualNodeFor(feature); } else { node = NodeModelUtils.findActualNodeFor(action); } if (node != null) { remove(context.getXtextDocument(), node); } } @Override public <T extends EObject> void remove(EObject element, Class<T> type, IModificationContext context) throws BadLocationException { // Make the function visible super.remove(element, type, context); } /** Remove the given node and the whitespaces before/after. * * @param <T> the type of element to remove (must be for the given element or one of its container). * @param element the source of the change. * @param type the type of element to remove (must be for the given element or one of its container). * @param context the modification context. * @throws BadLocationException if the location cannot be computed properly. */ public <T extends EObject> void removeIncludingWhiteSpaces(EObject element, Class<T> type, IModificationContext context) throws BadLocationException { // Search the node final T container = EcoreUtil2.getContainerOfType(element, type); if (container == null) { return; } final ICompositeNode node = NodeModelUtils.findActualNodeFor(container); if (node == null) { return; } // Compute region for the node int offset = node.getOffset(); int length = node.getLength(); if (node.hasPreviousSibling()) { final INode previousSibling = node.getPreviousSibling(); final int endOffset = previousSibling.getEndOffset(); length = length + (offset - endOffset); offset = endOffset; } final IXtextDocument document = context.getXtextDocument(); // Include spaces in the region final int docEndOffset = document.getLength(); int endOffset = offset + length; while (offset > 0 && isBasicSpace(document.getChar(offset - 1))) { --offset; ++length; } while (endOffset < docEndOffset && isBasicSpace(document.getChar(endOffset))) { ++endOffset; ++length; } document.replace(offset, length, ""); //$NON-NLS-1$ } /** Replies if the gien character is a simple space character or a tabulation character. * Line feeds and other white space characters that are supported by {@link Character#isWhitespace(char)} * are not considered as basic space by the current function. * * @param character the character to test. * @return {@code true} if the given character is a basic space or a tabulation character. */ public boolean isBasicSpace(char character) { return character == ' ' || character == '\t'; } /** Quick fix for "Duplicate type". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(IssueCodes.DUPLICATE_TYPE_NAME) public void fixDuplicateTopElements(Issue issue, IssueResolutionAcceptor acceptor) { MemberRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Duplicate field". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(IssueCodes.DUPLICATE_FIELD) public void fixDuplicateAttribute(Issue issue, IssueResolutionAcceptor acceptor) { MemberRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Duplicate method". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(IssueCodes.DUPLICATE_METHOD) public void fixDuplicateMethod(Issue issue, IssueResolutionAcceptor acceptor) { MemberRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "disallowed variable name". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(org.eclipse.xtext.xbase.validation.IssueCodes.VARIABLE_NAME_DISALLOWED) public void fixDisallowedFieldName(final Issue issue, IssueResolutionAcceptor acceptor) { MemberRenameModification.accept(this, issue, acceptor); MemberRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "disallowed variable name". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(org.eclipse.xtext.xbase.validation.IssueCodes.VARIABLE_NAME_DISCOURAGED) public void fixDiscouragedFieldName(final Issue issue, IssueResolutionAcceptor acceptor) { MemberRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Invalid member name". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(IssueCodes.INVALID_MEMBER_NAME) public void fixMemberName(final Issue issue, IssueResolutionAcceptor acceptor) { MemberRenameModification.accept(this, issue, acceptor); MemberRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Redundant interface implementation". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.REDUNDANT_INTERFACE_IMPLEMENTATION) public void fixRedundantInterface(final Issue issue, IssueResolutionAcceptor acceptor) { ImplementedTypeRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Variable name shadowing". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(org.eclipse.xtext.xbase.validation.IssueCodes.VARIABLE_NAME_SHADOWING) public void fixVariableNameShadowing(final Issue issue, IssueResolutionAcceptor acceptor) { MemberRemoveModification.accept(this, issue, acceptor); MemberRenameModification.accept(this, issue, acceptor); } /** Quick fix for "Override final operation". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(IssueCodes.OVERRIDDEN_FINAL) public void fixOverriddenFinal(Issue issue, IssueResolutionAcceptor acceptor) { final MultiModification modifications = new MultiModification( this, issue, acceptor, Messages.SARLQuickfixProvider_0, Messages.SARLQuickfixProvider_1); modifications.bind(XtendTypeDeclaration.class, SuperTypeRemoveModification.class); modifications.bind(XtendMember.class, MemberRemoveModification.class); } /** Quick fix for "Discouraged boolean expression". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.DISCOURAGED_BOOLEAN_EXPRESSION) public void fixDiscouragedBooleanExpression(final Issue issue, IssueResolutionAcceptor acceptor) { BehaviorUnitGuardRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Unreachable behavior unit". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.UNREACHABLE_BEHAVIOR_UNIT) public void fixUnreachableBehaviorUnit(Issue issue, IssueResolutionAcceptor acceptor) { MemberRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Invalid capacity type". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.INVALID_CAPACITY_TYPE) public void fixInvalidCapacityType(final Issue issue, IssueResolutionAcceptor acceptor) { CapacityReferenceRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Invalid firing event type". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.INVALID_FIRING_EVENT_TYPE) public void fixInvalidFiringEventType(final Issue issue, IssueResolutionAcceptor acceptor) { FiredEventRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Invalid implemented type". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.INVALID_IMPLEMENTED_TYPE) public void fixInvalidImplementedType(final Issue issue, IssueResolutionAcceptor acceptor) { ImplementedTypeRemoveModification.accept(this, issue, acceptor, RemovalType.OTHER); } /** Quick fix for "Invalid extended type". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.INVALID_EXTENDED_TYPE) public void fixInvalidExtendedType(final Issue issue, IssueResolutionAcceptor acceptor) { ExtendedTypeRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Cyclic hierarchy". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(IssueCodes.CYCLIC_INHERITANCE) public void fixCyclicInheritance(final Issue issue, IssueResolutionAcceptor acceptor) { ExtendedTypeRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Interface expected". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(IssueCodes.INTERFACE_EXPECTED) public void fixInteraceExpected(final Issue issue, IssueResolutionAcceptor acceptor) { ExtendedTypeRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Class expected". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(IssueCodes.CLASS_EXPECTED) public void fixClassExpected(final Issue issue, IssueResolutionAcceptor acceptor) { ExtendedTypeRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Discouraged capacity definition". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.DISCOURAGED_CAPACITY_DEFINITION) public void fixDiscouragedCapacityDefinition(Issue issue, IssueResolutionAcceptor acceptor) { MemberRemoveModification.accept(this, issue, acceptor, SarlCapacity.class); ActionAddModification.accept(this, issue, acceptor); } /** Quick fix for "Incompatible return type". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(org.eclipse.xtext.xbase.validation.IssueCodes.INCOMPATIBLE_RETURN_TYPE) public void fixIncompatibleReturnType(final Issue issue, IssueResolutionAcceptor acceptor) { ReturnTypeReplaceModification.accept(this, issue, acceptor); } /** Quick fix for "Return type is recommended". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.RETURN_TYPE_SPECIFICATION_IS_RECOMMENDED) public void fixReturnTypeRecommended(final Issue issue, IssueResolutionAcceptor acceptor) { ReturnTypeAddModification.accept(this, issue, acceptor); } /** Quick fix for "Unused agent capacity". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.UNUSED_AGENT_CAPACITY) public void fixUnusedAgentCapacity(final Issue issue, IssueResolutionAcceptor acceptor) { CapacityReferenceRemoveModification.accept(this, issue, acceptor); } /** Quick fix for "Redundant capacity use". * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.REDUNDANT_CAPACITY_USE) public void fixRedundantAgentCapacityUse(final Issue issue, IssueResolutionAcceptor acceptor) { CapacityReferenceRemoveModification.accept(this, issue, acceptor); } @Override protected void doOverrideMethods(Issue issue, IssueResolutionAcceptor acceptor, String label, String[] operationUris) { MissedMethodAddModification.accept(this, issue, acceptor, label, operationUris); } @Override protected void internalDoAddAbstractKeyword(EObject element, IModificationContext context) throws BadLocationException { EObject container = element; if (element instanceof SarlAction) { container = element.eContainer(); } XtendTypeDeclaration declaration = null; String keyword = null; if (container instanceof SarlAgent) { declaration = (XtendTypeDeclaration) container; keyword = getGrammarAccess().getAgentKeyword(); } else if (container instanceof SarlBehavior) { declaration = (XtendTypeDeclaration) container; keyword = getGrammarAccess().getBehaviorKeyword(); } else if (container instanceof SarlSkill) { declaration = (XtendTypeDeclaration) container; keyword = getGrammarAccess().getSkillKeyword(); } if (declaration != null && keyword != null) { final IXtextDocument document = context.getXtextDocument(); addAbstractKeyword(declaration, document, keyword); } else { super.internalDoAddAbstractKeyword(container, context); } } private void addAbstractKeyword(XtendTypeDeclaration typeDeclaration, IXtextDocument document, String declarationKeyword) throws BadLocationException { final ICompositeNode clazzNode = NodeModelUtils.findActualNodeFor(typeDeclaration); if (clazzNode == null) { throw new IllegalStateException("Cannot determine node for the type declaration" //$NON-NLS-1$ + typeDeclaration.getName()); } int offset = -1; final Iterator<ILeafNode> nodes = clazzNode.getLeafNodes().iterator(); while (offset == -1 && nodes.hasNext()) { final ILeafNode leafNode = nodes.next(); if (leafNode.getText().equals(declarationKeyword)) { offset = leafNode.getOffset(); } } final ReplacingAppendable appendable = this.appendableFactory.create(document, (XtextResource) typeDeclaration.eResource(), offset, 0); appendable.append(getGrammarAccess() .getAbstractKeyword()) .append(" "); //$NON-NLS-1$ appendable.commitChanges(); } /** Quick fix for the no viable alternative at an input that is a SARL keyword. * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(SyntaxIssueCodes.USED_RESERVED_KEYWORD) public void fixNoViableAlternativeAtKeyword(final Issue issue, IssueResolutionAcceptor acceptor) { ProtectKeywordModification.accept(this, issue, acceptor); } /** Quick fix for the discouraged annotation uses. * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.USED_RESERVED_SARL_ANNOTATION) public void fixDiscouragedAnnotationUse(final Issue issue, IssueResolutionAcceptor acceptor) { AnnotationRemoveModification.accept(this, issue, acceptor); } /** Quick fix for the manual definition of inline statements. * * @param issue - the issue. * @param acceptor - the quick fix acceptor. */ @Fix(io.sarl.lang.validation.IssueCodes.MANUAL_INLINE_DEFINITION) public void fixManualInlineDefinition(final Issue issue, IssueResolutionAcceptor acceptor) { AnnotationRemoveModification.accept(this, issue, acceptor); } }