Skip to content

Commit

Permalink
[513196] DOT Editor - Integrate Quickfixes for DOT Html-Like Labels.
Browse files Browse the repository at this point in the history
- Extend the DotQuickfixProvider to provide Quickfixes for the invalid
label validation issues. The DotHtmlLabelQuickfixDelegator provides a
bridge between the DotQuickfixProvider (host grammar) and the
DotHtmlLabelQuickfixProvider (sub-grammar) classes.
- Modify the DotHtmlLabelValidator to attach the necessary information
(issueCode, uriToProblem) to the issue needed by the quickfix.
- Implement corresponding DotQuickfixTest test cases.
  • Loading branch information
miklossy committed May 21, 2020
1 parent 20e9a61 commit 627c4df
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,76 @@ class DotQuickfixTest extends AbstractQuickfixTest {
'''graph{node[colorscheme=foo]}'''.testQuickfixesOn(DotAttributes.COLORSCHEME__GCNE, expectedQuickfixes)
}

@Test def node_html_like_label_001() {
'''
graph {
1[label = <<I>text</B>>]
}
'''.testQuickfixesOn(DotAttributes.LABEL__GCNE,
new Quickfix("Change the opening tag to 'B'.", "Change the opening tag from 'I' to 'B'.", '''
graph {
1[label = <<B>text</B>>]
}
'''),
new Quickfix("Change the closing tag to 'I'.", "Change the closing tag from 'B' to 'I'.", '''
graph {
1[label = <<I>text</I>>]
}
''')
)
}

@Test def node_html_like_label_002() {
'''
graph {
1[label = <<SUB>text</SUP>>]
}
'''.testQuickfixesOn(DotAttributes.LABEL__GCNE,
new Quickfix("Change the opening tag to 'SUP'.", "Change the opening tag from 'SUB' to 'SUP'.", '''
graph {
1[label = <<SUP>text</SUP>>]
}
'''),
new Quickfix("Change the closing tag to 'SUB'.", "Change the closing tag from 'SUP' to 'SUB'.", '''
graph {
1[label = <<SUB>text</SUB>>]
}
''')
)
}

@Test def node_html_like_label_003() {
'''
graph {
1[label = <
<FONT FOO=""></FONT>
>]
}
'''.testQuickfixesOn(DotAttributes.LABEL__GCNE,
new Quickfix("Change to 'COLOR'.", "Change 'FOO' to 'COLOR'.", '''
graph {
1[label = <
<FONT COLOR=""></FONT>
>]
}
'''),
new Quickfix("Change to 'FACE'.", "Change 'FOO' to 'FACE'.", '''
graph {
1[label = <
<FONT FACE=""></FONT>
>]
}
'''),
new Quickfix("Change to 'POINT-SIZE'.", "Change 'FOO' to 'POINT-SIZE'.", '''
graph {
1[label = <
<FONT POINT-SIZE=""></FONT>
>]
}
''')
)
}

