mirror of
https://github.com/cincheo/jsweet.git
synced 2025-12-15 15:29:22 +00:00
Merge pull request #631 from cincheo/feature/613_Optional_support_jsweet2
fix #613 Optional support
This commit is contained in:
commit
b61de98c5a
@ -48,6 +48,7 @@ import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -68,6 +69,7 @@ import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.PackageSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.TypeSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.VarSymbol;
|
||||
import com.sun.tools.javac.model.JavacElements;
|
||||
import com.sun.tools.javac.code.Symtab;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Types;
|
||||
@ -1562,30 +1564,6 @@ public class JSweetContext extends Context {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Gets the value of the given annotation property.
|
||||
// *
|
||||
// * @param annotation
|
||||
// * the annotation
|
||||
// * @param propertyName
|
||||
// * the name of the annotation property to get the value of
|
||||
// * @param defaultValue
|
||||
// * the value to return if not found
|
||||
// */
|
||||
// @SuppressWarnings("unchecked")
|
||||
// private static <T> T getAnnotationValue(AnnotationMirror annotation,
|
||||
// String propertyName, T defaultValue) {
|
||||
// for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue>
|
||||
// annoProperty : annotation
|
||||
// .getElementValues().entrySet()) {
|
||||
// if
|
||||
// (annoProperty.getKey().getSimpleName().toString().equals(propertyName)) {
|
||||
// return (T) annoProperty.getValue().getValue();
|
||||
// }
|
||||
// }
|
||||
// return defaultValue;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Gets the symbol's annotation that correspond to the given annotation type
|
||||
* name if exists.
|
||||
@ -1599,19 +1577,6 @@ public class JSweetContext extends Context {
|
||||
return null;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Gets the annotation tree that matches the given type name.
|
||||
// */
|
||||
// private static JCAnnotation getAnnotation(List<JCAnnotation> annotations,
|
||||
// String annotationType) {
|
||||
// for (JCAnnotation a : annotations) {
|
||||
// if (annotationType.equals(a.type.toString())) {
|
||||
// return a;
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Grabs the names of all the support interfaces in the class and interface
|
||||
* hierarchy.
|
||||
@ -1947,4 +1912,7 @@ public class JSweetContext extends Context {
|
||||
}
|
||||
}
|
||||
|
||||
public Elements elements() {
|
||||
return JavacElements.instance(this);
|
||||
}
|
||||
}
|
||||
@ -1315,11 +1315,8 @@ public class Java2TypeScriptAdapter extends PrinterAdapter {
|
||||
print(")");
|
||||
}
|
||||
} else {
|
||||
printMacroName(targetMethodName);
|
||||
print("(<any>((o1: any, o2: any) => { if(o1 && o1.equals) { return o1.equals(o2); } else { return o1 === o2; } })(");
|
||||
printTarget(invocationElement.getTargetExpression()).print(",")
|
||||
.print(invocationElement.getArgument(0));
|
||||
print("))");
|
||||
printDefaultEquals(invocationElement.getTargetExpression(),
|
||||
invocationElement.getArgument(0));
|
||||
}
|
||||
return true;
|
||||
case "compareTo":
|
||||
@ -1413,6 +1410,12 @@ public class Java2TypeScriptAdapter extends PrinterAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
protected void printDefaultEquals(ExtendedElement left, ExtendedElement right) {
|
||||
print("(<any>((o1: any, o2: any) => o1 && o1.equals ? o1.equals(o2) : o1 === o2)(");
|
||||
printTarget(left).print(",").print(right);
|
||||
print("))");
|
||||
}
|
||||
|
||||
protected void printFunctionalInvocation2(ExtendedElement target, String functionName,
|
||||
List<ExtendedElement> arguments) {
|
||||
print("((target => (target['" + functionName + "'] === undefined)?target:target['" + functionName + "'])(")
|
||||
@ -1677,4 +1680,5 @@ public class Java2TypeScriptAdapter extends PrinterAdapter {
|
||||
return super.substituteForEachLoop(foreachLoop, targetHasLength, indexVarName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -52,6 +52,7 @@ import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
@ -68,7 +69,6 @@ import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import org.jsweet.transpiler.JSweetContext;
|
||||
import org.jsweet.transpiler.Java2TypeScriptTranslator;
|
||||
import org.jsweet.transpiler.ModuleKind;
|
||||
@ -85,6 +85,7 @@ import org.jsweet.transpiler.model.VariableAccessElement;
|
||||
import org.jsweet.transpiler.model.support.ForeachLoopElementSupport;
|
||||
import org.jsweet.transpiler.util.Util;
|
||||
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
|
||||
import com.sun.tools.javac.tree.JCTree.JCLiteral;
|
||||
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
|
||||
@ -168,11 +169,29 @@ public class RemoveJavaDependenciesAdapter extends Java2TypeScriptAdapter {
|
||||
|
||||
extTypesMapping.put(Method.class.getName(), "{ owner: any, name: string, fn : Function }");
|
||||
|
||||
addTypeMapping(
|
||||
(typeTree,
|
||||
name) -> name.startsWith("java.")
|
||||
&& types().isSubtype(typeTree.getType(), util().getType(Throwable.class)) ? "Error"
|
||||
: null);
|
||||
addTypeMapping((ExtendedElement typeElement, String qualifiedName) -> {
|
||||
TypeMirror type = typeElement.getType();
|
||||
if (!qualifiedName.startsWith("java.") || type == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Throwable
|
||||
if (types().isSubtype(type, util().getType(Throwable.class))) {
|
||||
return "Error";
|
||||
}
|
||||
|
||||
// Optional
|
||||
if (types().isSubtype(types().erasure(type), types().erasure(util().getType(Optional.class)))
|
||||
&& type instanceof DeclaredType) {
|
||||
List<? extends TypeMirror> typeArgs = ((DeclaredType) type).getTypeArguments();
|
||||
if (typeArgs.size() > 0) {
|
||||
return getMappedType(typeArgs.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
// TODO: use standard API
|
||||
// addTypeMapping((typeTree,
|
||||
// name) -> typeTree.asType() instanceof DeclaredType
|
||||
@ -315,6 +334,11 @@ public class RemoveJavaDependenciesAdapter extends Java2TypeScriptAdapter {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "java.util.Optional":
|
||||
if (substituteMethodInvocationOnOptional(invocation, targetMethodName, delegate)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "java.lang.StringBuffer":
|
||||
case "java.lang.StringBuilder":
|
||||
if (substituteMethodInvocationOnStringBuilder(invocation, targetMethodName, delegate)) {
|
||||
@ -493,6 +517,68 @@ public class RemoveJavaDependenciesAdapter extends Java2TypeScriptAdapter {
|
||||
return super.substituteMethodInvocation(invocation);
|
||||
}
|
||||
|
||||
|
||||
private boolean substituteMethodInvocationOnOptional(MethodInvocationElement invocation, String targetMethodName,
|
||||
boolean delegate) {
|
||||
switch (targetMethodName) {
|
||||
case "of":
|
||||
print(invocation.getArgument(0));
|
||||
return true;
|
||||
case "empty":
|
||||
print("null");
|
||||
return true;
|
||||
case "ofNullable":
|
||||
print(invocation.getArgument(0));
|
||||
return true;
|
||||
case "equals":
|
||||
printDefaultEquals(invocation.getTargetExpression(), invocation.getArgument(0));
|
||||
return true;
|
||||
case "filter":
|
||||
printMacroName(targetMethodName);
|
||||
print("((o, condition) => condition(o) ? o : null)(").print(invocation.getTargetExpression()).print(",")
|
||||
.print(invocation.getArgument(0)).print(")");
|
||||
return true;
|
||||
case "orElse":
|
||||
printMacroName(targetMethodName);
|
||||
print("((v, v2) => v == null ? v2 : v)(").print(invocation.getTargetExpression()).print(",")
|
||||
.print(invocation.getArgument(0)).print(")");
|
||||
return true;
|
||||
case "orElseGet":
|
||||
case "or":
|
||||
printMacroName(targetMethodName);
|
||||
print("((v, get) => v == null ? get() : v)(").print(invocation.getTargetExpression()).print(",")
|
||||
.print(invocation.getArgument(0)).print(")");
|
||||
return true;
|
||||
case "ifPresent":
|
||||
printMacroName(targetMethodName);
|
||||
print("((v, action) => v != null ? action(v) : null)(").print(invocation.getTargetExpression()).print(",")
|
||||
.print(invocation.getArgument(0)).print(")");
|
||||
return true;
|
||||
case "ifPresentOrElse":
|
||||
printMacroName(targetMethodName);
|
||||
print("((v, action, fallbackAction) => v != null ? action(v) : fallbackAction())(")
|
||||
.print(invocation.getTargetExpression()).print(",").print(invocation.getArgument(0)).print(",")
|
||||
.print(invocation.getArgument(1)).print(")");
|
||||
return true;
|
||||
case "map":
|
||||
case "flatMap":
|
||||
printMacroName(targetMethodName);
|
||||
print("(").print(invocation.getArgument(0)).print(")(").print(invocation.getTargetExpression()).print(")");
|
||||
return true;
|
||||
case "orElseThrow":
|
||||
printMacroName(targetMethodName);
|
||||
if (invocation.getArgumentCount() > 0) {
|
||||
print("((v, throwError) => { if (v == null) throwError(); return v; })(")
|
||||
.print(invocation.getTargetExpression()).print(",").print(invocation.getArgument(0)).print(")");
|
||||
} else {
|
||||
print("(v => { if (v == null) throw new Error('value is null'); return v; })(")
|
||||
.print(invocation.getTargetExpression()).print(")");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean substituteMethodInvocationOnObjects(MethodInvocationElement invocation, String targetMethodName,
|
||||
boolean delegate) {
|
||||
switch (targetMethodName) {
|
||||
|
||||
@ -33,7 +33,6 @@ public interface Util {
|
||||
* Gets the type from an existing runtime class when possible (return null
|
||||
* when the type cannot be found in the compiler's symbol table).
|
||||
* <p/>
|
||||
* This method only looks up well-known Java types.
|
||||
*/
|
||||
TypeMirror getType(Class<?> clazz);
|
||||
|
||||
|
||||
@ -125,6 +125,12 @@ public class UtilSupport implements Util {
|
||||
case "java.util.List":
|
||||
return context.symtab.listType;
|
||||
}
|
||||
|
||||
TypeElement typeElement = context.elements().getTypeElement(clazz.getName());
|
||||
if (typeElement != null) {
|
||||
return typeElement.asType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
1
transpiler/src/test/java/.gitignore
vendored
Normal file
1
transpiler/src/test/java/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/source/
|
||||
@ -40,6 +40,7 @@ import source.api.ForeachIteration;
|
||||
import source.api.J4TSInvocations;
|
||||
import source.api.JdkInvocations;
|
||||
import source.api.Numbers;
|
||||
import source.api.Optionals;
|
||||
import source.api.PrimitiveInstantiation;
|
||||
import source.api.PromisesAsyncAwait;
|
||||
import source.api.QualifiedInstantiation;
|
||||
@ -156,7 +157,6 @@ public class ApiTests extends AbstractTest {
|
||||
assertTrue(result.get("subMethod2"));
|
||||
assertEquals("subMethod2", result.get("resultSubMethod2"));
|
||||
|
||||
|
||||
}, getSourceFile(PromisesAsyncAwait.class));
|
||||
}
|
||||
|
||||
@ -281,12 +281,15 @@ public class ApiTests extends AbstractTest {
|
||||
eval(ModuleKind.none, (logHandler, r) -> {
|
||||
logHandler.assertNoProblems();
|
||||
System.out.println("result = " + r.get("localeString"));
|
||||
assertTrue(asList(
|
||||
"1/1/2020, 1:00:00 AM",
|
||||
"2020-1-1 1:00:00 AM").contains(
|
||||
r.get("localeString")));
|
||||
assertTrue(asList("1/1/2020, 1:00:00 AM", "2020-1-1 1:00:00 AM").contains(r.get("localeString")));
|
||||
|
||||
}, getSourceFile(Dates.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptional() {
|
||||
eval((logHandler, result) -> {
|
||||
logHandler.assertNoProblems();
|
||||
}, getSourceFile(Optionals.class));
|
||||
}
|
||||
}
|
||||
|
||||
103
transpiler/src/test/java/source/api/Optionals.java
Normal file
103
transpiler/src/test/java/source/api/Optionals.java
Normal file
@ -0,0 +1,103 @@
|
||||
package source.api;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import def.js.Error;
|
||||
|
||||
public class Optionals {
|
||||
private static final String PREFERRED_VALUE = "c'est la vie";
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
}
|
||||
|
||||
static void executePresentTests() {
|
||||
Optional<String> o = Optional.of(PREFERRED_VALUE);
|
||||
assertContains(o, PREFERRED_VALUE);
|
||||
|
||||
o = Optional.ofNullable(PREFERRED_VALUE);
|
||||
assertContains(o, PREFERRED_VALUE);
|
||||
|
||||
assert !o.equals(Optional.ofNullable(null));
|
||||
|
||||
assertContains(o.filter((current) -> current == PREFERRED_VALUE), PREFERRED_VALUE);
|
||||
assertEmpty(o.filter((current) -> current == "XXXC'est la vie"));
|
||||
|
||||
assertContains(o.flatMap(current -> Optional.of("new")), "new");
|
||||
assertContains(o.map(current -> "new2"), "new2");
|
||||
|
||||
String v = o.orElse("defaultVal");
|
||||
assert v == PREFERRED_VALUE;
|
||||
v = o.orElseGet(() -> "defaultVal2");
|
||||
assert v == PREFERRED_VALUE;
|
||||
|
||||
String[] modifiedForPresent = { "", "" };
|
||||
o.ifPresent(__ -> modifiedForPresent[0] = "modified");
|
||||
|
||||
assert modifiedForPresent[0] == "modified";
|
||||
assert modifiedForPresent[1] == "modified";
|
||||
|
||||
// TODO
|
||||
// Object[] valsAfterStream = o.stream().toArray();
|
||||
}
|
||||
|
||||
static void executeAbsentTests() {
|
||||
Optional<String> o = Optional.empty();
|
||||
assertEmpty(o);
|
||||
|
||||
o = Optional.ofNullable(null);
|
||||
assertEmpty(o);
|
||||
|
||||
assert o.equals(Optional.ofNullable(null));
|
||||
|
||||
boolean caught = false;
|
||||
try {
|
||||
o = Optional.of(null);
|
||||
} catch (Error e) {
|
||||
caught = true;
|
||||
}
|
||||
assert caught;
|
||||
|
||||
String v = o.orElse("defaultVal");
|
||||
assert v == "defaultVal";
|
||||
v = o.orElseGet(() -> "defaultVal2");
|
||||
assert v == "defaultVal2";
|
||||
|
||||
final String[] modified = { "", "" };
|
||||
o.ifPresent(__ -> modified[0] = "what");
|
||||
|
||||
assert modified[0] == "";
|
||||
assert modified[1] == "valid";
|
||||
}
|
||||
|
||||
static <T> void assertContains(Optional<T> o, T value) {
|
||||
assert o.get() == value;
|
||||
assert o.isPresent();
|
||||
|
||||
o.orElseThrow(() -> new MyMissingError());
|
||||
}
|
||||
|
||||
static void assertEmpty(Optional<?> o) {
|
||||
assert !o.isPresent();
|
||||
assert o != null;
|
||||
|
||||
boolean caught = false;
|
||||
try {
|
||||
o.get();
|
||||
} catch (Error e) {
|
||||
caught = true;
|
||||
}
|
||||
assert caught;
|
||||
|
||||
caught = false;
|
||||
try {
|
||||
o.orElseThrow(() -> new MyMissingError());
|
||||
} catch (MyMissingError e) {
|
||||
caught = true;
|
||||
}
|
||||
assert caught;
|
||||
}
|
||||
}
|
||||
|
||||
class MyMissingError extends def.js.Error {
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user