Merge pull request #288 from eclipse/se_tracing

[code gen] Added support for tracing in code generation (#287)
This commit is contained in:
Sven Efftinge 2017-02-28 12:45:56 +01:00 committed by GitHub
commit 250d421868
35 changed files with 3208 additions and 0 deletions

View file

@ -0,0 +1,94 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox (https://typefox.io) 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.xtext.generator.trace.node
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtext.generator.trace.LocationData
import org.eclipse.xtext.generator.trace.SourceRelativeURI
import org.junit.Assert
import org.junit.Test
/**
* @author Sven Efftinge - Initial contribution and API
*/
class GeneratorNodeTest {
extension GeneratorNodeExtensions exts = new GeneratorNodeExtensions()
@Test def void testBasicCreationAndProcessing() {
val root = loc(0)
var node = root.trace
.append('notindented').appendNewLine
node.indent.trace(loc(1))
.append("indented1").appendNewLine
.append("indented2")
node.appendNewLine.append("dedented")
val processor = new GeneratorNodeProcessor()
val result = processor.process(node)
Assert.assertEquals('''
notindented
indented1
indented2
dedented'''.toString, result.toString)
Assert.assertEquals('''
CompletableTraceRegion [myOffset=0, myLength=44] associations={
LocationData [TextRegionWithLineInformation [0:100][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]
} nestedRegions={
CompletableTraceRegion [myOffset=14, myLength=21] associations={
LocationData [TextRegionWithLineInformation [1:99][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]
}
}'''.toString, result.traceRegion.toString)
}
@Test def void testTemplateProcessing() {
val root = loc(0)
var node = root.trace
.appendTemplate('''
«someCodeGen(2)»
''')
val processor = new GeneratorNodeProcessor()
val result = processor.process(node)
Assert.assertEquals(someCodeGen_noTrace(2).toString, result.toString)
Assert.assertEquals('''
CompletableTraceRegion [myOffset=0, myLength=80] associations={
LocationData [TextRegionWithLineInformation [0:100][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]
} nestedRegions={
CompletableTraceRegion [myOffset=7, myLength=5] associations={
LocationData [TextRegionWithLineInformation [10:90][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]
}
CompletableTraceRegion [myOffset=28, myLength=5] associations={
LocationData [TextRegionWithLineInformation [10:90][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]
}
CompletableTraceRegion [myOffset=47, myLength=5] associations={
LocationData [TextRegionWithLineInformation [11:89][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]
}
CompletableTraceRegion [myOffset=68, myLength=5] associations={
LocationData [TextRegionWithLineInformation [10:90][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]
}
}'''.toString, result.traceRegion.toString)
}
def StringConcatenationClient someCodeGen(int n) '''
«FOR i : 0..<n»
before «loc(10+i).trace.append("Hello")» after
«someCodeGen(n-1)»
«ENDFOR»
'''
def String someCodeGen_noTrace(int n) '''
«FOR i : 0..<n»
before «"Hello"» after
«someCodeGen_noTrace(n-1)»
«ENDFOR»
'''
def loc(int idx) {
new LocationData(idx, 100-idx, 0, 0, new SourceRelativeURI("foo/mymodel.dsl"))
}
}

View file

@ -0,0 +1,67 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox (https://typefox.io) 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.xtext.generator.trace.node
import org.junit.Test
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.junit.Assert
import org.eclipse.xtend2.lib.StringConcatenation
/**
* @author Sven Efftinge - Initial contribution and API
*/
class TemplateNodeTest {
@Test def void testSimpleString() {
assertEquals('''
fooo bar
''')
}
@Test def void testSimpleTemplate_01() {
assertEquals('''
«multiLineString» «multiLineString»
''')
}
@Test def void testWeirdTemplateString() {
assertEquals('''
fooo bar
d «IF 2 > 1»s
ee «FOR i : 1..4»
«''»
«other» «i»
«multiLineString» «multiLineString»
«ENDFOR»
«ENDIF»
''')
}
private def other() '''
foo «"dfdf" + 23» bar
'''
private def String multiLineString() '''
test
bar
«other()»
'''
def void assertEquals(StringConcatenationClient c) {
val ext = new GeneratorNodeExtensions()
val processor = new GeneratorNodeProcessor()
val root = new CompositeGeneratorNode()
ext.appendTemplate(root, c);
val result = processor.process(root)
val expected = new StringConcatenation()
expected.append(c)
Assert.assertEquals(expected.toString, result.toString)
}
}

View file

@ -0,0 +1,135 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox (https://typefox.io) 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.xtext.generator.trace.node
import com.google.inject.Inject
import org.eclipse.xtext.generator.InMemoryFileSystemAccess
import org.eclipse.xtext.linking.lazy.lazyLinking.LazyLinkingFactory
import org.eclipse.xtext.linking.lazy.lazyLinking.Model
import org.eclipse.xtext.linking.lazy.lazyLinking.Property
import org.eclipse.xtext.linking.lazy.lazyLinking.Type
import org.eclipse.xtext.linking.lazy.tests.LazyLinkingTestLanguageInjectorProvider
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.XtextRunner
import org.eclipse.xtext.testing.util.ParseHelper
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert
import org.eclipse.xtext.generator.trace.ITraceRegionProvider
/**
* @author Sven Efftinge - Initial contribution and API
*/
@RunWith(XtextRunner)
@InjectWith(LazyLinkingTestLanguageInjectorProvider)
class TracingSugarTest {
@TracedAccessors(LazyLinkingFactory)
static class MyExtensions {
/**
* manual implementation for unsupported multi cross reference
*/
def IGeneratorNode _type(Property it, (Type)=>String provider) {
val location = location(eClass.getEStructuralFeature("type"), 0)
val result = location.trace
result.append(provider.apply(type.head))
return result
}
}
@Inject extension MyExtensions
@Inject ParseHelper<Model> parseHelper
CharSequence generated
InMemoryFileSystemAccess fsa = new InMemoryFileSystemAccess() {
override generateFile(String fileName, CharSequence contents) {
generated = contents
super.generateFile(fileName, contents)
}
};
@Test def void testCodeGeneration() {
val root = parseHelper.parse('''
type String {}
type Foo {
String name;
}
''')
fsa.generateTracedFile('foo/bar.txt', root, '''
«FOR t : root.types»
«t._generateType»
«ENDFOR»
''')
// check the generated string is as expected
Assert.assertEquals('''
«FOR t : root.types»
«t.generateType»
«ENDFOR»
'''.toString, generated.toString)
val trace = (generated as ITraceRegionProvider).traceRegion
Assert.assertEquals('''
CompletableTraceRegion [myOffset=0, myLength=55] associations={
LocationData [TextRegionWithLineInformation [0:41][lineNumber=0, endLineNumber=3]][path=__synthetic0.lazylinkingtestlanguage]
} nestedRegions={
CompletableTraceRegion [myOffset=0, myLength=17] associations={
LocationData [TextRegionWithLineInformation [0:14][lineNumber=0, endLineNumber=0]][path=__synthetic0.lazylinkingtestlanguage]
} nestedRegions={
CompletableTraceRegion [myOffset=6, myLength=6] associations={
LocationData [TextRegionWithLineInformation [5:6][lineNumber=0, endLineNumber=0]][path=__synthetic0.lazylinkingtestlanguage]
}
}
CompletableTraceRegion [myOffset=17, myLength=38] associations={
LocationData [TextRegionWithLineInformation [15:26][lineNumber=1, endLineNumber=3]][path=__synthetic0.lazylinkingtestlanguage]
} nestedRegions={
CompletableTraceRegion [myOffset=23, myLength=3] associations={
LocationData [TextRegionWithLineInformation [20:3][lineNumber=1, endLineNumber=1]][path=__synthetic0.lazylinkingtestlanguage]
}
CompletableTraceRegion [myOffset=30, myLength=24] associations={
LocationData [TextRegionWithLineInformation [27:12][lineNumber=2, endLineNumber=2]][path=__synthetic0.lazylinkingtestlanguage]
} nestedRegions={
CompletableTraceRegion [myOffset=39, myLength=4] associations={
LocationData [TextRegionWithLineInformation [34:4][lineNumber=2, endLineNumber=2]][path=__synthetic0.lazylinkingtestlanguage]
}
CompletableTraceRegion [myOffset=46, myLength=6] associations={
LocationData [TextRegionWithLineInformation [27:6][lineNumber=2, endLineNumber=2]][path=__synthetic0.lazylinkingtestlanguage]
}
}
}
}'''.toString, trace.toString)
}
@Traced def _generateType(Type it) '''
Class «_name» {
«FOR p : properties»
«p._generateProperty»
«ENDFOR»
}
'''
@Traced def _generateProperty(Property it) '''
Property «_name» : «_type[name]»
'''
def generateType(Type it) '''
Class «name» {
«FOR p : properties»
«p.generateProperty»
«ENDFOR»
}
'''
def generateProperty(Property it) '''
Property «name» : «type.head.name»
'''
}

View file

@ -0,0 +1,179 @@
/**
* Copyright (c) 2017 TypeFox (https://typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.generator.trace.LocationData;
import org.eclipse.xtext.generator.trace.SourceRelativeURI;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.GeneratorNodeExtensions;
import org.eclipse.xtext.generator.trace.node.GeneratorNodeProcessor;
import org.eclipse.xtext.xbase.lib.ExclusiveRange;
import org.eclipse.xtext.xbase.lib.Extension;
import org.junit.Assert;
import org.junit.Test;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@SuppressWarnings("all")
public class GeneratorNodeTest {
@Extension
private GeneratorNodeExtensions exts = new GeneratorNodeExtensions();
@Test
public void testBasicCreationAndProcessing() {
final LocationData root = this.loc(0);
CompositeGeneratorNode node = this.exts.appendNewLine(this.exts.append(this.exts.trace(root), "notindented"));
this.exts.append(this.exts.appendNewLine(this.exts.append(this.exts.trace(this.exts.indent(node), this.loc(1)), "indented1")), "indented2");
this.exts.append(this.exts.appendNewLine(node), "dedented");
final GeneratorNodeProcessor processor = new GeneratorNodeProcessor();
final GeneratorNodeProcessor.Result result = processor.process(node);
StringConcatenation _builder = new StringConcatenation();
_builder.append("notindented");
_builder.newLine();
_builder.append(" ");
_builder.append("indented1");
_builder.newLine();
_builder.append(" ");
_builder.append("indented2");
_builder.newLine();
_builder.append("dedented");
Assert.assertEquals(_builder.toString(), result.toString());
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("CompletableTraceRegion [myOffset=0, myLength=44] associations={");
_builder_1.newLine();
_builder_1.append(" ");
_builder_1.append("LocationData [TextRegionWithLineInformation [0:100][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]");
_builder_1.newLine();
_builder_1.append("} nestedRegions={");
_builder_1.newLine();
_builder_1.append(" ");
_builder_1.append("CompletableTraceRegion [myOffset=14, myLength=21] associations={");
_builder_1.newLine();
_builder_1.append(" ");
_builder_1.append("LocationData [TextRegionWithLineInformation [1:99][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]");
_builder_1.newLine();
_builder_1.append(" ");
_builder_1.append("}");
_builder_1.newLine();
_builder_1.append("}");
Assert.assertEquals(_builder_1.toString(), result.getTraceRegion().toString());
}
@Test
public void testTemplateProcessing() {
final LocationData root = this.loc(0);
CompositeGeneratorNode _trace = this.exts.trace(root);
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
StringConcatenationClient _someCodeGen = GeneratorNodeTest.this.someCodeGen(2);
_builder.append(_someCodeGen);
_builder.newLineIfNotEmpty();
}
};
CompositeGeneratorNode node = this.exts.appendTemplate(_trace, _client);
final GeneratorNodeProcessor processor = new GeneratorNodeProcessor();
final GeneratorNodeProcessor.Result result = processor.process(node);
Assert.assertEquals(this.someCodeGen_noTrace(2).toString(), result.toString());
StringConcatenation _builder = new StringConcatenation();
_builder.append("CompletableTraceRegion [myOffset=0, myLength=80] associations={");
_builder.newLine();
_builder.append(" ");
_builder.append("LocationData [TextRegionWithLineInformation [0:100][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]");
_builder.newLine();
_builder.append("} nestedRegions={");
_builder.newLine();
_builder.append(" ");
_builder.append("CompletableTraceRegion [myOffset=7, myLength=5] associations={");
_builder.newLine();
_builder.append(" ");
_builder.append("LocationData [TextRegionWithLineInformation [10:90][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("CompletableTraceRegion [myOffset=28, myLength=5] associations={");
_builder.newLine();
_builder.append(" ");
_builder.append("LocationData [TextRegionWithLineInformation [10:90][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("CompletableTraceRegion [myOffset=47, myLength=5] associations={");
_builder.newLine();
_builder.append(" ");
_builder.append("LocationData [TextRegionWithLineInformation [11:89][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append(" ");
_builder.append("CompletableTraceRegion [myOffset=68, myLength=5] associations={");
_builder.newLine();
_builder.append(" ");
_builder.append("LocationData [TextRegionWithLineInformation [10:90][lineNumber=0, endLineNumber=0]][path=foo/mymodel.dsl]");
_builder.newLine();
_builder.append(" ");
_builder.append("}");
_builder.newLine();
_builder.append("}");
Assert.assertEquals(_builder.toString(), result.getTraceRegion().toString());
}
public StringConcatenationClient someCodeGen(final int n) {
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
{
ExclusiveRange _doubleDotLessThan = new ExclusiveRange(0, n, true);
for(final Integer i : _doubleDotLessThan) {
_builder.append("before ");
CompositeGeneratorNode _append = GeneratorNodeTest.this.exts.append(GeneratorNodeTest.this.exts.trace(GeneratorNodeTest.this.loc((10 + (i).intValue()))), "Hello");
_builder.append(_append);
_builder.append(" after");
_builder.newLineIfNotEmpty();
_builder.append(" ");
StringConcatenationClient _someCodeGen = GeneratorNodeTest.this.someCodeGen((n - 1));
_builder.append(_someCodeGen, " ");
_builder.newLineIfNotEmpty();
}
}
}
};
return _client;
}
public String someCodeGen_noTrace(final int n) {
StringConcatenation _builder = new StringConcatenation();
{
ExclusiveRange _doubleDotLessThan = new ExclusiveRange(0, n, true);
for(final Integer i : _doubleDotLessThan) {
_builder.append("before ");
_builder.append("Hello");
_builder.append(" after");
_builder.newLineIfNotEmpty();
_builder.append(" ");
String _someCodeGen_noTrace = this.someCodeGen_noTrace((n - 1));
_builder.append(_someCodeGen_noTrace, " ");
_builder.newLineIfNotEmpty();
}
}
return _builder.toString();
}
public LocationData loc(final int idx) {
SourceRelativeURI _sourceRelativeURI = new SourceRelativeURI("foo/mymodel.dsl");
return new LocationData(idx, (100 - idx), 0, 0, _sourceRelativeURI);
}
}

View file

@ -0,0 +1,123 @@
/**
* Copyright (c) 2017 TypeFox (https://typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.GeneratorNodeExtensions;
import org.eclipse.xtext.generator.trace.node.GeneratorNodeProcessor;
import org.eclipse.xtext.xbase.lib.IntegerRange;
import org.junit.Assert;
import org.junit.Test;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@SuppressWarnings("all")
public class TemplateNodeTest {
@Test
public void testSimpleString() {
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
_builder.append("fooo bar");
_builder.newLine();
}
};
this.assertEquals(_client);
}
@Test
public void testSimpleTemplate_01() {
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
String _multiLineString = TemplateNodeTest.this.multiLineString();
_builder.append(_multiLineString);
_builder.append(" ");
String _multiLineString_1 = TemplateNodeTest.this.multiLineString();
_builder.append(_multiLineString_1);
_builder.newLineIfNotEmpty();
}
};
this.assertEquals(_client);
}
@Test
public void testWeirdTemplateString() {
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
_builder.append("fooo bar");
_builder.newLine();
_builder.append("d ");
{
if ((2 > 1)) {
_builder.append("s");
_builder.newLineIfNotEmpty();
_builder.append("\t ");
_builder.append("ee ");
{
IntegerRange _upTo = new IntegerRange(1, 4);
for(final Integer i : _upTo) {
_builder.newLineIfNotEmpty();
_builder.newLineIfNotEmpty();
CharSequence _other = TemplateNodeTest.this.other();
_builder.append(_other);
_builder.append(" ");
_builder.append(i);
_builder.newLineIfNotEmpty();
String _multiLineString = TemplateNodeTest.this.multiLineString();
_builder.append(_multiLineString);
_builder.append(" ");
String _multiLineString_1 = TemplateNodeTest.this.multiLineString();
_builder.append(_multiLineString_1);
_builder.newLineIfNotEmpty();
}
}
}
}
}
};
this.assertEquals(_client);
}
private CharSequence other() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("foo ");
_builder.append(("dfdf" + Integer.valueOf(23)));
_builder.append(" bar");
_builder.newLineIfNotEmpty();
return _builder;
}
private String multiLineString() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("test ");
_builder.newLine();
_builder.append("bar");
_builder.newLine();
_builder.append("\t");
CharSequence _other = this.other();
_builder.append(_other, "\t");
_builder.newLineIfNotEmpty();
return _builder.toString();
}
public void assertEquals(final StringConcatenationClient c) {
final GeneratorNodeExtensions ext = new GeneratorNodeExtensions();
final GeneratorNodeProcessor processor = new GeneratorNodeProcessor();
final CompositeGeneratorNode root = new CompositeGeneratorNode();
ext.appendTemplate(root, c);
final GeneratorNodeProcessor.Result result = processor.process(root);
final StringConcatenation expected = new StringConcatenation();
expected.append(c);
Assert.assertEquals(expected.toString(), result.toString());
}
}

View file

@ -0,0 +1,393 @@
/**
* Copyright (c) 2017 TypeFox (https://typefox.io) 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.xtext.generator.trace.node;
import com.google.inject.Inject;
import java.util.function.Function;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.generator.InMemoryFileSystemAccess;
import org.eclipse.xtext.generator.trace.AbstractTraceRegion;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.ITraceRegionProvider;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.generator.trace.node.Traced;
import org.eclipse.xtext.generator.trace.node.TracedAccessors;
import org.eclipse.xtext.generator.trace.node.TracingSugar;
import org.eclipse.xtext.linking.lazy.lazyLinking.LazyLinkingFactory;
import org.eclipse.xtext.linking.lazy.lazyLinking.Model;
import org.eclipse.xtext.linking.lazy.lazyLinking.Property;
import org.eclipse.xtext.linking.lazy.lazyLinking.Type;
import org.eclipse.xtext.linking.lazy.lazyLinking.UnresolvedProxyProperty;
import org.eclipse.xtext.linking.lazy.tests.LazyLinkingTestLanguageInjectorProvider;
import org.eclipse.xtext.testing.InjectWith;
import org.eclipse.xtext.testing.XtextRunner;
import org.eclipse.xtext.testing.util.ParseHelper;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@RunWith(XtextRunner.class)
@InjectWith(LazyLinkingTestLanguageInjectorProvider.class)
@SuppressWarnings("all")
public class TracingSugarTest {
@TracedAccessors(LazyLinkingFactory.class)
public static class MyExtensions extends TracingSugar {
/**
* manual implementation for unsupported multi cross reference
*/
public IGeneratorNode _type(final Property it, final Function1<? super Type, ? extends String> provider) {
final ILocationData location = this.location(it, it.eClass().getEStructuralFeature("type"), 0);
final CompositeGeneratorNode result = this.trace(location);
this.append(result, provider.apply(IterableExtensions.<Type>head(it.getType())));
return result;
}
public IGeneratorNode _class(final Model target, final Function<Class<?>, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("class");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getClass()));
return trace;
}
public IGeneratorNode _name(final Property target) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("name");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, target.getName());
return trace;
}
public IGeneratorNode _name(final Property target, final Function<String, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("name");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getName()));
return trace;
}
public IGeneratorNode _class(final Property target, final Function<Class<?>, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("class");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getClass()));
return trace;
}
public IGeneratorNode _extends(final Type target, final Function<Type, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("extends");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getExtends()));
return trace;
}
public IGeneratorNode _name(final Type target) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("name");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, target.getName());
return trace;
}
public IGeneratorNode _name(final Type target, final Function<String, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("name");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getName()));
return trace;
}
public IGeneratorNode _parentId(final Type target, final Function<Property, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("parentId");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getParentId()));
return trace;
}
public IGeneratorNode _class(final Type target, final Function<Class<?>, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("class");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getClass()));
return trace;
}
public IGeneratorNode _name(final UnresolvedProxyProperty target) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("name");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, target.getName());
return trace;
}
public IGeneratorNode _name(final UnresolvedProxyProperty target, final Function<String, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("name");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getName()));
return trace;
}
public IGeneratorNode _class(final UnresolvedProxyProperty target, final Function<Class<?>, String> stringProvider) {
EStructuralFeature feature = target.eClass().getEStructuralFeature("class");
ILocationData location = this.location(target, feature, -1);
CompositeGeneratorNode trace = this.trace(location);
this.append(trace, stringProvider.apply(target.getClass()));
return trace;
}
}
@Inject
@Extension
private TracingSugarTest.MyExtensions _myExtensions;
@Inject
private ParseHelper<Model> parseHelper;
private CharSequence generated;
private InMemoryFileSystemAccess fsa = new InMemoryFileSystemAccess() {
@Override
public void generateFile(final String fileName, final CharSequence contents) {
TracingSugarTest.this.generated = contents;
super.generateFile(fileName, contents);
}
};
@Test
public void testCodeGeneration() {
try {
StringConcatenation _builder = new StringConcatenation();
_builder.append("type String {}");
_builder.newLine();
_builder.append("type Foo {");
_builder.newLine();
_builder.append("\t");
_builder.append("String name;");
_builder.newLine();
_builder.append("}");
_builder.newLine();
final Model root = this.parseHelper.parse(_builder);
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
{
EList<Type> _types = root.getTypes();
for(final Type t : _types) {
IGeneratorNode __generateType = TracingSugarTest.this._generateType(t);
_builder.append(__generateType);
_builder.newLineIfNotEmpty();
}
}
}
};
this._myExtensions.generateTracedFile(this.fsa, "foo/bar.txt", root, _client);
StringConcatenation _builder_1 = new StringConcatenation();
{
EList<Type> _types = root.getTypes();
for(final Type t : _types) {
CharSequence _generateType = this.generateType(t);
_builder_1.append(_generateType);
_builder_1.newLineIfNotEmpty();
}
}
Assert.assertEquals(_builder_1.toString(), this.generated.toString());
final AbstractTraceRegion trace = ((ITraceRegionProvider) this.generated).getTraceRegion();
StringConcatenation _builder_2 = new StringConcatenation();
_builder_2.append("CompletableTraceRegion [myOffset=0, myLength=55] associations={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("LocationData [TextRegionWithLineInformation [0:41][lineNumber=0, endLineNumber=3]][path=__synthetic0.lazylinkingtestlanguage]");
_builder_2.newLine();
_builder_2.append("} nestedRegions={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("CompletableTraceRegion [myOffset=0, myLength=17] associations={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("LocationData [TextRegionWithLineInformation [0:14][lineNumber=0, endLineNumber=0]][path=__synthetic0.lazylinkingtestlanguage]");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("} nestedRegions={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("CompletableTraceRegion [myOffset=6, myLength=6] associations={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("LocationData [TextRegionWithLineInformation [5:6][lineNumber=0, endLineNumber=0]][path=__synthetic0.lazylinkingtestlanguage]");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("}");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("}");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("CompletableTraceRegion [myOffset=17, myLength=38] associations={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("LocationData [TextRegionWithLineInformation [15:26][lineNumber=1, endLineNumber=3]][path=__synthetic0.lazylinkingtestlanguage]");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("} nestedRegions={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("CompletableTraceRegion [myOffset=23, myLength=3] associations={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("LocationData [TextRegionWithLineInformation [20:3][lineNumber=1, endLineNumber=1]][path=__synthetic0.lazylinkingtestlanguage]");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("}");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("CompletableTraceRegion [myOffset=30, myLength=24] associations={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("LocationData [TextRegionWithLineInformation [27:12][lineNumber=2, endLineNumber=2]][path=__synthetic0.lazylinkingtestlanguage]");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("} nestedRegions={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("CompletableTraceRegion [myOffset=39, myLength=4] associations={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("LocationData [TextRegionWithLineInformation [34:4][lineNumber=2, endLineNumber=2]][path=__synthetic0.lazylinkingtestlanguage]");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("}");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("CompletableTraceRegion [myOffset=46, myLength=6] associations={");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("LocationData [TextRegionWithLineInformation [27:6][lineNumber=2, endLineNumber=2]][path=__synthetic0.lazylinkingtestlanguage]");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("}");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("}");
_builder_2.newLine();
_builder_2.append(" ");
_builder_2.append("}");
_builder_2.newLine();
_builder_2.append("}");
Assert.assertEquals(_builder_2.toString(), trace.toString());
} catch (Throwable _e) {
throw Exceptions.sneakyThrow(_e);
}
}
@Traced
public IGeneratorNode _generateType(final Type it) {
ILocationData _location = this._myExtensions.location(it);
CompositeGeneratorNode _traceNode = this._myExtensions.trace(_location);
this._myExtensions.appendTemplate(_traceNode, __generateType(it));
return _traceNode;
}
@Traced
public IGeneratorNode _generateProperty(final Property it) {
ILocationData _location = this._myExtensions.location(it);
CompositeGeneratorNode _traceNode = this._myExtensions.trace(_location);
this._myExtensions.appendTemplate(_traceNode, __generateProperty(it));
return _traceNode;
}
public CharSequence generateType(final Type it) {
StringConcatenation _builder = new StringConcatenation();
_builder.append("Class ");
String _name = it.getName();
_builder.append(_name);
_builder.append(" {");
_builder.newLineIfNotEmpty();
{
EList<Property> _properties = it.getProperties();
for(final Property p : _properties) {
_builder.append("\t");
CharSequence _generateProperty = this.generateProperty(p);
_builder.append(_generateProperty, "\t");
_builder.newLineIfNotEmpty();
}
}
_builder.append("}");
_builder.newLine();
return _builder;
}
public CharSequence generateProperty(final Property it) {
StringConcatenation _builder = new StringConcatenation();
_builder.append("Property ");
String _name = it.getName();
_builder.append(_name);
_builder.append(" : ");
String _name_1 = IterableExtensions.<Type>head(it.getType()).getName();
_builder.append(_name_1);
_builder.newLineIfNotEmpty();
return _builder;
}
public StringConcatenationClient __generateType(final Type it) {
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
_builder.append("Class ");
IGeneratorNode __name = TracingSugarTest.this._myExtensions._name(it);
_builder.append(__name);
_builder.append(" {");
_builder.newLineIfNotEmpty();
{
EList<Property> _properties = it.getProperties();
for(final Property p : _properties) {
_builder.append("\t");
IGeneratorNode __generateProperty = TracingSugarTest.this._generateProperty(p);
_builder.append(__generateProperty, "\t");
_builder.newLineIfNotEmpty();
}
}
_builder.append("}");
_builder.newLine();
}
};
return _client;
}
public StringConcatenationClient __generateProperty(final Property it) {
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
_builder.append("Property ");
IGeneratorNode __name = TracingSugarTest.this._myExtensions._name(it);
_builder.append(__name);
_builder.append(" : ");
final Function1<Type, String> _function = (Type it_1) -> {
return it_1.getName();
};
IGeneratorNode __type = TracingSugarTest.this._myExtensions._type(it, _function);
_builder.append(__type);
_builder.newLineIfNotEmpty();
}
};
return _client;
}
}

View file

@ -34,6 +34,7 @@ Export-Package: org.eclipse.xtext,
org.eclipse.xtext.generator,
org.eclipse.xtext.generator.trace;x-friends:="org.eclipse.xtext.xbase.ui,org.eclipse.xtend.ide",
org.eclipse.xtext.generator.trace.internal;x-friends:="org.eclipse.xtext.ui,org.eclipse.xtext.xbase.ui",
org.eclipse.xtext.generator.trace.node;x-friends:="org.eclipse.xtext.tests",
org.eclipse.xtext.grammaranalysis;x-internal:=true,
org.eclipse.xtext.grammaranalysis.impl;x-friends:="org.eclipse.xtext.junit4,org.eclipse.xtext.xtext.generator",
org.eclipse.xtext.impl,

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import java.util.List
import org.eclipse.xtend.lib.annotations.Accessors
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Accessors class CompositeGeneratorNode implements IGeneratorNode {
val List<IGeneratorNode> children = newArrayList
override toString() '''
«class.simpleName» {
«FOR c: children»
«c.toString»
«ENDFOR»
}
'''
}

View file

@ -0,0 +1,117 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import com.google.inject.Inject
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtext.generator.trace.ILocationData
/**
* A builder API to create generator node trees
*
* @author Sven Efftinge - Initial contribution and API
*/
class GeneratorNodeExtensions {
@Inject GeneratorWhiteSpaceConfig wsConfig = new GeneratorWhiteSpaceConfig
/**
* @return a root trace node for the given location
*/
def CompositeGeneratorNode trace(ILocationData data) {
val result = new TraceNode(data)
return result
}
/**
* @return a trace node for the given location, appended as a child on the given parent
*/
def CompositeGeneratorNode trace(CompositeGeneratorNode parent, ILocationData data) {
val result = new TraceNode(data)
parent.children += result
return result
}
/**
* @return an indentation node, using the default indentation string, appended as a child on the given parent
*/
def CompositeGeneratorNode indent(CompositeGeneratorNode parent) {
return indent(parent, wsConfig.indentationString)
}
/**
* Appends the indentation string at the current position of the parent and adds a new composite node, indicating the same indentation for
* subsequent lines.
*
* @return an indentation node, using the given indentString, appended as a child on the given parent
*/
def CompositeGeneratorNode indent(CompositeGeneratorNode parent, String indentString) {
val text = new TextNode(indentString)
parent.children += text
val result = new IndentNode(indentString)
parent.children += result
return result
}
/**
* Appends a line separator node to the given parent.
*
* @return the given parent node
*/
def CompositeGeneratorNode appendNewLine(CompositeGeneratorNode parent) {
parent.children += new NewLineNode(wsConfig.lineDelimiter, false)
return parent
}
/**
* Appends a line separator node to the given parent.
*
* @return the given parent node
*/
def CompositeGeneratorNode appendNewLine(CompositeGeneratorNode parent, String lineSeparator) {
parent.children += new NewLineNode(lineSeparator, false)
return parent
}
/**
* Appends a line separator node that will only be effective if the current line contains non-whitespace text.
*
* @return the given parent node
*/
def CompositeGeneratorNode appendNewLineIfNotEmpty(CompositeGeneratorNode parent) {
parent.children += new NewLineNode(wsConfig.lineDelimiter, true)
return parent
}
/**
* Creates a text node containing the toString() representation of the given object and
* appends it to the given parent node.
*
* @return the given parent node
*/
def CompositeGeneratorNode append(CompositeGeneratorNode parent, Object object) {
if (object !== null) {
parent.children += new TextNode(object.toString)
}
return parent
}
/**
* Creates a template node for the given templateString and appends it to the given parent node.
*
* Templates are translated to generator node trees and expressions in templates can be of type IGeneratorNode.
*
* @return the given parent node
*/
def CompositeGeneratorNode appendTemplate(CompositeGeneratorNode parent, StringConcatenationClient templateString) {
val proc = new TemplateNode(templateString, this)
parent.children += proc
return parent
}
}

View file

@ -0,0 +1,179 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import java.util.ArrayDeque
import java.util.Deque
import java.util.List
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtend.lib.annotations.Data
import org.eclipse.xtend.lib.annotations.Delegate
import org.eclipse.xtext.generator.trace.AbstractStatefulTraceRegion
import org.eclipse.xtext.generator.trace.AbstractTraceRegion
import org.eclipse.xtext.generator.trace.ILocationData
import org.eclipse.xtext.generator.trace.ITraceRegionProvider
import org.eclipse.xtext.generator.trace.TraceNotFoundException
import org.eclipse.xtext.util.ITextRegionWithLineInformation
import org.eclipse.xtext.util.TextRegionWithLineInformation
/**
* @author Sven Efftinge - Initial contribution and API
*/
class GeneratorNodeProcessor {
@Data static class Result implements CharSequence, ITraceRegionProvider {
@Delegate CharSequence contents
AbstractTraceRegion traceRegion
override getTraceRegion() throws TraceNotFoundException {
return traceRegion
}
override toString() {
contents.toString()
}
}
@Accessors protected static class Context {
List<StringBuilder> lines
Deque<String> currentIndents
boolean pendingIndent
AbstractTraceRegion currentRegion = null
def StringBuilder currentLine() {
return lines.get(currentLineNumber);
}
def int contentLength() {
val contentLength = lines.fold(0) [ $0 + $1.length ]
if (pendingIndent) {
return contentLength + currentIndents.fold(0) [ $0 + $1.length ]
} else {
return contentLength
}
}
def int currentLineNumber() {
return lines.size - 1
}
}
def Result process(IGeneratorNode root) {
val ctx = new Context => [
lines = newArrayList(new StringBuilder())
currentIndents = new ArrayDeque<String>()
pendingIndent = true
]
doProcess(root, ctx)
return new Result(ctx.lines.join, ctx.currentRegion);
}
/**
* Indent nodes apply indentation between newline and content of its children.
*/
protected def dispatch void doProcess(IndentNode node, Context ctx) {
try {
ctx.currentIndents.push(node.indentationString);
doProcessChildren(node, ctx);
} finally {
ctx.currentIndents.pop
}
}
protected def dispatch void doProcess(NewLineNode node, Context ctx) {
val trimmedLine = ctx.currentLine.toString.trim
if (node.ifNotEmpty && trimmedLine.empty) {
ctx.lines.set(ctx.currentLineNumber, new StringBuilder())
return
}
ctx.currentLine.append(node.lineDelimiter)
ctx.lines.add(new StringBuilder)
ctx.pendingIndent = true
}
protected def dispatch void doProcess(TextNode node, Context ctx) {
val txt = node.text.toString
if (txt.empty) {
return;
}
if (ctx.pendingIndent) {
val indentString = new StringBuilder()
for (indentationString : ctx.currentIndents) {
indentString.append(indentationString)
}
ctx.currentLine.insert(0, indentString)
ctx.pendingIndent = false
}
ctx.currentLine.append(node.text)
}
protected def dispatch void doProcess(CompositeGeneratorNode node, Context ctx) {
doProcessChildren(node, ctx)
}
protected def dispatch void doProcess(TraceNode node, Context ctx) {
val beforeRegion = ctx.currentRegion
val newRegion = new CompletableTraceRegion(false, node.sourceLocation, beforeRegion)
val offset = ctx.contentLength
val startLineNumber = ctx.currentLineNumber;
try {
ctx.currentRegion = newRegion
doProcessChildren(node, ctx);
} finally {
if (beforeRegion !== null)
ctx.currentRegion = beforeRegion
newRegion.complete(offset, ctx.contentLength - offset, startLineNumber, ctx.currentLineNumber)
}
}
protected def void doProcessChildren(CompositeGeneratorNode node, Context ctx) {
for (child : node.children) {
doProcess(child, ctx)
}
}
/**
* Used to avoid multi-pass processing, when constructing a trace region tree.
*
* @author Sven Efftinge - Initial contribution and API
*/
static class CompletableTraceRegion extends AbstractStatefulTraceRegion {
static class CompletableTextRegion implements ITextRegionWithLineInformation {
ITextRegionWithLineInformation delegate
@Delegate def ITextRegionWithLineInformation getDelegate() {
if (delegate === null) {
throw new IllegalStateException("region not completed")
}
return delegate
}
}
CompletableTextRegion region
new(boolean useForDebugging, ILocationData associatedLocation, AbstractTraceRegion parent) {
this(new CompletableTextRegion(), useForDebugging, associatedLocation, parent)
}
protected new(CompletableTextRegion region, boolean useForDebugging, ILocationData associatedLocation,
AbstractTraceRegion parent) {
super(region, useForDebugging, associatedLocation, parent)
this.region = region
}
def void complete(int offset, int length, int startLine, int endLine) {
this.region.delegate = new TextRegionWithLineInformation(offset, length, startLine, endLine)
}
override protected isConsistentWithParent() {
return true
}
}
}

View file

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
/**
* @author Sven Efftinge - Initial contribution and API
*/
class GeneratorWhiteSpaceConfig {
def String getIndentationString() {
' '
}
def String getLineDelimiter() {
'\n'
}
}

View file

@ -0,0 +1,18 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
/**
*
* A node in the code generator graph
*
* @author Sven Efftinge - Initial contribution and API
*/
interface IGeneratorNode {
}

View file

@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
/**
* @author Sven Efftinge - Initial contribution and API
*/
@FinalFieldsConstructor class IndentNode extends CompositeGeneratorNode {
@Accessors val String indentationString
}

View file

@ -0,0 +1,18 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import org.eclipse.xtend.lib.annotations.Data
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Data class NewLineNode implements IGeneratorNode {
String lineDelimiter
boolean ifNotEmpty
}

View file

@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import com.google.common.base.Splitter
import com.google.common.collect.Iterables
import java.util.regex.Pattern
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtend2.lib.StringConcatenationClient.TargetStringConcatenation
/**
* @author Sven Efftinge - Initial contribution and API
*/
class TemplateNode extends CompositeGeneratorNode implements TargetStringConcatenation {
val GeneratorNodeExtensions nodeFactory
new(StringConcatenationClient contents, GeneratorNodeExtensions nodeFactory) {
this.nodeFactory = nodeFactory
StringConcatenationClient.appendTo(contents, this)
}
CompositeGeneratorNode currentParent = this
boolean isEmptyLine = true;
override append(Object object, String indentation) {
if (indentation.length > 0) {
val before = currentParent
try {
currentParent = new IndentNode(indentation)
before.children += currentParent
append(object)
} finally {
currentParent = before
}
} else {
append(object)
}
}
val lineSplitter = Splitter.on(Pattern.compile("\\R"))
override append(Object object) {
switch object {
StringConcatenationClient:
nodeFactory.appendTemplate(currentParent, object)
IGeneratorNode: {
currentParent.children += object
}
default: {
val str = object.toString
val iter = lineSplitter.split(str).iterator
while (iter.hasNext) {
val segment = iter.next
if (!segment.isEmpty)
isEmptyLine = false
nodeFactory.append(currentParent, segment)
if (iter.hasNext) {
newLine
}
}
}
}
}
protected def Iterable<IGeneratorNode> leafsBackwards(IGeneratorNode it) {
switch it {
CompositeGeneratorNode : {
children.reverseView.map[leafsBackwards].reduce[p1, p2| Iterables.concat(p1, p2)]
}
default : {
#[it]
}
}
}
override appendImmediate(Object object, String indentation) {
for (var i = currentParent.children.size; i >= 0; i--) {
val node = currentParent.children.get(i)
if (node instanceof TextNode) {
if (node.text.toString.trim.length === 0) {
currentParent.children.remove(i)
}
}
}
append(object, indentation)
}
override newLine() {
this.nodeFactory.appendNewLine(currentParent)
}
override newLineIfNotEmpty() {
this.nodeFactory.appendNewLineIfNotEmpty(currentParent)
}
//
override charAt(int index) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
override length() {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
override subSequence(int start, int end) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
}

View file

@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import org.eclipse.xtend.lib.annotations.Data
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Data class TextNode implements IGeneratorNode {
CharSequence text
}

View file

@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox (http://www.typefox.io) 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.xtext.generator.trace.node
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
import org.eclipse.xtext.generator.trace.ILocationData
/**
* @author Sven Efftinge - initial contribution and API
*/
@FinalFieldsConstructor class TraceNode extends CompositeGeneratorNode {
@Accessors final ILocationData sourceLocation
}

View file

@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtend.lib.macro.AbstractMethodProcessor
import org.eclipse.xtend.lib.macro.Active
import org.eclipse.xtend.lib.macro.TransformationContext
import org.eclipse.xtend.lib.macro.declaration.MutableClassDeclaration
import org.eclipse.xtend.lib.macro.declaration.MutableMethodDeclaration
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtext.generator.trace.ILocationData
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Active(TracedProcessor)
annotation Traced {
String tracingSugarFieldName = '_traceExtensions'
}
class TracedProcessor extends AbstractMethodProcessor {
override doTransform(MutableMethodDeclaration annotatedMethod, extension TransformationContext context) {
val traceSugar = TracingSugar.newTypeReference
val templateClient = StringConcatenationClient.newTypeReference
val nodeType = IGeneratorNode.newTypeReference
val eobjectType = EObject.newTypeReference
val clazz = annotatedMethod.declaringType as MutableClassDeclaration
val field = clazz.declaredFields.findFirst[
traceSugar.isAssignableFrom(type)
]
if (field === null) {
annotatedMethod.addError('''@«Traced.simpleName» methods can only declared in a class with a field of type «TracingSugar»''')
return;
}
val traceParam = annotatedMethod.parameters.findFirst[eobjectType.isAssignableFrom(type)]
if (traceParam === null) {
annotatedMethod.addError('''@«Traced.simpleName» methods need at least one parameter of type «EObject».''')
return;
}
clazz.addMethod('_'+annotatedMethod.simpleName) [
for (p : annotatedMethod.parameters) {
addParameter(p.simpleName, p.type)
}
returnType = templateClient
body = annotatedMethod.body
]
annotatedMethod.returnType = nodeType
annotatedMethod.body = '''
«ILocationData» _location = this.«field.simpleName».location(«traceParam.simpleName»);
«CompositeGeneratorNode» _traceNode = this.«field.simpleName».trace(_location);
this.«field.simpleName».appendTemplate(_traceNode, _«annotatedMethod.simpleName»(«annotatedMethod.parameters.join(',')[simpleName]»));
return _traceNode;
'''
}
}

View file

@ -0,0 +1,94 @@
/*******************************************************************************
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node
import java.util.function.Function
import org.eclipse.emf.ecore.EFactory
import org.eclipse.emf.ecore.EStructuralFeature
import org.eclipse.xtend.lib.macro.AbstractClassProcessor
import org.eclipse.xtend.lib.macro.Active
import org.eclipse.xtend.lib.macro.TransformationContext
import org.eclipse.xtend.lib.macro.declaration.InterfaceDeclaration
import org.eclipse.xtend.lib.macro.declaration.MutableClassDeclaration
import org.eclipse.xtend.lib.macro.declaration.ResolvedMethod
import org.eclipse.xtext.generator.trace.ILocationData
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Active(TracedAccessorsProcessor)
annotation TracedAccessors {
Class<? extends EFactory>[] value
}
class TracedAccessorsProcessor extends AbstractClassProcessor {
override doTransform(MutableClassDeclaration annotatedClass, extension TransformationContext context) {
annotatedClass.extendedClass = TracingSugar.newTypeReference()
val iterableType = Iterable.newTypeReference(newWildcardTypeReference)
val annotationType = TracedAccessors.newTypeReference
val factories = annotatedClass.findAnnotation(annotationType.type)?.getClassArrayValue("value")
if (factories === null) {
return;
}
for (f : factories.map[type].filter(InterfaceDeclaration)) {
for (t: f.declaredMethods.filter[simpleName.startsWith('create') && parameters.empty].map[returnType]) {
for (getter : t.allResolvedMethods.filter[ isSupportedGetter].filter[!iterableType.isAssignableFrom(declaration.returnType)]) {
val rt = getter.resolvedReturnType
if (TYPES_WITH_GOOD_TO_STRING.contains(rt.type.simpleName.toLowerCase)) {
annotatedClass.addMethod(getter.tracerName) [
returnType = IGeneratorNode.newTypeReference()
addParameter('target', t)
body = '''
«EStructuralFeature» feature = target.eClass().getEStructuralFeature("«getter.featureName»");
«ILocationData» location = this.location(target, feature, -1);
«CompositeGeneratorNode» trace = this.trace(location);
this.append(trace, target.«getter.declaration.simpleName»());
return trace;
'''
]
}
annotatedClass.addMethod(getter.tracerName) [
returnType = IGeneratorNode.newTypeReference
addParameter('target', t)
val stringProvider = Function.newTypeReference(rt, string)
addParameter('stringProvider', stringProvider)
body = '''
«EStructuralFeature» feature = target.eClass().getEStructuralFeature("«getter.featureName»");
«ILocationData» location = this.location(target, feature, -1);
«CompositeGeneratorNode» trace = this.trace(location);
this.append(trace, stringProvider.apply(target.«getter.declaration.simpleName»()));
return trace;
'''
]
}
}
}
}
def String tracerName(ResolvedMethod m) {
'_'+m.featureName
}
def String featureName(ResolvedMethod m) {
val n = m.declaration.simpleName
val skip = if (n.startsWith('get')) 3 else 2
m.declaration.simpleName.substring(skip).toFirstLower
}
static val TYPES_WITH_GOOD_TO_STRING = #{'string','boolean','int','long','integer'}
def boolean isSupportedGetter(ResolvedMethod it) {
if (!declaration.parameters.empty)
return false
if (declaration.static)
return false
val n = it.declaration.simpleName
return n.startsWith('get') || n.startsWith('is')
}
}

View file

@ -0,0 +1,79 @@
package org.eclipse.xtext.generator.trace.node
import com.google.inject.Inject
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EStructuralFeature
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.trace.ILocationData
import org.eclipse.xtext.generator.trace.ITraceURIConverter
import org.eclipse.xtext.generator.trace.LocationData
import org.eclipse.xtext.resource.ILocationInFileProvider
import org.eclipse.xtext.util.ITextRegionWithLineInformation
/**
* Some additional sugar extension to
* - create generator nodes for EObjects
* - create ILocationData for EObjects.
* - enhance FileSystemAccess for tracing
*/
class TracingSugar extends GeneratorNodeExtensions {
@Inject protected ILocationInFileProvider locationProvider
@Inject protected ITraceURIConverter traceURIConverter
@Inject protected GeneratorWhiteSpaceConfig whiteSpaceConfig
@Inject protected GeneratorNodeProcessor processor
/**
* Convenience extension, to generate traced code.
*/
def void generateTracedFile(IFileSystemAccess2 fsa, String path, EObject rootTrace, StringConcatenationClient code) {
val node = trace(rootTrace, code)
generateTracedFile(fsa, path, node);
}
/**
* Use to generate a file based on generator node.
*/
def void generateTracedFile(IFileSystemAccess2 fsa, String path, CompositeGeneratorNode rootNode) {
val result = processor.process(rootNode);
fsa.generateFile(path, result)
}
/**
* Convenience shorthand for <code>obj.location.trace</code>
*/
def CompositeGeneratorNode trace(EObject obj) {
return obj.location.trace;
}
/**
* Convenience shorthand for <code>obj.trace.appendTemplate('''some template''')</code>
*/
def CompositeGeneratorNode trace(EObject obj, StringConcatenationClient code) {
return obj.trace.appendTemplate(code)
}
/**
* @return ILocationData covering the <code>fullTextRegion</code> of the given EObject.
*/
def ILocationData location(EObject obj) {
val region = locationProvider.getFullTextRegion(obj)
val uri = traceURIConverter.getURIForTrace(obj.eResource)
return new LocationData(region as ITextRegionWithLineInformation, uri);
}
/**
* @param obj the EObject containing the feature
* @param feature the EStructuralFeature to trace
* @param idx the index of the value to trace, in case the feature contains a list, should be <code>-1</code> otherwise.
*
* @return ILocationData covering the <code>fullTextRegion</code> of the given feature in the given EObject.
*/
def ILocationData location(EObject obj, EStructuralFeature feature, int idx) {
val region = locationProvider.getFullTextRegion(obj, feature, idx)
val uri = traceURIConverter.getURIForTrace(obj.eResource)
return new LocationData(region as ITextRegionWithLineInformation, uri);
}
}

View file

@ -0,0 +1,49 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import java.util.List;
import org.eclipse.xtend.lib.annotations.Accessors;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Pure;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Accessors
@SuppressWarnings("all")
public class CompositeGeneratorNode implements IGeneratorNode {
private final List<IGeneratorNode> children = CollectionLiterals.<IGeneratorNode>newArrayList();
@Override
public String toString() {
StringConcatenation _builder = new StringConcatenation();
String _simpleName = this.getClass().getSimpleName();
_builder.append(_simpleName);
_builder.append(" {");
_builder.newLineIfNotEmpty();
{
for(final IGeneratorNode c : this.children) {
_builder.append("\t");
String _string = c.toString();
_builder.append(_string, "\t");
_builder.newLineIfNotEmpty();
}
}
_builder.append("}");
_builder.newLine();
return _builder.toString();
}
@Pure
public List<IGeneratorNode> getChildren() {
return this.children;
}
}

View file

@ -0,0 +1,141 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import com.google.inject.Inject;
import java.util.List;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.GeneratorWhiteSpaceConfig;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.generator.trace.node.IndentNode;
import org.eclipse.xtext.generator.trace.node.NewLineNode;
import org.eclipse.xtext.generator.trace.node.TemplateNode;
import org.eclipse.xtext.generator.trace.node.TextNode;
import org.eclipse.xtext.generator.trace.node.TraceNode;
/**
* A builder API to create generator node trees
*
* @author Sven Efftinge - Initial contribution and API
*/
@SuppressWarnings("all")
public class GeneratorNodeExtensions {
@Inject
private GeneratorWhiteSpaceConfig wsConfig = new GeneratorWhiteSpaceConfig();
/**
* @return a root trace node for the given location
*/
public CompositeGeneratorNode trace(final ILocationData data) {
final TraceNode result = new TraceNode(data);
return result;
}
/**
* @return a trace node for the given location, appended as a child on the given parent
*/
public CompositeGeneratorNode trace(final CompositeGeneratorNode parent, final ILocationData data) {
final TraceNode result = new TraceNode(data);
List<IGeneratorNode> _children = parent.getChildren();
_children.add(result);
return result;
}
/**
* @return an indentation node, using the default indentation string, appended as a child on the given parent
*/
public CompositeGeneratorNode indent(final CompositeGeneratorNode parent) {
return this.indent(parent, this.wsConfig.getIndentationString());
}
/**
* Appends the indentation string at the current position of the parent and adds a new composite node, indicating the same indentation for
* subsequent lines.
*
* @return an indentation node, using the given indentString, appended as a child on the given parent
*/
public CompositeGeneratorNode indent(final CompositeGeneratorNode parent, final String indentString) {
final TextNode text = new TextNode(indentString);
List<IGeneratorNode> _children = parent.getChildren();
_children.add(text);
final IndentNode result = new IndentNode(indentString);
List<IGeneratorNode> _children_1 = parent.getChildren();
_children_1.add(result);
return result;
}
/**
* Appends a line separator node to the given parent.
*
* @return the given parent node
*/
public CompositeGeneratorNode appendNewLine(final CompositeGeneratorNode parent) {
List<IGeneratorNode> _children = parent.getChildren();
String _lineDelimiter = this.wsConfig.getLineDelimiter();
NewLineNode _newLineNode = new NewLineNode(_lineDelimiter, false);
_children.add(_newLineNode);
return parent;
}
/**
* Appends a line separator node to the given parent.
*
* @return the given parent node
*/
public CompositeGeneratorNode appendNewLine(final CompositeGeneratorNode parent, final String lineSeparator) {
List<IGeneratorNode> _children = parent.getChildren();
NewLineNode _newLineNode = new NewLineNode(lineSeparator, false);
_children.add(_newLineNode);
return parent;
}
/**
* Appends a line separator node that will only be effective if the current line contains non-whitespace text.
*
* @return the given parent node
*/
public CompositeGeneratorNode appendNewLineIfNotEmpty(final CompositeGeneratorNode parent) {
List<IGeneratorNode> _children = parent.getChildren();
String _lineDelimiter = this.wsConfig.getLineDelimiter();
NewLineNode _newLineNode = new NewLineNode(_lineDelimiter, true);
_children.add(_newLineNode);
return parent;
}
/**
* Creates a text node containing the toString() representation of the given object and
* appends it to the given parent node.
*
* @return the given parent node
*/
public CompositeGeneratorNode append(final CompositeGeneratorNode parent, final Object object) {
if ((object != null)) {
List<IGeneratorNode> _children = parent.getChildren();
String _string = object.toString();
TextNode _textNode = new TextNode(_string);
_children.add(_textNode);
}
return parent;
}
/**
* Creates a template node for the given templateString and appends it to the given parent node.
*
* Templates are translated to generator node trees and expressions in templates can be of type IGeneratorNode.
*
* @return the given parent node
*/
public CompositeGeneratorNode appendTemplate(final CompositeGeneratorNode parent, final StringConcatenationClient templateString) {
final TemplateNode proc = new TemplateNode(templateString, this);
List<IGeneratorNode> _children = parent.getChildren();
_children.add(proc);
return parent;
}
}

View file

@ -0,0 +1,381 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
import java.util.stream.IntStream;
import org.eclipse.xtend.lib.annotations.Accessors;
import org.eclipse.xtend.lib.annotations.Data;
import org.eclipse.xtend.lib.annotations.Delegate;
import org.eclipse.xtext.generator.trace.AbstractStatefulTraceRegion;
import org.eclipse.xtext.generator.trace.AbstractTraceRegion;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.ITraceRegionProvider;
import org.eclipse.xtext.generator.trace.TraceNotFoundException;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.generator.trace.node.IndentNode;
import org.eclipse.xtext.generator.trace.node.NewLineNode;
import org.eclipse.xtext.generator.trace.node.TextNode;
import org.eclipse.xtext.generator.trace.node.TraceNode;
import org.eclipse.xtext.util.ITextRegion;
import org.eclipse.xtext.util.ITextRegionWithLineInformation;
import org.eclipse.xtext.util.TextRegionWithLineInformation;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Functions.Function2;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.Pure;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@SuppressWarnings("all")
public class GeneratorNodeProcessor {
@Data
public static class Result implements CharSequence, ITraceRegionProvider {
@Delegate
private final CharSequence contents;
private final AbstractTraceRegion traceRegion;
@Override
public AbstractTraceRegion getTraceRegion() throws TraceNotFoundException {
return this.traceRegion;
}
@Override
public String toString() {
return this.contents.toString();
}
public Result(final CharSequence contents, final AbstractTraceRegion traceRegion) {
super();
this.contents = contents;
this.traceRegion = traceRegion;
}
@Override
@Pure
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.contents== null) ? 0 : this.contents.hashCode());
result = prime * result + ((this.traceRegion== null) ? 0 : this.traceRegion.hashCode());
return result;
}
@Override
@Pure
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GeneratorNodeProcessor.Result other = (GeneratorNodeProcessor.Result) obj;
if (this.contents == null) {
if (other.contents != null)
return false;
} else if (!this.contents.equals(other.contents))
return false;
if (this.traceRegion == null) {
if (other.traceRegion != null)
return false;
} else if (!this.traceRegion.equals(other.traceRegion))
return false;
return true;
}
@Pure
public CharSequence getContents() {
return this.contents;
}
public char charAt(final int index) {
return this.contents.charAt(index);
}
public IntStream chars() {
return this.contents.chars();
}
public IntStream codePoints() {
return this.contents.codePoints();
}
public int length() {
return this.contents.length();
}
public CharSequence subSequence(final int start, final int end) {
return this.contents.subSequence(start, end);
}
}
@Accessors
protected static class Context {
private List<StringBuilder> lines;
private Deque<String> currentIndents;
private boolean pendingIndent;
private AbstractTraceRegion currentRegion = null;
public StringBuilder currentLine() {
return this.lines.get(this.currentLineNumber());
}
public int contentLength() {
final Function2<Integer, StringBuilder, Integer> _function = (Integer $0, StringBuilder $1) -> {
int _length = $1.length();
return Integer.valueOf((($0).intValue() + _length));
};
final Integer contentLength = IterableExtensions.<StringBuilder, Integer>fold(this.lines, Integer.valueOf(0), _function);
if (this.pendingIndent) {
final Function2<Integer, String, Integer> _function_1 = (Integer $0, String $1) -> {
int _length = $1.length();
return Integer.valueOf((($0).intValue() + _length));
};
Integer _fold = IterableExtensions.<String, Integer>fold(this.currentIndents, Integer.valueOf(0), _function_1);
return ((contentLength).intValue() + (_fold).intValue());
} else {
return (contentLength).intValue();
}
}
public int currentLineNumber() {
int _size = this.lines.size();
return (_size - 1);
}
@Pure
public List<StringBuilder> getLines() {
return this.lines;
}
public void setLines(final List<StringBuilder> lines) {
this.lines = lines;
}
@Pure
public Deque<String> getCurrentIndents() {
return this.currentIndents;
}
public void setCurrentIndents(final Deque<String> currentIndents) {
this.currentIndents = currentIndents;
}
@Pure
public boolean isPendingIndent() {
return this.pendingIndent;
}
public void setPendingIndent(final boolean pendingIndent) {
this.pendingIndent = pendingIndent;
}
@Pure
public AbstractTraceRegion getCurrentRegion() {
return this.currentRegion;
}
public void setCurrentRegion(final AbstractTraceRegion currentRegion) {
this.currentRegion = currentRegion;
}
}
/**
* Used to avoid multi-pass processing, when constructing a trace region tree.
*
* @author Sven Efftinge - Initial contribution and API
*/
public static class CompletableTraceRegion extends AbstractStatefulTraceRegion {
public static class CompletableTextRegion implements ITextRegionWithLineInformation {
private ITextRegionWithLineInformation delegate;
@Delegate
public ITextRegionWithLineInformation getDelegate() {
if ((this.delegate == null)) {
throw new IllegalStateException("region not completed");
}
return this.delegate;
}
public int getEndLineNumber() {
return this.getDelegate().getEndLineNumber();
}
public int getLineNumber() {
return this.getDelegate().getLineNumber();
}
public ITextRegionWithLineInformation merge(final ITextRegionWithLineInformation other) {
return this.getDelegate().merge(other);
}
public boolean contains(final ITextRegion other) {
return this.getDelegate().contains(other);
}
public boolean contains(final int offset) {
return this.getDelegate().contains(offset);
}
public int getLength() {
return this.getDelegate().getLength();
}
public int getOffset() {
return this.getDelegate().getOffset();
}
public ITextRegion merge(final ITextRegion region) {
return this.getDelegate().merge(region);
}
}
private GeneratorNodeProcessor.CompletableTraceRegion.CompletableTextRegion region;
public CompletableTraceRegion(final boolean useForDebugging, final ILocationData associatedLocation, final AbstractTraceRegion parent) {
this(new GeneratorNodeProcessor.CompletableTraceRegion.CompletableTextRegion(), useForDebugging, associatedLocation, parent);
}
protected CompletableTraceRegion(final GeneratorNodeProcessor.CompletableTraceRegion.CompletableTextRegion region, final boolean useForDebugging, final ILocationData associatedLocation, final AbstractTraceRegion parent) {
super(region, useForDebugging, associatedLocation, parent);
this.region = region;
}
public void complete(final int offset, final int length, final int startLine, final int endLine) {
TextRegionWithLineInformation _textRegionWithLineInformation = new TextRegionWithLineInformation(offset, length, startLine, endLine);
this.region.delegate = _textRegionWithLineInformation;
}
@Override
protected boolean isConsistentWithParent() {
return true;
}
}
public GeneratorNodeProcessor.Result process(final IGeneratorNode root) {
GeneratorNodeProcessor.Context _context = new GeneratorNodeProcessor.Context();
final Procedure1<GeneratorNodeProcessor.Context> _function = (GeneratorNodeProcessor.Context it) -> {
StringBuilder _stringBuilder = new StringBuilder();
it.lines = CollectionLiterals.<StringBuilder>newArrayList(_stringBuilder);
ArrayDeque<String> _arrayDeque = new ArrayDeque<String>();
it.currentIndents = _arrayDeque;
it.pendingIndent = true;
};
final GeneratorNodeProcessor.Context ctx = ObjectExtensions.<GeneratorNodeProcessor.Context>operator_doubleArrow(_context, _function);
this.doProcess(root, ctx);
String _join = IterableExtensions.join(ctx.lines);
return new GeneratorNodeProcessor.Result(_join, ctx.currentRegion);
}
/**
* Indent nodes apply indentation between newline and content of its children.
*/
protected void _doProcess(final IndentNode node, final GeneratorNodeProcessor.Context ctx) {
try {
ctx.currentIndents.push(node.getIndentationString());
this.doProcessChildren(node, ctx);
} finally {
ctx.currentIndents.pop();
}
}
protected void _doProcess(final NewLineNode node, final GeneratorNodeProcessor.Context ctx) {
final String trimmedLine = ctx.currentLine().toString().trim();
if ((node.isIfNotEmpty() && trimmedLine.isEmpty())) {
int _currentLineNumber = ctx.currentLineNumber();
StringBuilder _stringBuilder = new StringBuilder();
ctx.lines.set(_currentLineNumber, _stringBuilder);
return;
}
ctx.currentLine().append(node.getLineDelimiter());
StringBuilder _stringBuilder_1 = new StringBuilder();
ctx.lines.add(_stringBuilder_1);
ctx.pendingIndent = true;
}
protected void _doProcess(final TextNode node, final GeneratorNodeProcessor.Context ctx) {
final String txt = node.getText().toString();
boolean _isEmpty = txt.isEmpty();
if (_isEmpty) {
return;
}
if (ctx.pendingIndent) {
final StringBuilder indentString = new StringBuilder();
for (final String indentationString : ctx.currentIndents) {
indentString.append(indentationString);
}
ctx.currentLine().insert(0, indentString);
ctx.pendingIndent = false;
}
ctx.currentLine().append(node.getText());
}
protected void _doProcess(final CompositeGeneratorNode node, final GeneratorNodeProcessor.Context ctx) {
this.doProcessChildren(node, ctx);
}
protected void _doProcess(final TraceNode node, final GeneratorNodeProcessor.Context ctx) {
final AbstractTraceRegion beforeRegion = ctx.currentRegion;
ILocationData _sourceLocation = node.getSourceLocation();
final GeneratorNodeProcessor.CompletableTraceRegion newRegion = new GeneratorNodeProcessor.CompletableTraceRegion(false, _sourceLocation, beforeRegion);
final int offset = ctx.contentLength();
final int startLineNumber = ctx.currentLineNumber();
try {
ctx.currentRegion = newRegion;
this.doProcessChildren(node, ctx);
} finally {
if ((beforeRegion != null)) {
ctx.currentRegion = beforeRegion;
}
int _contentLength = ctx.contentLength();
int _minus = (_contentLength - offset);
newRegion.complete(offset, _minus, startLineNumber, ctx.currentLineNumber());
}
}
protected void doProcessChildren(final CompositeGeneratorNode node, final GeneratorNodeProcessor.Context ctx) {
List<IGeneratorNode> _children = node.getChildren();
for (final IGeneratorNode child : _children) {
this.doProcess(child, ctx);
}
}
protected void doProcess(final IGeneratorNode node, final GeneratorNodeProcessor.Context ctx) {
if (node instanceof IndentNode) {
_doProcess((IndentNode)node, ctx);
return;
} else if (node instanceof TraceNode) {
_doProcess((TraceNode)node, ctx);
return;
} else if (node instanceof CompositeGeneratorNode) {
_doProcess((CompositeGeneratorNode)node, ctx);
return;
} else if (node instanceof NewLineNode) {
_doProcess((NewLineNode)node, ctx);
return;
} else if (node instanceof TextNode) {
_doProcess((TextNode)node, ctx);
return;
} else {
throw new IllegalArgumentException("Unhandled parameter types: " +
Arrays.<Object>asList(node, ctx).toString());
}
}
}

