Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ST partitioners to extract body from algorithms and methods #899

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.eclipse.fordiac.ide.model.dataexport.CommonElementExporter;
import org.eclipse.fordiac.ide.model.libraryElement.ICallable;
import org.eclipse.fordiac.ide.model.libraryElement.LibraryElementFactory;
import org.eclipse.fordiac.ide.model.libraryElement.STAlgorithm;
Expand Down Expand Up @@ -82,7 +82,6 @@ void testCombineLegacy() {
ALGORITHM REQ
// content
END_ALGORITHM

""";
fbType.getCallables().add(createSTAlgorithm("REQ", "// content"));
assertEquals(text, partitioner.combine(fbType));
Expand All @@ -101,7 +100,7 @@ void testCombineMethod() {
END_METHOD
""";
fbType.getCallables().add(createSTMethod("TEST", method));
assertEquals(algorithm + method, partitioner.combine(fbType));
assertEquals(algorithm + CommonElementExporter.LINE_END + method, partitioner.combine(fbType));
}

@Test
Expand All @@ -112,12 +111,34 @@ void testPartition() throws Exception {
ALGORITHM REQ
END_ALGORITHM
""";
assertCallablesEquals(List.of(algorithm), partition(algorithm));
assertCallablesEquals(List.of(""), partition(algorithm));
final String method = """
METHOD TEST
END_METHOD
""";
assertCallablesEquals(List.of(algorithm.trim(), '\n' + method), partition(algorithm + method));
assertCallablesEquals(List.of("", ""), partition(algorithm + method));
}

@Test
void testPartitionComment() throws Exception {
final String text = """
/*
* test comment 1
*/
ALGORITHM REQ
// inner comment 1
END_ALGORITHM

// outer comment

/*
* test comment 2
*/
METHOD TEST
// inner comment 2
END_METHOD
""";
assertCallablesEquals(List.of("// inner comment 1", "// outer comment", "// inner comment 2"), partition(text));
}

private static SimpleFBType createSimpleFBType() {
Expand Down Expand Up @@ -148,14 +169,12 @@ private STAlgorithmPartition partition(final String text) throws Exception {
assertTrue(partition.isPresent());
assertTrue(partition.get() instanceof STAlgorithmPartition);
assertEquals(text, partition.get().getOriginalSource());
assertEquals(text, ((STAlgorithmPartition) partition.get()).getCallables().stream()
.map(STAlgorithmPartitionerTest::getText).collect(Collectors.joining()));
return (STAlgorithmPartition) partition.get();
}

private static void assertCallablesEquals(final List<String> expected, final STAlgorithmPartition actual) {
assertIterableEquals(expected,
actual.getCallables().stream().map(STAlgorithmPartitionerTest::getText).toList());
assertIterableEquals(expected.stream().map(String::trim).toList(),
actual.getCallables().stream().map(STAlgorithmPartitionerTest::getText).map(String::trim).toList());
}

protected static String getText(final ICallable callable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@
*/
package org.eclipse.fordiac.ide.structuredtextalgorithm.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

Expand All @@ -33,13 +31,12 @@
import org.eclipse.fordiac.ide.structuredtextcore.stcore.STVarDeclaration;
import org.eclipse.fordiac.ide.structuredtextcore.util.STCorePartition;
import org.eclipse.fordiac.ide.structuredtextcore.util.STRecoveringPartitioner;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResource;

import com.google.inject.Inject;

public class STAlgorithmPartitioner extends STRecoveringPartitioner<ICallable> {
public class STAlgorithmPartitioner extends STRecoveringPartitioner<STAlgorithmSourceElement, ICallable> {

@Inject
private STAlgorithmGrammarAccess grammarAccess;
Expand All @@ -57,7 +54,8 @@ protected String combine(final BaseFBType baseFBType) {
}

protected String combine(final List<? extends ICallable> callables) {
return callables.stream().map(this::toSTText).collect(Collectors.joining());
return callables.stream().map(this::toSTText).collect(Collectors.joining(
CommonElementExporter.LINE_END + CommonElementExporter.LINE_END, "", CommonElementExporter.LINE_END)); //$NON-NLS-1$
}

protected String toSTText(final ICallable callable) {
Expand All @@ -72,26 +70,44 @@ protected String toSTText(final STAlgorithm algorithm) {
final String text = algorithm.getText();
if (text.contains(grammarAccess.getSTAlgorithmAccess().getALGORITHMKeyword_0().getValue())
|| text.contains(grammarAccess.getSTAlgorithmAccess().getEND_ALGORITHMKeyword_3().getValue())) {
return text;
return text.trim();
}
return generateAlgorithmDefinition(algorithm);
}

protected static String generateAlgorithmDefinition(final STAlgorithm algorithm) {
final StringBuilder builder = new StringBuilder();
appendBlockComment(algorithm, builder);
builder.append("ALGORITHM "); //$NON-NLS-1$
builder.append(algorithm.getName());
builder.append(CommonElementExporter.LINE_END);
builder.append(algorithm.getText());
builder.append(CommonElementExporter.LINE_END);
appendText(algorithm.getText(), builder);
builder.append("END_ALGORITHM"); //$NON-NLS-1$
builder.append(CommonElementExporter.LINE_END);
builder.append(CommonElementExporter.LINE_END);
return builder.toString();
}

protected static String toSTText(final STMethod method) {
return method.getText();
protected String toSTText(final STMethod method) {
final String name = method.getName();
final String text = method.getText();
if ((name != null && name.startsWith(LOST_AND_FOUND_NAME))
|| text.contains(grammarAccess.getSTMethodAccess().getMETHODKeyword_0().getValue())
|| text.contains(grammarAccess.getSTMethodAccess().getEND_METHODKeyword_4().getValue())) {
return text.trim();
}
return generateMethodDefinition(method);
}

protected static String generateMethodDefinition(final STMethod method) {
final StringBuilder builder = new StringBuilder();
appendBlockComment(method, builder);
builder.append("METHOD "); //$NON-NLS-1$
builder.append(method.getName());
if (method.getReturnType() != null) {
builder.append(" : "); //$NON-NLS-1$
builder.append(method.getReturnType().getName());
}
appendText(method.getText(), builder);
builder.append("END_METHOD"); //$NON-NLS-1$
return builder.toString();
}

@Override
Expand All @@ -108,17 +124,15 @@ public Optional<STCorePartition> partition(final XtextResource resource) {

public Optional<STCorePartition> partition(final STAlgorithmSource source) {
try {
final ICompositeNode node = NodeModelUtils.getNode(source);
final List<ICallable> result = source.getElements().stream().map(this::convertSourceElement)
.filter(Objects::nonNull).collect(Collectors.toCollection(ArrayList::new));
handleLostAndFound(node.getRootNode(), source.getElements(), result);
handleDuplicates(result);
final var node = NodeModelUtils.getNode(source);
final var result = convertSourceElements(node.getRootNode(), source.getElements());
return Optional.of(new STAlgorithmPartition(null, Collections.emptyList(), node.getText(), result));
} catch (final Exception e) {
return emergencyPartition(source);
}
}

@Override
protected ICallable convertSourceElement(final STAlgorithmSourceElement element) {
return switch (element) {
case final org.eclipse.fordiac.ide.structuredtextalgorithm.stalgorithm.STAlgorithm algorithm ->
Expand All @@ -131,7 +145,7 @@ protected ICallable convertSourceElement(final STAlgorithmSourceElement element)

protected ICallable convertSourceElement(
final org.eclipse.fordiac.ide.structuredtextalgorithm.stalgorithm.STAlgorithm algorithm) {
final var node = NodeModelUtils.findActualNodeFor(algorithm);
final var node = NodeModelUtils.getNode(algorithm.getBody());
if (node == null || algorithm.getName() == null) {
return null;
}
Expand All @@ -141,13 +155,13 @@ protected ICallable convertSourceElement(
if (comment != null) {
result.setComment(comment);
}
result.setText(node.getText());
result.setText(getTotalText(node));
return result;
}

protected org.eclipse.fordiac.ide.model.libraryElement.STMethod convertSourceElement(
protected ICallable convertSourceElement(
final org.eclipse.fordiac.ide.structuredtextalgorithm.stalgorithm.STMethod method) {
final var node = NodeModelUtils.findActualNodeFor(method);
final var node = NodeModelUtils.getNode(method.getBody());
if (node == null || method.getName() == null) {
return null;
}
Expand All @@ -167,7 +181,7 @@ protected org.eclipse.fordiac.ide.model.libraryElement.STMethod convertSourceEle
.filter(STAlgorithmPartitioner::isValidParameter).map(this::convertInOutParameter)
.forEachOrdered(result.getInOutParameters()::add);
result.setReturnType(resolveDataType(method.getReturnType(), method, null));
result.setText(node.getText());
result.setText(getTotalText(node));
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,28 @@
*******************************************************************************/
package org.eclipse.fordiac.ide.structuredtextcore.util;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.SequencedMap;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.fordiac.ide.model.dataexport.CommonElementExporter;
import org.eclipse.fordiac.ide.model.libraryElement.INamedElement;
import org.eclipse.fordiac.ide.structuredtextcore.stcore.STSource;
import org.eclipse.xtext.documentation.IEObjectDocumentationProviderExtension;
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.util.ITextRegion;

public abstract class STRecoveringPartitioner<E extends INamedElement> extends STAbstractCorePartitioner<E> {
public abstract class STRecoveringPartitioner<S extends EObject, E extends INamedElement>
extends STAbstractCorePartitioner<E> {
public static final String LOST_AND_FOUND_NAME = "LOST_AND_FOUND"; //$NON-NLS-1$
protected static final String LOST_AND_FOUND_NAME_PATTERN = LOST_AND_FOUND_NAME + "_%s"; //$NON-NLS-1$
public static final String LOST_AND_FOUND_COMMENT = "lost+found"; //$NON-NLS-1$
Expand All @@ -44,33 +55,51 @@ protected Optional<STCorePartition> emergencyPartition(final STSource source) {
return Optional.empty();
}

protected void handleLostAndFound(final ICompositeNode rootNode, final List<? extends EObject> source,
final List<E> result) {
var lastOffset = 0;
for (int index = 0; index < source.size(); index++) {
final var element = source.get(index);
final var node = NodeModelUtils.findActualNodeFor(element);
final int totalOffset = node.getTotalOffset();
if (totalOffset > lastOffset) {
handleLostAndFound(rootNode, index, lastOffset, totalOffset, result);
protected List<E> convertSourceElements(final ICompositeNode rootNode, final EList<S> elements) {
final SequencedMap<S, E> mapping = new LinkedHashMap<>();
for (final S element : elements) {
final E converted = convertSourceElement(element);
if (converted != null) {
mapping.put(element, converted);
}
}
final List<E> result = handleLostAndFound(rootNode, mapping);
handleDuplicates(result);
return result;
}

protected List<E> handleLostAndFound(final ICompositeNode rootNode, final SequencedMap<S, E> elements) {
int lastOffset = 0;
final List<E> result = new ArrayList<>(elements.size());
for (final Map.Entry<S, E> entry : elements.entrySet()) {
final ITextRegion region = getTextRegionWithComment(entry.getKey());
if (region.getOffset() > lastOffset) {
handleLostAndFound(rootNode, lastOffset, region.getOffset(), result);
}
lastOffset = node.getTotalEndOffset();
lastOffset = region.getOffset() + region.getLength();
result.add(entry.getValue());
}
final int totalEndOffset = rootNode.getTotalEndOffset();
if (totalEndOffset > lastOffset) {
if (result.isEmpty()) {
handleLostAndFound(rootNode, result.size(), lastOffset, totalEndOffset, result);
} else {
appendText(result.get(result.size() - 1), rootNode.getText().substring(lastOffset, totalEndOffset));
}
handleLostAndFound(rootNode, lastOffset, totalEndOffset, result);
}
return result;
}

protected void handleLostAndFound(final ICompositeNode rootNode, final int index, final int start, final int end,
private ITextRegion getTextRegionWithComment(final EObject element) {
final INode node = NodeModelUtils.getNode(element);
if (getDocumentationProvider() instanceof final IEObjectDocumentationProviderExtension documentationProviderExtension) {
return documentationProviderExtension.getDocumentationNodes(element).stream().map(INode::getTextRegion)
.reduce(node.getTextRegion(), ITextRegion::merge);
}
return node.getTotalTextRegion();
}

protected void handleLostAndFound(final ICompositeNode rootNode, final int start, final int end,
final List<E> result) {
final String text = rootNode.getText().substring(start, end);
if (!text.isBlank()) {
result.add(index, createLostAndFound(text, index));
result.add(createLostAndFound(text.trim(), result.size()));
}
}

Expand All @@ -82,8 +111,53 @@ protected static String generateLostAndFoundComment(final int index) {
return LOST_AND_FOUND_COMMENT_PATTERN.formatted(Integer.valueOf(index));
}

protected static void appendBlockComment(final INamedElement element, final StringBuilder builder) {
final String comment = element.getComment();
if (comment != null && !comment.isBlank()) {
builder.append("(*"); //$NON-NLS-1$
builder.append(CommonElementExporter.LINE_END);
for (final String s : comment.trim().split(CommonElementExporter.LINE_END)) {
builder.append(" * "); //$NON-NLS-1$
builder.append(s);
builder.append(CommonElementExporter.LINE_END);
}
builder.append(" *)"); //$NON-NLS-1$
builder.append(CommonElementExporter.LINE_END);
}
}

protected static void appendText(final String text, final StringBuilder builder) {
if (!text.startsWith(CommonElementExporter.LINE_END)) {
builder.append(CommonElementExporter.LINE_END);
}
builder.append(text);
if (!text.endsWith(CommonElementExporter.LINE_END)) {
builder.append(CommonElementExporter.LINE_END);
}
}

protected static String getText(final INode node) {
return node.getRootNode().getText().substring(node.getOffset(), node.getEndOffset());
}

protected static String getTotalText(final INode node) {
// rewind begin until after non-hidden node
INode begin = node;
while (begin.getPreviousSibling() instanceof final ILeafNode leafNode && leafNode.isHidden()) {
begin = leafNode;
}
// forward end until before non-hidden node
INode end = node;
while (end.getNextSibling() instanceof final ILeafNode leafNode && leafNode.isHidden()) {
end = leafNode;
}
return node.getRootNode().getText().substring(begin.getTotalOffset(), end.getTotalEndOffset());
}

protected abstract STCorePartition createEmergencyPartition(final String originalSource);

protected abstract E convertSourceElement(final S function);

protected abstract E createLostAndFound(final String text, final int index);

protected abstract void appendText(final E element, final String text);
Expand Down
Loading
Loading