@Test def node_shape_001() {
val List<Quickfix> expectedQuickfixes = newArrayList
for (validNodeShape : validNodeShapes) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*******************************************************************************
* Copyright (c) 2020 itemis AG 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
*
* Contributors:
* Tamas Miklossy (itemis AG) - initial API and implementation (bug #513196)
*******************************************************************************/
package org.eclipse.gef.dot.internal.ui.language.quickfix;

import java.io.IOException;
import java.util.List;

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.util.EcoreUtil;
import org.eclipse.gef.dot.internal.language.dot.Attribute;
import org.eclipse.gef.dot.internal.language.terminals.ID;
import org.eclipse.gef.dot.internal.ui.language.DotActivator;
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
import org.eclipse.xtext.resource.IResourceFactory;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;
import org.eclipse.xtext.ui.editor.model.XtextDocument;
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.IssueResolution;
import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionAcceptor;
import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionProvider;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.util.StringInputStream;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.validation.Issue;

import com.google.inject.Injector;

class DotHtmlLabelQuickfixDelegator {

private Injector injector;

public DotHtmlLabelQuickfixDelegator() {
injector = DotActivator.getInstance().getInjector(
DotActivator.ORG_ECLIPSE_GEF_DOT_INTERNAL_LANGUAGE_DOTHTMLLABEL);
}

public void provideQuickfixes(Issue originalIssue, Issue subgrammarIssue,
IssueResolutionAcceptor acceptor) {
List<IssueResolution> resolutions = getResolutions(subgrammarIssue);
for (IssueResolution issueResolution : resolutions) {
acceptor.accept(originalIssue, issueResolution.getLabel(),
issueResolution.getDescription(),
issueResolution.getImage(), new ISemanticModification() {

@Override
public void apply(EObject element,
IModificationContext context) throws Exception {
Attribute attribute = (Attribute) element;
String originalText = attribute.getValue()
.toValue();
String modifiedText = getModifiedText(originalText,
issueResolution);
attribute.setValue(ID.fromValue(modifiedText,
ID.Type.HTML_STRING));
}
});
}
}

private List<IssueResolution> getResolutions(Issue issue) {
IssueResolutionProvider quickfixProvider = injector
.getInstance(IssueResolutionProvider.class);
return quickfixProvider.getResolutions(issue);
}

private String getModifiedText(String originalText,
IssueResolution issueResolution) {
/*
* manually create an IModificationContext with an XtextDocument and
* call the apply method of the issueResolution with that
* IModificationContext
*/
IXtextDocument document = getDocument(originalText);
IModificationContext modificationContext = new IModificationContext() {

@Override
public IXtextDocument getXtextDocument() {
return document;
}

@Override
public IXtextDocument getXtextDocument(URI uri) {
return document;
}
};

new IssueResolution(issueResolution.getLabel(),
issueResolution.getDescription(), issueResolution.getImage(),
modificationContext, issueResolution.getModification(),
issueResolution.getRelevance()).apply();
return document.get();
}

/**
* The implementation of the following helper methods are taken from the
* org.eclipse.xtext.ui.testing.ContentAssistProcessorTestBuilder class.
*/
private IXtextDocument getDocument(String model) {
XtextResource xtextResource = getXtextResource(model);
XtextDocument document = injector.getInstance(XtextDocument.class);
document.set(model);
document.setInput(xtextResource);
return document;
}

private XtextResource getXtextResource(String model) {
StringInputStream in = new StringInputStream(
Strings.emptyIfNull(model));

// creating an in-memory EMF Resource
URI uri = URI.createURI(""); //$NON-NLS-1$
Resource resource = injector.getInstance(IResourceFactory.class)
.createResource(uri);
new XtextResourceSet().getResources().add(resource);
try {
resource.load(in, null);
} catch (IOException e) {
e.printStackTrace();
}
if (resource instanceof LazyLinkingResource) {
((LazyLinkingResource) resource)
.resolveLazyCrossReferences(CancelIndicator.NullImpl);
} else {
EcoreUtil.resolveAll(resource);
}
return (XtextResource) resource;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public class DotHtmlLabelQuickfixProvider
public void fixInvalidTagName(final Issue issue,
IssueResolutionAcceptor acceptor) {
String[] issueData = issue.getData();
String openingTagName = issueData[0];
String closingTagName = issueData[1];
String openingTagName = issueData[2];
String closingTagName = issueData[3];

// change the opening tag name
String label = "Change the opening tag to '" + closingTagName + "'."; //$NON-NLS-1$ //$NON-NLS-2$
Expand Down Expand Up @@ -82,8 +82,8 @@ public void apply(EObject element,
public void fixInvalidAttributeName(final Issue issue,
IssueResolutionAcceptor acceptor) {
String[] issueData = issue.getData();
String tagName = issueData[0];
String invalidAttribute = issueData[1];
String tagName = issueData[2];
String invalidAttribute = issueData[3];

Set<String> validAttributes = DotHtmlLabelHelper.getValidAttributes()
.get(tagName.toUpperCase());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.Arrays;
import java.util.List;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gef.dot.internal.DotAttributes;
Expand Down Expand Up @@ -150,6 +151,26 @@ public void fixDirAttributeValue(final Issue issue,
acceptor);
}

@Fix(DotAttributes.LABEL__GCNE)
public void fixHtmlLabelAttributeValue(final Issue issue,
IssueResolutionAcceptor acceptor) {
String[] data = issue.getData();
if (data == null || data.length < 4) {
return;
}

String htmlLabelIssueCode = data[0];
String htmlLabelUriToProblem = data[1];

Issue.IssueImpl htmlLabelIssue = new Issue.IssueImpl();
htmlLabelIssue.setCode(htmlLabelIssueCode);
htmlLabelIssue.setData(data);
htmlLabelIssue.setUriToProblem(URI.createURI(htmlLabelUriToProblem));

new DotHtmlLabelQuickfixDelegator().provideQuickfixes(issue,
htmlLabelIssue, acceptor);
}

@Fix(DotAttributes.LAYOUT__G)
public void fixLayoutAttributeValue(final Issue issue,
IssueResolutionAcceptor acceptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gef.dot.internal.language.htmllabel.DotHtmlLabelHelper;
import org.eclipse.gef.dot.internal.language.htmllabel.HtmlAttr;
import org.eclipse.gef.dot.internal.language.htmllabel.HtmlContent;
Expand Down Expand Up @@ -114,7 +115,9 @@ public void checkTagIsClosed(HtmlTag tag) {
+ tag.getName() + ">' but got '</"
+ tag.getCloseName() + ">').",
tag, HtmllabelPackage.Literals.HTML_TAG__CLOSE_NAME,
new String[] { tag.getName(), tag.getCloseName() });
new String[] { HTML_TAG_IS_NOT_PROPERLY_CLOSED,
EcoreUtil.getURI(tag).toString(), tag.getName(),
tag.getCloseName() });
}
}

Expand Down Expand Up @@ -240,7 +243,9 @@ public void checkAttributeNameIsValid(HtmlAttr attr) {
"Attribute '" + attrName + "' is not allowed inside '<"
+ tagName + ">'.",
attr, HtmllabelPackage.Literals.HTML_ATTR__NAME,
new String[] { tagName, attrName });
new String[] { HTML_ATTRIBUTE_INVALID_ATTRIBUTE_NAME,
EcoreUtil.getURI(attr).toString(),
tagName, attrName });
}
}
}
Expand Down

0 comments on commit 627c4df

Please sign in to comment.