View file

@ -0,0 +1,22 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@SuppressWarnings("all")
public class GeneratorWhiteSpaceConfig {
public String getIndentationString() {
return " ";
}
public String getLineDelimiter() {
return "\n";
}
}

View file

@ -0,0 +1,17 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
/**
* A node in the code generator graph
*
* @author Sven Efftinge - Initial contribution and API
*/
@SuppressWarnings("all")
public interface IGeneratorNode {
}

View file

@ -0,0 +1,33 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.xtend.lib.annotations.Accessors;
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.xbase.lib.Pure;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@FinalFieldsConstructor
@SuppressWarnings("all")
public class IndentNode extends CompositeGeneratorNode {
@Accessors
private final String indentationString;
public IndentNode(final String indentationString) {
super();
this.indentationString = indentationString;
}
@Pure
public String getIndentationString() {
return this.indentationString;
}
}

View file

@ -0,0 +1,79 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.xtend.lib.annotations.Data;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Data
@SuppressWarnings("all")
public class NewLineNode implements IGeneratorNode {
private final String lineDelimiter;
private final boolean ifNotEmpty;
public NewLineNode(final String lineDelimiter, final boolean ifNotEmpty) {
super();
this.lineDelimiter = lineDelimiter;
this.ifNotEmpty = ifNotEmpty;
}
@Override
@Pure
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.lineDelimiter== null) ? 0 : this.lineDelimiter.hashCode());
result = prime * result + (this.ifNotEmpty ? 1231 : 1237);
return result;
}
@Override
@Pure
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NewLineNode other = (NewLineNode) obj;
if (this.lineDelimiter == null) {
if (other.lineDelimiter != null)
return false;
} else if (!this.lineDelimiter.equals(other.lineDelimiter))
return false;
if (other.ifNotEmpty != this.ifNotEmpty)
return false;
return true;
}
@Override
@Pure
public String toString() {
ToStringBuilder b = new ToStringBuilder(this);
b.add("lineDelimiter", this.lineDelimiter);
b.add("ifNotEmpty", this.ifNotEmpty);
return b.toString();
}
@Pure
public String getLineDelimiter() {
return this.lineDelimiter;
}
@Pure
public boolean isIfNotEmpty() {
return this.ifNotEmpty;
}
}

View file

@ -0,0 +1,163 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.GeneratorNodeExtensions;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.generator.trace.node.IndentNode;
import org.eclipse.xtext.generator.trace.node.TextNode;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.Functions.Function2;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.ListExtensions;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@SuppressWarnings("all")
public class TemplateNode extends CompositeGeneratorNode implements StringConcatenationClient.TargetStringConcatenation {
private final GeneratorNodeExtensions nodeFactory;
public TemplateNode(final StringConcatenationClient contents, final GeneratorNodeExtensions nodeFactory) {
this.nodeFactory = nodeFactory;
StringConcatenationClient.appendTo(contents, this);
}
private CompositeGeneratorNode currentParent = this;
private boolean isEmptyLine = true;
@Override
public void append(final Object object, final String indentation) {
int _length = indentation.length();
boolean _greaterThan = (_length > 0);
if (_greaterThan) {
final CompositeGeneratorNode before = this.currentParent;
try {
IndentNode _indentNode = new IndentNode(indentation);
this.currentParent = _indentNode;
List<IGeneratorNode> _children = before.getChildren();
_children.add(this.currentParent);
this.append(object);
} finally {
this.currentParent = before;
}
} else {
this.append(object);
}
}
private final Splitter lineSplitter = Splitter.on(Pattern.compile("\\R"));
@Override
public void append(final Object object) {
boolean _matched = false;
if (object instanceof StringConcatenationClient) {
_matched=true;
this.nodeFactory.appendTemplate(this.currentParent, ((StringConcatenationClient)object));
}
if (!_matched) {
if (object instanceof IGeneratorNode) {
_matched=true;
List<IGeneratorNode> _children = this.currentParent.getChildren();
_children.add(((IGeneratorNode)object));
}
}
if (!_matched) {
{
final String str = object.toString();
final Iterator<String> iter = this.lineSplitter.split(str).iterator();
while (iter.hasNext()) {
{
final String segment = iter.next();
boolean _isEmpty = segment.isEmpty();
boolean _not = (!_isEmpty);
if (_not) {
this.isEmptyLine = false;
}
this.nodeFactory.append(this.currentParent, segment);
boolean _hasNext = iter.hasNext();
if (_hasNext) {
this.newLine();
}
}
}
}
}
}
protected Iterable<IGeneratorNode> leafsBackwards(final IGeneratorNode it) {
Iterable<IGeneratorNode> _switchResult = null;
boolean _matched = false;
if (it instanceof CompositeGeneratorNode) {
_matched=true;
final Function1<IGeneratorNode, Iterable<IGeneratorNode>> _function = (IGeneratorNode it_1) -> {
return this.leafsBackwards(it_1);
};
final Function2<Iterable<IGeneratorNode>, Iterable<IGeneratorNode>, Iterable<IGeneratorNode>> _function_1 = (Iterable<IGeneratorNode> p1, Iterable<IGeneratorNode> p2) -> {
return Iterables.<IGeneratorNode>concat(p1, p2);
};
_switchResult = IterableExtensions.<Iterable<IGeneratorNode>>reduce(ListExtensions.<IGeneratorNode, Iterable<IGeneratorNode>>map(ListExtensions.<IGeneratorNode>reverseView(((CompositeGeneratorNode)it).getChildren()), _function), _function_1);
}
if (!_matched) {
_switchResult = Collections.<IGeneratorNode>unmodifiableList(CollectionLiterals.<IGeneratorNode>newArrayList(it));
}
return _switchResult;
}
@Override
public void appendImmediate(final Object object, final String indentation) {
for (int i = this.currentParent.getChildren().size(); (i >= 0); i--) {
{
final IGeneratorNode node = this.currentParent.getChildren().get(i);
if ((node instanceof TextNode)) {
int _length = ((TextNode)node).getText().toString().trim().length();
boolean _tripleEquals = (_length == 0);
if (_tripleEquals) {
this.currentParent.getChildren().remove(i);
}
}
}
}
this.append(object, indentation);
}
@Override
public void newLine() {
this.nodeFactory.appendNewLine(this.currentParent);
}
@Override
public void newLineIfNotEmpty() {
this.nodeFactory.appendNewLineIfNotEmpty(this.currentParent);
}
@Override
public char charAt(final int index) {
throw new UnsupportedOperationException("TODO: auto-generated method stub");
}
@Override
public int length() {
throw new UnsupportedOperationException("TODO: auto-generated method stub");
}
@Override
public CharSequence subSequence(final int start, final int end) {
throw new UnsupportedOperationException("TODO: auto-generated method stub");
}
}

View file

@ -0,0 +1,67 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.xtend.lib.annotations.Data;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Data
@SuppressWarnings("all")
public class TextNode implements IGeneratorNode {
private final CharSequence text;
public TextNode(final CharSequence text) {
super();
this.text = text;
}
@Override
@Pure
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.text== null) ? 0 : this.text.hashCode());
return result;
}
@Override
@Pure
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TextNode other = (TextNode) obj;
if (this.text == null) {
if (other.text != null)
return false;
} else if (!this.text.equals(other.text))
return false;
return true;
}
@Override
@Pure
public String toString() {
ToStringBuilder b = new ToStringBuilder(this);
b.add("text", this.text);
return b.toString();
}
@Pure
public CharSequence getText() {
return this.text;
}
}

View file

@ -0,0 +1,34 @@
/**
* Copyright (c) 2017 TypeFox (http://www.typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.xtend.lib.annotations.Accessors;
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.xbase.lib.Pure;
/**
* @author Sven Efftinge - initial contribution and API
*/
@FinalFieldsConstructor
@SuppressWarnings("all")
public class TraceNode extends CompositeGeneratorNode {
@Accessors
private final ILocationData sourceLocation;
public TraceNode(final ILocationData sourceLocation) {
super();
this.sourceLocation = sourceLocation;
}
@Pure
public ILocationData getSourceLocation() {
return this.sourceLocation;
}
}

