mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-15 16:28:56 +00:00
Merge pull request #288 from eclipse/se_tracing
[code gen] Added support for tracing in code generation (#287)
This commit is contained in:
commit
250d421868
35 changed files with 3208 additions and 0 deletions
|
@ -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"))
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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»
|
||||
'''
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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»
|
||||
}
|
||||
'''
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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'
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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;
|
||||
'''
|
||||
}
|
||||
|
||||
}
|
|
@ -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')
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue