Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions annot/src/test/java/com/predic8/membrane/annot/ParsingTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.predic8.membrane.annot;

import com.predic8.membrane.annot.util.CompilerHelper;
import org.junit.jupiter.api.Test;

import static com.predic8.membrane.annot.SpringConfigurationXSDGeneratingAnnotationProcessorTest.MC_MAIN_DEMO;
import static com.predic8.membrane.annot.util.CompilerHelper.*;

public class ParsingTest {

private String wrapSpring(String content) {
return """
<spring:beans xmlns="http://membrane-soa.org/demo/1/"
xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://membrane-soa.org/demo/1/ http://membrane-soa.org/schemas/demo-1.xsd">
""" + content + """
</spring:beans>
""";
}

@Test
public void simple() {
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
import java.util.List;
@MCElement(name="demo")
public class DemoElement {
}
""");
var result = CompilerHelper.compile(sources, false);
assertCompilerResult(true, result);

parse(result, wrapSpring("""
<demo />
"""));
}

@Test
public void childElements() {
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
import java.util.List;
@MCElement(name="root")
public class DemoElement {
@MCChildElement
public void setChild(AbstractDemoChildElement s) {}
}
---
package com.predic8.membrane.demo;
public abstract class AbstractDemoChildElement {
}
---
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
@MCElement(name="child1")
public class Child1 extends AbstractDemoChildElement {
}
---
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
@MCElement(name="child2")
public class Child2 extends AbstractDemoChildElement {
}
""");
var result = CompilerHelper.compile(sources, false);
assertCompilerResult(true, result);

parse(result, wrapSpring("""
<root>
<child1 />
</root>
"""));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
public class SpringConfigXSDErrorsTest {
@Test
public void mcMainMissing() {
List<JavaFileObject> sources = splitSources("""
var sources = splitSources("""
package com.predic8.membrane.demo;
public class Demo {
}
Expand All @@ -47,7 +47,7 @@ public class DemoElement {

@Test
public void mcElementNameMissing() {
List<JavaFileObject> sources = splitSources("""
var sources = splitSources("""
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
@MCElement
Expand All @@ -63,7 +63,7 @@ public class DemoElement {

@Test
public void mcElementMissing() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO);
var sources = splitSources(MC_MAIN_DEMO);
var result = CompilerHelper.compile(sources, false);

assertCompilerResult(false, of(
Expand All @@ -74,7 +74,7 @@ public void mcElementMissing() {

@Test
public void duplicateMcElementId() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
@MCElement(name="demo")
Expand All @@ -97,7 +97,7 @@ public class DemoElement2 {

@Test
public void duplicateMcElementName() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
@MCElement(name="demo", id="demo1")
Expand All @@ -123,7 +123,7 @@ class NoEnvelope {

@Test
public void topLevel() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
@MCElement(name="demo", noEnvelope=true)
Expand All @@ -139,7 +139,7 @@ public class DemoElement {

@Test
public void mixed() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
@MCElement(name="demo", noEnvelope=true, topLevel=false, mixed=true)
Expand All @@ -155,7 +155,7 @@ public class DemoElement {

@Test
public void noChildElements() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
@MCElement(name="demo", noEnvelope=true, topLevel=false)
Expand All @@ -171,7 +171,7 @@ public class DemoElement {

@Test
public void twoChildElements() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.annot.MCChildElement;
Expand All @@ -193,7 +193,7 @@ public void setChild2(List<DemoElement> s) {}

@Test
public void childIsNotAList() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.annot.MCChildElement;
Expand All @@ -212,7 +212,7 @@ public void setChild1(DemoElement s) {}

@Test
public void hasAttributes() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
import java.util.List;
Expand All @@ -233,7 +233,7 @@ public void setAttribute1(String s) {}

@Test
public void otherAttributes() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
import java.util.List;
Expand All @@ -255,7 +255,7 @@ public void setAttributes(Map<String, String> attributes) {}

@Test
public void textContent() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
import java.util.List;
Expand All @@ -280,7 +280,7 @@ public void setContent(String content) {}
class TextContent {
@Test
public void mcTextContentMissing() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
@MCElement(name="demo", mixed=true)
Expand All @@ -295,7 +295,7 @@ public class DemoElement {
}
@Test
public void mixedMissing() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
@MCElement(name="demo")
Expand All @@ -315,7 +315,7 @@ public void setContent(String content) {}

@Test
public void noConcreteChildMcElement() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
import java.util.List;
Expand All @@ -338,7 +338,7 @@ public abstract class AbstractDemoChildElement {

@Test
public void childNameNotUnique() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.*;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
public class SpringConfigurationXSDGeneratingAnnotationProcessorTest {

public static final String MC_MAIN_DEMO = """
resource META-INF/spring.handlers
http\\://membrane-soa.org/demo/1/=com.predic8.membrane.demo.config.spring.NamespaceHandler
---
resource META-INF/spring.schemas
http\\://membrane-soa.org/schemas/demo-1.xsd=com/predic8/membrane/demo/config/spring/router-conf.xsd
---
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCMain;
@MCMain(
Expand All @@ -45,7 +51,7 @@ public void init() {

@Test
public void simpleTest() {
List<JavaFileObject> sources = splitSources(MC_MAIN_DEMO + """
var sources = splitSources(MC_MAIN_DEMO + """
package com.predic8.membrane.demo;
import com.predic8.membrane.annot.MCElement;
@MCElement(name="demo")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,52 +18,131 @@
import org.hamcrest.collection.IsIterableContainingInAnyOrder;

import javax.tools.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import static java.util.List.of;
import static java.util.stream.StreamSupport.stream;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class CompilerHelper {
public static CompilerResult compile(Iterable<? extends JavaFileObject> sources, boolean logCompilerOutput) {
/**
* Compile the given source files.
*
* @param sourceFiles the source files to compile
* @param logCompilerOutput if true, print the compiler output to stderr
*/
public static CompilerResult compile(Iterable<? extends FileObject> sourceFiles, boolean logCompilerOutput) {
var javaSources = getJavaSources(sourceFiles);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
throw new IllegalStateException("No system Java compiler found. Run tests with a JDK, not a JRE.");
}
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
JavaFileManager fileManager = new CustomJavaFileManager(compiler.getStandardFileManager(diagnostics, null, null));
JavaFileManager fileManager = new LoggingInMemoryJavaFileManager(compiler.getStandardFileManager(diagnostics, null, null));

copyResourcesToOutput(getResources(sourceFiles), fileManager);

JavaCompiler.CompilationTask task = compiler.getTask(
null,
fileManager,
diagnostics,
List.of("-processor", "com.predic8.membrane.annot.SpringConfigurationXSDGeneratingAnnotationProcessor"),
of("-processor", "com.predic8.membrane.annot.SpringConfigurationXSDGeneratingAnnotationProcessor"),
null,
sources
javaSources
);

boolean success = task.call();

if (logCompilerOutput)
diagnostics.getDiagnostics().forEach(System.err::println);

return new CompilerResult(success, diagnostics);
return new CompilerResult(success, diagnostics, fileManager.getClassLoader(CLASS_OUTPUT));
}

/**
* Parse the given XML Spring config.
*/
public static void parse(CompilerResult cr, String xmlSpringConfig) {
ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();
try {
InMemoryClassLoader loaderA = (InMemoryClassLoader) cr.classLoader();
loaderA.defineOverlay(new OverlayInMemoryFile("/demo.xml", xmlSpringConfig));
CompositeClassLoader cl = new CompositeClassLoader(loaderA, CompilerHelper.class.getClassLoader());
Thread.currentThread().setContextClassLoader(cl);
Class<?> c = cl.loadClass("org.springframework.context.support.ClassPathXmlApplicationContext");
c.getConstructor(String.class).newInstance("/demo.xml");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
Thread.currentThread().setContextClassLoader(originalClassloader);
}
}

private static List<JavaFileObject> getJavaSources(Iterable<? extends FileObject> sources) {
return stream(sources.spliterator(), false)
.filter(i -> i instanceof JavaFileObject)
.map(i -> (JavaFileObject) i)
.toList();
}

private static List<OverlayInMemoryFile> getResources(Iterable<? extends FileObject> sources) {
return stream(sources.spliterator(), false)
.filter(i -> i instanceof OverlayInMemoryFile)
.map(i -> (OverlayInMemoryFile) i)
.toList();
}

private static void copyResourcesToOutput(List<? extends OverlayInMemoryFile> sources, JavaFileManager fileManager) {
sources.forEach(i -> {
PrintWriter pw = null;
try {
pw = new PrintWriter(fileManager.getFileForOutput(CLASS_OUTPUT, "", i.getName(), null)
.openWriter());
} catch (IOException e) {
throw new RuntimeException(e);
}
pw.write(i.getCharContent(true).toString());
pw.close();
});
}

public static List<JavaFileObject> splitSources(String sources) {
public static List<FileObject> splitSources(String sources) {
return Stream.of(sources.split("---"))
.filter(s -> !s.isBlank())
.map(CompilerHelper::toInMemoryJavaFile)
.map(CompilerHelper::toFile)
.toList();
}

private static FileObject toFile( String content) {
if (!content.trim().startsWith("resource"))
return toInMemoryJavaFile(content);

String[] parts;
while(true) {
parts = content.split("\n", 2);
if (parts.length != 2)
throw new RuntimeException("Invalid resource file: " + content + ". The resource is expected to have the format 'resource <path>\n<content>'.");
if (!parts[0].isEmpty())
break;
content = parts[1];
};

String name = parts[0].substring(9).trim();
return new OverlayInMemoryFile(name, parts[1]);
}

private static JavaFileObject toInMemoryJavaFile(String source) {
String pkg = extractPackage(source);
String cls = extractName(source);
return new InMemoryJavaFile(pkg + "." + cls, source);
return new OverlayInMemoryJavaFile(pkg + "." + cls, source);
}

private static String extractName(String source) {
Expand Down
Loading