View file

@ -0,0 +1,19 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.xtend.lib.macro.Active;
import org.eclipse.xtext.generator.trace.node.TracedProcessor;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Active(TracedProcessor.class)
public @interface Traced {
public String tracingSugarFieldName() default "_traceExtensions";
}

View file

@ -0,0 +1,20 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.xtend.lib.macro.Active;
import org.eclipse.xtext.generator.trace.node.TracedAccessorsProcessor;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@Active(TracedAccessorsProcessor.class)
public @interface TracedAccessors {
public Class<? extends EFactory>[] value();
}

View file

@ -0,0 +1,189 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import com.google.common.collect.Iterables;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtend.lib.macro.AbstractClassProcessor;
import org.eclipse.xtend.lib.macro.TransformationContext;
import org.eclipse.xtend.lib.macro.declaration.AnnotationReference;
import org.eclipse.xtend.lib.macro.declaration.InterfaceDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MethodDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableClassDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableMethodDeclaration;
import org.eclipse.xtend.lib.macro.declaration.ResolvedMethod;
import org.eclipse.xtend.lib.macro.declaration.Type;
import org.eclipse.xtend.lib.macro.declaration.TypeReference;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.generator.trace.node.TracedAccessors;
import org.eclipse.xtext.generator.trace.node.TracingSugar;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Conversions;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.ListExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.StringExtensions;
@SuppressWarnings("all")
public class TracedAccessorsProcessor extends AbstractClassProcessor {
@Override
public void doTransform(final MutableClassDeclaration annotatedClass, @Extension final TransformationContext context) {
annotatedClass.setExtendedClass(context.newTypeReference(TracingSugar.class));
final TypeReference iterableType = context.newTypeReference(Iterable.class, context.newWildcardTypeReference());
final TypeReference annotationType = context.newTypeReference(TracedAccessors.class);
AnnotationReference _findAnnotation = annotatedClass.findAnnotation(annotationType.getType());
TypeReference[] _classArrayValue = null;
if (_findAnnotation!=null) {
_classArrayValue=_findAnnotation.getClassArrayValue("value");
}
final TypeReference[] factories = _classArrayValue;
if ((factories == null)) {
return;
}
final Function1<TypeReference, Type> _function = (TypeReference it) -> {
return it.getType();
};
Iterable<InterfaceDeclaration> _filter = Iterables.<InterfaceDeclaration>filter(ListExtensions.<TypeReference, Type>map(((List<TypeReference>)Conversions.doWrapArray(factories)), _function), InterfaceDeclaration.class);
for (final InterfaceDeclaration f : _filter) {
final Function1<MethodDeclaration, Boolean> _function_1 = (MethodDeclaration it) -> {
return Boolean.valueOf((it.getSimpleName().startsWith("create") && IterableExtensions.isEmpty(it.getParameters())));
};
final Function1<MethodDeclaration, TypeReference> _function_2 = (MethodDeclaration it) -> {
return it.getReturnType();
};
Iterable<TypeReference> _map = IterableExtensions.map(IterableExtensions.filter(f.getDeclaredMethods(), _function_1), _function_2);
for (final TypeReference t : _map) {
final Function1<ResolvedMethod, Boolean> _function_3 = (ResolvedMethod it) -> {
return Boolean.valueOf(this.isSupportedGetter(it));
};
final Function1<ResolvedMethod, Boolean> _function_4 = (ResolvedMethod it) -> {
boolean _isAssignableFrom = iterableType.isAssignableFrom(it.getDeclaration().getReturnType());
return Boolean.valueOf((!_isAssignableFrom));
};
Iterable<? extends ResolvedMethod> _filter_1 = IterableExtensions.filter(IterableExtensions.filter(t.getAllResolvedMethods(), _function_3), _function_4);
for (final ResolvedMethod getter : _filter_1) {
{
final TypeReference rt = getter.getResolvedReturnType();
boolean _contains = TracedAccessorsProcessor.TYPES_WITH_GOOD_TO_STRING.contains(rt.getType().getSimpleName().toLowerCase());
if (_contains) {
final Procedure1<MutableMethodDeclaration> _function_5 = (MutableMethodDeclaration it) -> {
it.setReturnType(context.newTypeReference(IGeneratorNode.class));
it.addParameter("target", t);
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
_builder.append(EStructuralFeature.class);
_builder.append(" feature = target.eClass().getEStructuralFeature(\"");
String _featureName = TracedAccessorsProcessor.this.featureName(getter);
_builder.append(_featureName);
_builder.append("\");");
_builder.newLineIfNotEmpty();
_builder.append(ILocationData.class);
_builder.append(" location = this.location(target, feature, -1);");
_builder.newLineIfNotEmpty();
_builder.append(CompositeGeneratorNode.class);
_builder.append(" trace = this.trace(location);");
_builder.newLineIfNotEmpty();
_builder.append("this.append(trace, target.");
String _simpleName = getter.getDeclaration().getSimpleName();
_builder.append(_simpleName);
_builder.append("());");
_builder.newLineIfNotEmpty();
_builder.append("return trace;");
_builder.newLine();
}
};
it.setBody(_client);
};
annotatedClass.addMethod(this.tracerName(getter), _function_5);
}
final Procedure1<MutableMethodDeclaration> _function_6 = (MutableMethodDeclaration it) -> {
it.setReturnType(context.newTypeReference(IGeneratorNode.class));
it.addParameter("target", t);
final TypeReference stringProvider = context.newTypeReference(Function.class, rt, context.getString());
it.addParameter("stringProvider", stringProvider);
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
_builder.append(EStructuralFeature.class);
_builder.append(" feature = target.eClass().getEStructuralFeature(\"");
String _featureName = TracedAccessorsProcessor.this.featureName(getter);
_builder.append(_featureName);
_builder.append("\");");
_builder.newLineIfNotEmpty();
_builder.append(ILocationData.class);
_builder.append(" location = this.location(target, feature, -1);");
_builder.newLineIfNotEmpty();
_builder.append(CompositeGeneratorNode.class);
_builder.append(" trace = this.trace(location);");
_builder.newLineIfNotEmpty();
_builder.append("this.append(trace, stringProvider.apply(target.");
String _simpleName = getter.getDeclaration().getSimpleName();
_builder.append(_simpleName);
_builder.append("()));");
_builder.newLineIfNotEmpty();
_builder.append("return trace;");
_builder.newLine();
}
};
it.setBody(_client);
};
annotatedClass.addMethod(this.tracerName(getter), _function_6);
}
}
}
}
}
public String tracerName(final ResolvedMethod m) {
String _featureName = this.featureName(m);
return ("_" + _featureName);
}
public String featureName(final ResolvedMethod m) {
String _xblockexpression = null;
{
final String n = m.getDeclaration().getSimpleName();
int _xifexpression = (int) 0;
boolean _startsWith = n.startsWith("get");
if (_startsWith) {
_xifexpression = 3;
} else {
_xifexpression = 2;
}
final int skip = _xifexpression;
_xblockexpression = StringExtensions.toFirstLower(m.getDeclaration().getSimpleName().substring(skip));
}
return _xblockexpression;
}
private final static Set<String> TYPES_WITH_GOOD_TO_STRING = Collections.<String>unmodifiableSet(CollectionLiterals.<String>newHashSet("string", "boolean", "int", "long", "integer"));
public boolean isSupportedGetter(final ResolvedMethod it) {
boolean _isEmpty = IterableExtensions.isEmpty(it.getDeclaration().getParameters());
boolean _not = (!_isEmpty);
if (_not) {
return false;
}
boolean _isStatic = it.getDeclaration().isStatic();
if (_isStatic) {
return false;
}
final String n = it.getDeclaration().getSimpleName();
return (n.startsWith("get") || n.startsWith("is"));
}
}

