option + adapter method to sort class members (+associated test)

This commit is contained in:
Renaud Pawlak 2020-06-14 10:06:56 +02:00
parent 9088aa0a52
commit 534b5a66cb
14 changed files with 207 additions and 4 deletions

View File

@ -616,6 +616,15 @@ public class JSweetCommandLineLauncher {
"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);

View File

@ -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();
}

View File

@ -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<String> adapters = new ArrayList<>();
private File configurationFile;
@ -1945,5 +1946,13 @@ public class JSweetTranspiler implements JSweetOptions {
this.nonEnumerableTransients = nonEnumerableTransients;
}
@Override
public boolean isSortClassMembers() {
return this.sortClassMembers;
}
public void setSortClassMembers(boolean sortClassMembers) {
this.sortClassMembers = sortClassMembers;
}
}

View File

@ -1717,7 +1717,23 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter {
boolean hasUninitializedFields = false;
List<JCTree> defs = classdecl.defs;
if (context.options.isSortClassMembers()) {
List<JCTree> 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

View File

@ -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<ExtendedElement> getClassMemberComparator() {
return new Comparator<ExtendedElement>() {
@Override
public int compare(ExtendedElement e1, ExtendedElement e2) {
return e1.getStartSourcePosition() - e2.getStartSourcePosition();
}
};
}
}

View File

@ -0,0 +1,7 @@
package org.jsweet.transpiler.model;
public interface ExecutableElement extends ExtendedElement {
javax.lang.model.element.ExecutableElement getStandardElement();
}

View File

@ -69,4 +69,9 @@ public interface ExtendedElement {
*/
boolean isStringLiteral();
/**
* Gets the start position of the element in the source code.
*/
int getStartSourcePosition();
}

View File

@ -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:

View File

@ -0,0 +1,7 @@
package org.jsweet.transpiler.model;
public interface VariableElement extends ExtendedElement {
javax.lang.model.element.VariableElement getStandardElement();
}

View File

@ -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<JCMethodDecl> implements ExecutableElement {
public ExecutableElementSupport(JCMethodDecl tree) {
super(tree);
}
@Override
public javax.lang.model.element.ExecutableElement getStandardElement() {
return tree.sym;
}
}

View File

@ -92,4 +92,10 @@ public class ExtendedElementSupport<T extends JCTree> implements ExtendedElement
public boolean isStringLiteral() {
return getTree().getKind() == Kind.STRING_LITERAL;
}
@Override
public int getStartSourcePosition() {
return getTree().getStartPosition();
}
}

View File

@ -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<JCVariableDecl> implements VariableElement {
public VariableElementSupport(JCVariableDecl tree) {
super(tree);
}
@Override
public javax.lang.model.element.VariableElement getStandardElement() {
return tree.sym;
}
}

View File

@ -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<ExtendedElement> getClassMemberComparator() {
Comparator<ExtendedElement> defaultComparator = super.getClassMemberComparator();
return new Comparator<ExtendedElement>() {
@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() {

View File

@ -0,0 +1,11 @@
package source.extension;
public class ToBeSorted {
public void myMethod() {
}
static void myStaticMethod() {
}
}