From 534b5a66cb3fbf077dfbdd82526047a0f96e3d5f Mon Sep 17 00:00:00 2001 From: Renaud Pawlak Date: Sun, 14 Jun 2020 10:06:56 +0200 Subject: [PATCH] option + adapter method to sort class members (+associated test) --- .../org/jsweet/JSweetCommandLineLauncher.java | 14 ++++- .../org/jsweet/transpiler/JSweetOptions.java | 13 +++- .../jsweet/transpiler/JSweetTranspiler.java | 11 +++- .../transpiler/Java2TypeScriptTranslator.java | 18 +++++- .../transpiler/extension/PrinterAdapter.java | 14 +++++ .../transpiler/model/ExecutableElement.java | 7 +++ .../transpiler/model/ExtendedElement.java | 5 ++ .../model/ExtendedElementFactory.java | 8 +++ .../transpiler/model/VariableElement.java | 7 +++ .../support/ExecutableElementSupport.java | 19 ++++++ .../model/support/ExtendedElementSupport.java | 6 ++ .../model/support/VariableElementSupport.java | 18 ++++++ .../test/transpiler/ExtensionTests.java | 60 +++++++++++++++++++ .../java/source/extension/ToBeSorted.java | 11 ++++ 14 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 transpiler/src/main/java/org/jsweet/transpiler/model/ExecutableElement.java create mode 100644 transpiler/src/main/java/org/jsweet/transpiler/model/VariableElement.java create mode 100644 transpiler/src/main/java/org/jsweet/transpiler/model/support/ExecutableElementSupport.java create mode 100644 transpiler/src/main/java/org/jsweet/transpiler/model/support/VariableElementSupport.java create mode 100644 transpiler/src/test/java/source/extension/ToBeSorted.java diff --git a/transpiler/src/main/java/org/jsweet/JSweetCommandLineLauncher.java b/transpiler/src/main/java/org/jsweet/JSweetCommandLineLauncher.java index 10e0c2cd..d170c2a2 100644 --- a/transpiler/src/main/java/org/jsweet/JSweetCommandLineLauncher.java +++ b/transpiler/src/main/java/org/jsweet/JSweetCommandLineLauncher.java @@ -615,7 +615,16 @@ public class JSweetCommandLineLauncher { "but it may result into runtime static initialization issues (cross-class " + "static dependencies)."); jsap.registerParameter(switchArg); - + + // Sort class members + switchArg = new Switch(JSweetOptions.sortClassMembers); + switchArg.setLongFlag(JSweetOptions.sortClassMembers); + switchArg.setHelp( + "If enabled, class members are sorted using " + + "PrinterAdapter#getClassMemberComparator(), to be overloaded by the user to "+ + "implement the desired order."); + jsap.registerParameter(switchArg); + return jsap; } @@ -848,6 +857,9 @@ public class JSweetCommandLineLauncher { if (jsapArgs.userSpecified(JSweetOptions.disableStaticsLazyInitialization)) { transpiler.setLazyInitializedStatics(!jsapArgs.getBoolean(JSweetOptions.disableStaticsLazyInitialization)); } + if (jsapArgs.userSpecified(JSweetOptions.sortClassMembers)) { + transpiler.setSortClassMembers(jsapArgs.getBoolean(JSweetOptions.sortClassMembers)); + } if (tsOutputDir != null) { transpiler.setTsOutputDir(tsOutputDir); diff --git a/transpiler/src/main/java/org/jsweet/transpiler/JSweetOptions.java b/transpiler/src/main/java/org/jsweet/transpiler/JSweetOptions.java index 6ae818d0..7ab49df2 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/JSweetOptions.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/JSweetOptions.java @@ -139,13 +139,18 @@ public interface JSweetOptions { */ String classpath = "classpath"; + /** + * Constant string for the 'sortClassMembers' option. + */ + String sortClassMembers = "sortClassMembers"; + /** * All the supported options (used to report non-blocking errors when options do not exist). */ String[] options = { bundle, noRootDirectories, sourceMap, module, encoding, outEncoding, enableAssertions, declaration, tsOnly, ignoreDefinitions, ignoreJavaErrors, header, disableSinglePrecisionFloats, disableStaticsLazyInitialization, targetVersion, tsout, dtsout, jsout, candiesJsOut, moduleResolution, - extraSystemPath, useSingleQuotesForStringLiterals, nonEnumerableTransients, classpath }; + extraSystemPath, useSingleQuotesForStringLiterals, nonEnumerableTransients, classpath, sortClassMembers }; /** * Returns the configuration from the configuration file. @@ -364,4 +369,10 @@ public interface JSweetOptions { * JavaScript properties. */ boolean isNonEnumerableTransients(); + + /** + * If true, class members are sorted using + * {@link PrinterAdapter#getClassMemberComparator()}. + */ + boolean isSortClassMembers(); } \ No newline at end of file diff --git a/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java b/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java index 32a9c113..d8451eff 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java @@ -242,6 +242,7 @@ public class JSweetTranspiler implements JSweetOptions { private boolean lazyInitializedStatics = true; private boolean useSingleQuotesForStringLiterals = false; private boolean nonEnumerableTransients = false; + private boolean sortClassMembers = false; private ArrayList adapters = new ArrayList<>(); private File configurationFile; @@ -1944,6 +1945,14 @@ public class JSweetTranspiler implements JSweetOptions { public void setNonEnumerableTransients(boolean nonEnumerableTransients) { this.nonEnumerableTransients = nonEnumerableTransients; } - + + @Override + public boolean isSortClassMembers() { + return this.sortClassMembers; + } + + public void setSortClassMembers(boolean sortClassMembers) { + this.sortClassMembers = sortClassMembers; + } } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java index b74c2708..f3cec5c1 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java @@ -1717,7 +1717,23 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { boolean hasUninitializedFields = false; - for (JCTree def : classdecl.defs) { + List defs = classdecl.defs; + if (context.options.isSortClassMembers()) { + List memberDefs = defs.stream() + .filter(t -> (t instanceof JCMethodDecl || t instanceof JCVariableDecl)) + .sorted((t1, t2) -> getAdapter().getClassMemberComparator().compare( + ExtendedElementFactory.INSTANCE.create(t1), ExtendedElementFactory.INSTANCE.create(t2))) + .collect(Collectors.toList()); + defs = new ArrayList<>(); + defs.addAll(memberDefs); + for (JCTree def : classdecl.defs) { + if (!defs.contains(def)) { + defs.add(def); + } + } + } + + for (JCTree def : defs) { if (getScope().interfaceScope && ((def instanceof JCMethodDecl && ((JCMethodDecl) def).sym.isStatic()) || (def instanceof JCVariableDecl && ((JCVariableDecl) def).sym.isStatic()))) { // static interface members are printed in a namespace diff --git a/transpiler/src/main/java/org/jsweet/transpiler/extension/PrinterAdapter.java b/transpiler/src/main/java/org/jsweet/transpiler/extension/PrinterAdapter.java index 0af742c6..a90fae6c 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/extension/PrinterAdapter.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/extension/PrinterAdapter.java @@ -19,6 +19,7 @@ package org.jsweet.transpiler.extension; import java.lang.annotation.Annotation; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -1204,4 +1205,17 @@ public class PrinterAdapter { return printer.isInlinedExpression(((ExtendedElementSupport) element).getTree()); } + /** + * Returns a comparator that will control the order of class members for output + * (default will keep order of appearance in the source code). + */ + public Comparator getClassMemberComparator() { + return new Comparator() { + @Override + public int compare(ExtendedElement e1, ExtendedElement e2) { + return e1.getStartSourcePosition() - e2.getStartSourcePosition(); + } + }; + } + } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/model/ExecutableElement.java b/transpiler/src/main/java/org/jsweet/transpiler/model/ExecutableElement.java new file mode 100644 index 00000000..c8f303e5 --- /dev/null +++ b/transpiler/src/main/java/org/jsweet/transpiler/model/ExecutableElement.java @@ -0,0 +1,7 @@ +package org.jsweet.transpiler.model; + +public interface ExecutableElement extends ExtendedElement { + + javax.lang.model.element.ExecutableElement getStandardElement(); + +} diff --git a/transpiler/src/main/java/org/jsweet/transpiler/model/ExtendedElement.java b/transpiler/src/main/java/org/jsweet/transpiler/model/ExtendedElement.java index 017b42ff..3227e22a 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/model/ExtendedElement.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/model/ExtendedElement.java @@ -69,4 +69,9 @@ public interface ExtendedElement { */ boolean isStringLiteral(); + /** + * Gets the start position of the element in the source code. + */ + int getStartSourcePosition(); + } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/model/ExtendedElementFactory.java b/transpiler/src/main/java/org/jsweet/transpiler/model/ExtendedElementFactory.java index 1299c3ff..1720a211 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/model/ExtendedElementFactory.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/model/ExtendedElementFactory.java @@ -25,6 +25,7 @@ import org.jsweet.transpiler.model.support.AssignmentElementSupport; import org.jsweet.transpiler.model.support.BinaryOperatorElementSupport; import org.jsweet.transpiler.model.support.CaseElementSupport; import org.jsweet.transpiler.model.support.CompilationUnitElementSupport; +import org.jsweet.transpiler.model.support.ExecutableElementSupport; import org.jsweet.transpiler.model.support.ExtendedElementSupport; import org.jsweet.transpiler.model.support.ForeachLoopElementSupport; import org.jsweet.transpiler.model.support.IdentifierElementSupport; @@ -35,6 +36,7 @@ import org.jsweet.transpiler.model.support.NewArrayElementSupport; import org.jsweet.transpiler.model.support.NewClassElementSupport; import org.jsweet.transpiler.model.support.UnaryOperatorElementSupport; import org.jsweet.transpiler.model.support.VariableAccessElementSupport; +import org.jsweet.transpiler.model.support.VariableElementSupport; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCArrayAccess; @@ -47,10 +49,12 @@ import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.tree.JCTree.JCLiteral; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCUnary; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; /** * A factory to create extended elements. It defines an overloaded create method @@ -91,6 +95,10 @@ public class ExtendedElementFactory { return null; } switch (tree.getTag()) { + case METHODDEF: + return new ExecutableElementSupport((JCMethodDecl) tree); + case VARDEF: + return new VariableElementSupport((JCVariableDecl) tree); case APPLY: return new MethodInvocationElementSupport((JCMethodInvocation) tree); case SELECT: diff --git a/transpiler/src/main/java/org/jsweet/transpiler/model/VariableElement.java b/transpiler/src/main/java/org/jsweet/transpiler/model/VariableElement.java new file mode 100644 index 00000000..0c6eac67 --- /dev/null +++ b/transpiler/src/main/java/org/jsweet/transpiler/model/VariableElement.java @@ -0,0 +1,7 @@ +package org.jsweet.transpiler.model; + +public interface VariableElement extends ExtendedElement { + + javax.lang.model.element.VariableElement getStandardElement(); + +} diff --git a/transpiler/src/main/java/org/jsweet/transpiler/model/support/ExecutableElementSupport.java b/transpiler/src/main/java/org/jsweet/transpiler/model/support/ExecutableElementSupport.java new file mode 100644 index 00000000..2f7a3c70 --- /dev/null +++ b/transpiler/src/main/java/org/jsweet/transpiler/model/support/ExecutableElementSupport.java @@ -0,0 +1,19 @@ +package org.jsweet.transpiler.model.support; + +import org.jsweet.transpiler.model.ExecutableElement; + +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; + +public class ExecutableElementSupport extends ExtendedElementSupport implements ExecutableElement { + + + public ExecutableElementSupport(JCMethodDecl tree) { + super(tree); + } + + @Override + public javax.lang.model.element.ExecutableElement getStandardElement() { + return tree.sym; + } + +} diff --git a/transpiler/src/main/java/org/jsweet/transpiler/model/support/ExtendedElementSupport.java b/transpiler/src/main/java/org/jsweet/transpiler/model/support/ExtendedElementSupport.java index b251620a..e21f5304 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/model/support/ExtendedElementSupport.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/model/support/ExtendedElementSupport.java @@ -92,4 +92,10 @@ public class ExtendedElementSupport implements ExtendedElement public boolean isStringLiteral() { return getTree().getKind() == Kind.STRING_LITERAL; } + + @Override + public int getStartSourcePosition() { + return getTree().getStartPosition(); + } + } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/model/support/VariableElementSupport.java b/transpiler/src/main/java/org/jsweet/transpiler/model/support/VariableElementSupport.java new file mode 100644 index 00000000..c7f99e1f --- /dev/null +++ b/transpiler/src/main/java/org/jsweet/transpiler/model/support/VariableElementSupport.java @@ -0,0 +1,18 @@ +package org.jsweet.transpiler.model.support; + +import org.jsweet.transpiler.model.VariableElement; + +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; + +public class VariableElementSupport extends ExtendedElementSupport implements VariableElement { + + public VariableElementSupport(JCVariableDecl tree) { + super(tree); + } + + @Override + public javax.lang.model.element.VariableElement getStandardElement() { + return tree.sym; + } + +} diff --git a/transpiler/src/test/java/org/jsweet/test/transpiler/ExtensionTests.java b/transpiler/src/test/java/org/jsweet/test/transpiler/ExtensionTests.java index c7f205f4..7c0b9447 100644 --- a/transpiler/src/test/java/org/jsweet/test/transpiler/ExtensionTests.java +++ b/transpiler/src/test/java/org/jsweet/test/transpiler/ExtensionTests.java @@ -2,10 +2,12 @@ package org.jsweet.test.transpiler; import java.io.IOException; import java.math.BigDecimal; +import java.util.Comparator; import java.util.EventObject; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; @@ -29,6 +31,7 @@ import org.jsweet.transpiler.extension.Java2TypeScriptAdapter; import org.jsweet.transpiler.extension.MapAdapter; import org.jsweet.transpiler.extension.PrinterAdapter; import org.jsweet.transpiler.extension.RemoveJavaDependenciesAdapter; +import org.jsweet.transpiler.model.ExtendedElement; import org.jsweet.transpiler.model.ImportElement; import org.junit.Assert; import org.junit.Test; @@ -41,6 +44,7 @@ import source.extension.HelloWorldDto; import source.extension.HelloWorldService; import source.extension.IAddNumber; import source.extension.Maps; +import source.extension.ToBeSorted; import source.extension.UseOfGlobalVariable; class TestFactory extends JSweetFactory { @@ -106,6 +110,37 @@ class HelloWorldAdapter extends PrinterAdapter { } } +class SortAdapter extends PrinterAdapter { + + public SortAdapter(PrinterAdapter parentAdapter) { + super(parentAdapter); + } + + @Override + public Comparator getClassMemberComparator() { + + Comparator defaultComparator = super.getClassMemberComparator(); + + return new Comparator() { + @Override + public int compare(ExtendedElement e1, ExtendedElement e2) { + if(e1 instanceof org.jsweet.transpiler.model.ExecutableElement && + e2 instanceof org.jsweet.transpiler.model.ExecutableElement) { + ExecutableElement se1 = ((org.jsweet.transpiler.model.ExecutableElement)e1).getStandardElement(); + ExecutableElement se2 = ((org.jsweet.transpiler.model.ExecutableElement)e2).getStandardElement(); + if (se1.getModifiers().contains(Modifier.STATIC) && !se2.getModifiers().contains(Modifier.STATIC)) { + return -1; + } else if (se2.getModifiers().contains(Modifier.STATIC) && !se1.getModifiers().contains(Modifier.STATIC)) { + return 1; + } + } + return defaultComparator.compare(e1, e2); + } + }; + } + +} + class JaxRSStubAdapter extends PrinterAdapter { public JaxRSStubAdapter(PrinterAdapter parent) { @@ -245,6 +280,31 @@ public class ExtensionTests extends AbstractTest { Assert.assertFalse(generatedCode.contains("date : Date")); } + @Test + public void testSortAdapter() throws IOException { + TranspilerTestRunner transpilerTest = new TranspilerTestRunner(getCurrentTestOutDir(), new JSweetFactory()); + SourceFile f = getSourceFile(ToBeSorted.class); + transpilerTest.transpile(logHandler -> { + logHandler.assertNoProblems(); + }, f); + String generatedCode = FileUtils.readFileToString(f.getTsFile()); + Assert.assertTrue(generatedCode.indexOf("myStaticMethod") > generatedCode.indexOf("myMethod")); + transpilerTest = new TranspilerTestRunner(getCurrentTestOutDir(), new JSweetFactory() { + + @Override + public PrinterAdapter createAdapter(JSweetContext context) { + return new SortAdapter(super.createAdapter(context)); + } + }); + transpilerTest.getTranspiler().setSortClassMembers(true); + f = getSourceFile(ToBeSorted.class); + transpilerTest.transpile(logHandler -> { + logHandler.assertNoProblems(); + }, f); + generatedCode = FileUtils.readFileToString(f.getTsFile()); + Assert.assertTrue(generatedCode.indexOf("myStaticMethod") < generatedCode.indexOf("myMethod")); + } + @Test public void testDisallowGlobalVariablesAdapter() { TranspilerTestRunner transpilerTest = new TranspilerTestRunner(getCurrentTestOutDir(), new JSweetFactory() { diff --git a/transpiler/src/test/java/source/extension/ToBeSorted.java b/transpiler/src/test/java/source/extension/ToBeSorted.java new file mode 100644 index 00000000..6a952ffe --- /dev/null +++ b/transpiler/src/test/java/source/extension/ToBeSorted.java @@ -0,0 +1,11 @@ +package source.extension; + +public class ToBeSorted { + + public void myMethod() { + } + + static void myStaticMethod() { + } + +}