View file

@ -0,0 +1,120 @@
/**
* Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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.xtext.generator.trace.node;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtend.lib.macro.AbstractMethodProcessor;
import org.eclipse.xtend.lib.macro.TransformationContext;
import org.eclipse.xtend.lib.macro.declaration.MutableClassDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableFieldDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableMethodDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableParameterDeclaration;
import org.eclipse.xtend.lib.macro.declaration.MutableTypeDeclaration;
import org.eclipse.xtend.lib.macro.declaration.TypeReference;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.IGeneratorNode;
import org.eclipse.xtext.generator.trace.node.Traced;
import org.eclipse.xtext.generator.trace.node.TracingSugar;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
@SuppressWarnings("all")
public class TracedProcessor extends AbstractMethodProcessor {
@Override
public void doTransform(final MutableMethodDeclaration annotatedMethod, @Extension final TransformationContext context) {
final TypeReference traceSugar = context.newTypeReference(TracingSugar.class);
final TypeReference templateClient = context.newTypeReference(StringConcatenationClient.class);
final TypeReference nodeType = context.newTypeReference(IGeneratorNode.class);
final TypeReference eobjectType = context.newTypeReference(EObject.class);
MutableTypeDeclaration _declaringType = annotatedMethod.getDeclaringType();
final MutableClassDeclaration clazz = ((MutableClassDeclaration) _declaringType);
final Function1<MutableFieldDeclaration, Boolean> _function = (MutableFieldDeclaration it) -> {
return Boolean.valueOf(traceSugar.isAssignableFrom(it.getType()));
};
final MutableFieldDeclaration field = IterableExtensions.findFirst(clazz.getDeclaredFields(), _function);
if ((field == null)) {
StringConcatenation _builder = new StringConcatenation();
_builder.append("@");
String _simpleName = Traced.class.getSimpleName();
_builder.append(_simpleName);
_builder.append(" methods can only declared in a class with a field of type ");
_builder.append(TracingSugar.class);
context.addError(annotatedMethod, _builder.toString());
return;
}
final Function1<MutableParameterDeclaration, Boolean> _function_1 = (MutableParameterDeclaration it) -> {
return Boolean.valueOf(eobjectType.isAssignableFrom(it.getType()));
};
final MutableParameterDeclaration traceParam = IterableExtensions.findFirst(annotatedMethod.getParameters(), _function_1);
if ((traceParam == null)) {
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("@");
String _simpleName_1 = Traced.class.getSimpleName();
_builder_1.append(_simpleName_1);
_builder_1.append(" methods need at least one parameter of type ");
_builder_1.append(EObject.class);
_builder_1.append(".");
context.addError(annotatedMethod, _builder_1.toString());
return;
}
String _simpleName_2 = annotatedMethod.getSimpleName();
String _plus = ("_" + _simpleName_2);
final Procedure1<MutableMethodDeclaration> _function_2 = (MutableMethodDeclaration it) -> {
Iterable<? extends MutableParameterDeclaration> _parameters = annotatedMethod.getParameters();
for (final MutableParameterDeclaration p : _parameters) {
it.addParameter(p.getSimpleName(), p.getType());
}
it.setReturnType(templateClient);
it.setBody(annotatedMethod.getBody());
};
clazz.addMethod(_plus, _function_2);
annotatedMethod.setReturnType(nodeType);
StringConcatenationClient _client = new StringConcatenationClient() {
@Override
protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) {
_builder.append(ILocationData.class);
_builder.append(" _location = this.");
String _simpleName = field.getSimpleName();
_builder.append(_simpleName);
_builder.append(".location(");
String _simpleName_1 = traceParam.getSimpleName();
_builder.append(_simpleName_1);
_builder.append(");");
_builder.newLineIfNotEmpty();
_builder.append(CompositeGeneratorNode.class);
_builder.append(" _traceNode = this.");
String _simpleName_2 = field.getSimpleName();
_builder.append(_simpleName_2);
_builder.append(".trace(_location);");
_builder.newLineIfNotEmpty();
_builder.append("this.");
String _simpleName_3 = field.getSimpleName();
_builder.append(_simpleName_3);
_builder.append(".appendTemplate(_traceNode, _");
String _simpleName_4 = annotatedMethod.getSimpleName();
_builder.append(_simpleName_4);
_builder.append("(");
final Function1<MutableParameterDeclaration, CharSequence> _function = (MutableParameterDeclaration it) -> {
return it.getSimpleName();
};
String _join = IterableExtensions.join(annotatedMethod.getParameters(), ",", _function);
_builder.append(_join);
_builder.append("));");
_builder.newLineIfNotEmpty();
_builder.append("return _traceNode;");
_builder.newLine();
}
};
annotatedMethod.setBody(_client);
}
}

View file

@ -0,0 +1,91 @@
package org.eclipse.xtext.generator.trace.node;
import com.google.inject.Inject;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtend2.lib.StringConcatenationClient;
import org.eclipse.xtext.generator.IFileSystemAccess2;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.ITraceURIConverter;
import org.eclipse.xtext.generator.trace.LocationData;
import org.eclipse.xtext.generator.trace.SourceRelativeURI;
import org.eclipse.xtext.generator.trace.node.CompositeGeneratorNode;
import org.eclipse.xtext.generator.trace.node.GeneratorNodeExtensions;
import org.eclipse.xtext.generator.trace.node.GeneratorNodeProcessor;
import org.eclipse.xtext.generator.trace.node.GeneratorWhiteSpaceConfig;
import org.eclipse.xtext.resource.ILocationInFileProvider;
import org.eclipse.xtext.util.ITextRegion;
import org.eclipse.xtext.util.ITextRegionWithLineInformation;
/**
* Some additional sugar extension to
* - create generator nodes for EObjects
* - create ILocationData for EObjects.
* - enhance FileSystemAccess for tracing
*/
@SuppressWarnings("all")
public class TracingSugar extends GeneratorNodeExtensions {
@Inject
protected ILocationInFileProvider locationProvider;
@Inject
protected ITraceURIConverter traceURIConverter;
@Inject
protected GeneratorWhiteSpaceConfig whiteSpaceConfig;
@Inject
protected GeneratorNodeProcessor processor;
/**
* Convenience extension, to generate traced code.
*/
public void generateTracedFile(final IFileSystemAccess2 fsa, final String path, final EObject rootTrace, final StringConcatenationClient code) {
final CompositeGeneratorNode node = this.trace(rootTrace, code);
this.generateTracedFile(fsa, path, node);
}
/**
* Use to generate a file based on generator node.
*/
public void generateTracedFile(final IFileSystemAccess2 fsa, final String path, final CompositeGeneratorNode rootNode) {
final GeneratorNodeProcessor.Result result = this.processor.process(rootNode);
fsa.generateFile(path, result);
}
/**
* Convenience shorthand for <code>obj.location.trace</code>
*/
public CompositeGeneratorNode trace(final EObject obj) {
return this.trace(this.location(obj));
}
/**
* Convenience shorthand for <code>obj.trace.appendTemplate('''some template''')</code>
*/
public CompositeGeneratorNode trace(final EObject obj, final StringConcatenationClient code) {
return this.appendTemplate(this.trace(obj), code);
}
/**
* @return ILocationData covering the <code>fullTextRegion</code> of the given EObject.
*/
public ILocationData location(final EObject obj) {
final ITextRegion region = this.locationProvider.getFullTextRegion(obj);
final SourceRelativeURI uri = this.traceURIConverter.getURIForTrace(obj.eResource());
return new LocationData(((ITextRegionWithLineInformation) region), uri);
}
/**
* @param obj the EObject containing the feature
* @param feature the EStructuralFeature to trace
* @param idx the index of the value to trace, in case the feature contains a list, should be <code>-1</code> otherwise.
*
* @return ILocationData covering the <code>fullTextRegion</code> of the given feature in the given EObject.
*/
public ILocationData location(final EObject obj, final EStructuralFeature feature, final int idx) {
final ITextRegion region = this.locationProvider.getFullTextRegion(obj, feature, idx);
final SourceRelativeURI uri = this.traceURIConverter.getURIForTrace(obj.eResource());
return new LocationData(((ITextRegionWithLineInformation) region), uri);
}
}