| /* |
| * Copyright (c) 2011 Google Inc. |
| * |
| * 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 com.google.eclipse.protobuf.ui.quickfix; |
| |
| import static com.google.eclipse.protobuf.grammar.Syntaxes.proto2; |
| import static com.google.eclipse.protobuf.grammar.Syntaxes.proto3; |
| import static com.google.eclipse.protobuf.protobuf.BOOL.FALSE; |
| import static com.google.eclipse.protobuf.protobuf.BOOL.TRUE; |
| import static com.google.eclipse.protobuf.ui.quickfix.Messages.changeValueDescription; |
| import static com.google.eclipse.protobuf.ui.quickfix.Messages.changeValueLabel; |
| import static com.google.eclipse.protobuf.ui.quickfix.Messages.regenerateTagNumberDescription; |
| import static com.google.eclipse.protobuf.ui.quickfix.Messages.regenerateTagNumberLabel; |
| import static com.google.eclipse.protobuf.ui.quickfix.Messages.removeDuplicatePackageLabel; |
| import static com.google.eclipse.protobuf.util.Strings.quote; |
| import static com.google.eclipse.protobuf.validation.DataTypeValidator.EXPECTED_BOOL_ERROR; |
| import static com.google.eclipse.protobuf.validation.DataTypeValidator.EXPECTED_STRING_ERROR; |
| import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.INVALID_FIELD_TAG_NUMBER_ERROR; |
| import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.MISSING_MODIFIER_ERROR; |
| import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.MORE_THAN_ONE_PACKAGE_ERROR; |
| import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.REQUIRED_IN_PROTO3_ERROR; |
| import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.SYNTAX_IS_NOT_KNOWN_ERROR; |
| import static org.eclipse.emf.ecore.util.EcoreUtil.remove; |
| import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findActualNodeFor; |
| |
| import com.google.eclipse.protobuf.grammar.CommonKeyword; |
| import com.google.eclipse.protobuf.model.util.INodes; |
| import com.google.eclipse.protobuf.model.util.IndexedElements; |
| import com.google.eclipse.protobuf.naming.NameResolver; |
| import com.google.eclipse.protobuf.protobuf.BOOL; |
| import com.google.eclipse.protobuf.protobuf.BooleanLink; |
| import com.google.eclipse.protobuf.protobuf.FieldOption; |
| import com.google.eclipse.protobuf.protobuf.IndexedElement; |
| import com.google.eclipse.protobuf.protobuf.MessageField; |
| import com.google.eclipse.protobuf.protobuf.ModifierEnum; |
| import com.google.eclipse.protobuf.protobuf.Package; |
| import com.google.eclipse.protobuf.protobuf.ProtobufFactory; |
| import com.google.eclipse.protobuf.protobuf.StringLink; |
| import com.google.eclipse.protobuf.protobuf.Syntax; |
| import com.google.eclipse.protobuf.protobuf.Value; |
| import com.google.eclipse.protobuf.validation.ProtobufJavaValidator; |
| import com.google.inject.Inject; |
| |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.xtext.nodemodel.INode; |
| 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.model.edit.ISemanticModification; |
| 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.concurrent.IUnitOfWork; |
| import org.eclipse.xtext.validation.Issue; |
| |
| /** |
| * @author alruiz@google.com (Alex Ruiz) |
| */ |
| public class ProtobufQuickfixProvider extends DefaultQuickfixProvider { |
| private static final String ICON_FOR_CHANGE = "change.gif"; |
| |
| @Inject private IndexedElements indexedElements; |
| @Inject private NameResolver nameResolver; |
| @Inject private INodes nodes; |
| |
| @Fix(SYNTAX_IS_NOT_KNOWN_ERROR) |
| public void changeSyntaxToProto2(Issue issue, IssueResolutionAcceptor acceptor) { |
| ISemanticModification modification = new ISemanticModification() { |
| @Override public void apply(EObject element, IModificationContext context) throws Exception { |
| Syntax syntax = (Syntax) element; |
| syntax.setName(proto2()); |
| } |
| }; |
| String description = String.format(changeValueDescription, "syntax", quote(proto2())); |
| String label = String.format(changeValueLabel, proto2()); |
| acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification); |
| } |
| |
| @Fix(SYNTAX_IS_NOT_KNOWN_ERROR) |
| public void changeSyntaxToProto3(Issue issue, IssueResolutionAcceptor acceptor) { |
| ISemanticModification modification = new ISemanticModification() { |
| @Override public void apply(EObject element, IModificationContext context) throws Exception { |
| Syntax syntax = (Syntax) element; |
| syntax.setName(proto3()); |
| } |
| }; |
| String description = String.format(changeValueDescription, "syntax", quote(proto3())); |
| String label = String.format(changeValueLabel, proto3()); |
| acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification); |
| } |
| |
| @Fix(INVALID_FIELD_TAG_NUMBER_ERROR) |
| public void regenerateTagNumber(Issue issue, IssueResolutionAcceptor acceptor) { |
| ISemanticModification modification = new ISemanticModification() { |
| @Override public void apply(EObject element, IModificationContext context) throws Exception { |
| IndexedElement e = (IndexedElement) element; |
| long tagNumber = indexedElements.calculateNewIndexFor(e); |
| indexedElements.setIndexTo(e, tagNumber); |
| } |
| }; |
| acceptor.accept(issue, regenerateTagNumberLabel, regenerateTagNumberDescription, "field.gif", modification); |
| } |
| |
| @Fix(MORE_THAN_ONE_PACKAGE_ERROR) |
| public void removeDuplicatePackage(Issue issue, IssueResolutionAcceptor acceptor) { |
| final Package aPackage = element(issue, Package.class); |
| if (aPackage == null) { |
| return; |
| } |
| ISemanticModification modification = new ISemanticModification() { |
| @Override public void apply(EObject element, IModificationContext context) throws Exception { |
| if (element == aPackage) { |
| remove(aPackage); |
| } |
| } |
| }; |
| INode node = findActualNodeFor(aPackage); |
| String description = nodes.textOf(node); |
| acceptor.accept(issue, removeDuplicatePackageLabel, description, "remove.gif", modification); |
| } |
| |
| @Fix(MISSING_MODIFIER_ERROR) |
| public void changeModifierToRequired(Issue issue, IssueResolutionAcceptor acceptor) { |
| ISemanticModification modification = new ISemanticModification() { |
| @Override |
| public void apply(EObject element, IModificationContext context) throws Exception { |
| MessageField field = (MessageField) element; |
| field.setModifier(ModifierEnum.REQUIRED); |
| } |
| }; |
| String description = String.format(changeValueDescription, "modifier", "required"); |
| String label = String.format(changeValueLabel, "required"); |
| acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification); |
| } |
| |
| @Fix(MISSING_MODIFIER_ERROR) |
| public void changeModifierToRepeated(Issue issue, IssueResolutionAcceptor acceptor) { |
| ISemanticModification modification = new ISemanticModification() { |
| @Override |
| public void apply(EObject element, IModificationContext context) throws Exception { |
| MessageField field = (MessageField) element; |
| field.setModifier(ModifierEnum.REPEATED); |
| } |
| }; |
| String description = String.format(changeValueDescription, "modifier", "repeated"); |
| String label = String.format(changeValueLabel, "repeated"); |
| acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification); |
| } |
| |
| @Fix(MISSING_MODIFIER_ERROR) |
| public void changeModifierToOptional(Issue issue, IssueResolutionAcceptor acceptor) { |
| ISemanticModification modification = new ISemanticModification() { |
| @Override |
| public void apply(EObject element, IModificationContext context) throws Exception { |
| MessageField field = (MessageField) element; |
| field.setModifier(ModifierEnum.OPTIONAL); |
| } |
| }; |
| String description = String.format(changeValueDescription, "modifier", "optional"); |
| String label = String.format(changeValueLabel, "optional"); |
| acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification); |
| } |
| |
| @Fix(REQUIRED_IN_PROTO3_ERROR) |
| public void changeModifierToOptionalOnRequired(Issue issue, IssueResolutionAcceptor acceptor) { |
| ISemanticModification modification = new ISemanticModification() { |
| @Override |
| public void apply(EObject element, IModificationContext context) throws Exception { |
| MessageField field = (MessageField) element; |
| field.setModifier(ModifierEnum.OPTIONAL); |
| } |
| }; |
| String description = String.format(changeValueDescription, "modifier", "optional"); |
| String label = String.format(changeValueLabel, "optional"); |
| acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification); |
| } |
| |
| @Fix(EXPECTED_BOOL_ERROR) |
| public void changeValueToTrue(Issue issue, IssueResolutionAcceptor acceptor) { |
| EObject element = elementIn(issue); |
| if (element instanceof FieldOption) { |
| FieldOption option = (FieldOption) element; |
| changeValue(option, linkTo(TRUE), CommonKeyword.TRUE, issue, acceptor); |
| } |
| } |
| |
| @Fix(EXPECTED_BOOL_ERROR) |
| public void changeValueToFalse(Issue issue, IssueResolutionAcceptor acceptor) { |
| EObject element = elementIn(issue); |
| if (element instanceof FieldOption) { |
| FieldOption option = (FieldOption) element; |
| changeValue(option, linkTo(FALSE), CommonKeyword.FALSE, issue, acceptor); |
| } |
| } |
| |
| // TODO rename BooleanLink to BoolLink |
| private BooleanLink linkTo(BOOL value) { |
| BooleanLink link = ProtobufFactory.eINSTANCE.createBooleanLink(); |
| link.setTarget(value); |
| return link; |
| } |
| |
| @Fix(EXPECTED_STRING_ERROR) |
| public void changeValueToEmptyString(Issue issue, IssueResolutionAcceptor acceptor) { |
| EObject element = elementIn(issue); |
| if (element instanceof FieldOption) { |
| FieldOption option = (FieldOption) element; |
| String valueToPropose = ""; |
| changeValue(option, linkTo(valueToPropose), valueToPropose, issue, acceptor); |
| } |
| } |
| |
| private StringLink linkTo(String value) { |
| StringLink link = ProtobufFactory.eINSTANCE.createStringLink(); |
| link.setTarget(value); |
| return link; |
| } |
| |
| private void changeValue(final FieldOption option, final Value newValue, Object proposedValue, Issue issue, |
| IssueResolutionAcceptor acceptor) { |
| ISemanticModification modification = new ISemanticModification() { |
| @Override public void apply(EObject element, IModificationContext context) throws Exception { |
| option.setValue(newValue); |
| } |
| }; |
| String name = nameResolver.nameOf(option); |
| String description = String.format(changeValueDescription, name, proposedValue); |
| String label = String.format(changeValueLabel, proposedValue); |
| acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification); |
| } |
| |
| private EObject elementIn(Issue issue) { |
| return element(issue, EObject.class); |
| } |
| |
| private <T extends EObject> T element(final Issue issue, final Class<T> type) { |
| IModificationContext modificationContext = getModificationContextFactory().createModificationContext(issue); |
| IXtextDocument xtextDocument = modificationContext.getXtextDocument(); |
| return xtextDocument.readOnly(new IUnitOfWork<T, XtextResource>() { |
| @Override public T exec(XtextResource state) throws Exception { |
| EObject e = state.getEObject(issue.getUriToProblem().fragment()); |
| return (type.isInstance(e)) ? type.cast(e) : null; |
| } |
| }); |
| } |
| } |