转载

jsweet中英文文档,java代码转js代码

这个工具可以将java代码转为js代码,从而可以使用java编写前端代码

如果排版看着费劲可以下载下方html,打开html后使用google翻译

JSweet语言规范

版本:2.x(快照)

作者:Renaud Pawlak

作者助理:Louis Grignon

JSweet JavaDoc API:http://www.jsweet.org/core-api-javadoc/

注意:此降价是从Latex源文件自动生成的。不要直接修改。

内容

基本概念

核心类型和对象

接口

无类型对象(地图)

枚举

全局

可选参数和重载

桥接外部JavaScript元素

例子

写定义规则(又名桥梁)

无法访问

混入

从现有的TypeScript定义生成JSweet糖果

辅助类型

功能类型

对象类型

字符串类型

元组类型

联盟类型

交叉类型

语义

主要方法

初始化器

数组初始化和分配

异步编程

姓名冲突

测试对象的类型

lambda表达式中的变量作用域

这个范围

打包

使用您的文件,无需任何包装

为浏览器创建捆绑包

包装模块

根包

包装JSweet jar(糖果)

扩展转换器

核心注释

集中注释 jsweetconfig.json

使用适配器进行编程调整

扩展示例

附录1:JSweet转换器选项

附录2:包装和静态行为

调用main方法时

静态和继承依赖项

基本概念

本节介绍JSweet语言的基本概念。必须记住,JSweet作为Java-to-JavaScript转换器,是编译时Java的扩展,并在运行时作为JavaScript执行。JSweet旨在通过尽可能多地尊重Java语义来实现Java和JavaScript之间的权衡,但不会失去与JavaScript的互操作性。因此,在某种程度上,JSweet可以看作是Java和JavaScript之间的融合,试图用一种独特且一致的语言来充分利用这两个世界。在某些情况下,很难充分利用这两个世界,JSweet可以提供方便实用的选择。

因为JSweet是一个开放的JavaScript转换器,所以用户可以毫不费力地调整JavaScript生成,从而做出除默认选择之外的其他选择来将Java映射到JavaScript。例如,如果JSweet实现Java映射的方式不适合您的上下文或用例,则可以编写JSweet扩展以覆盖默认策略。第6节详细介绍了编程和激活JSweet扩展 。

核心类型和对象

JSweet允许使用原始Java类型,核心Java对象(在java.lang许多JDK类中定义(特别是java.util但不仅仅是),以及在def.js包中定义的核心JavaScript对象 。接下来,我们描述了这些核心类型的使用和对象。

原始Java类型

JSweet允许使用Java原始类型(和相关的文字)。

int,byte,short,double,float被全部转换成JavaScript数字(打字稿number类型)。精密通常不会在JSweet无所谓,但是,铸造int,byte或 short强制四舍五入到合适的长度整数数量。

char遵循Java类型规则,但由转换器转换为JavaScript string。

boolean对应于JavaScript boolean。

java.lang.String对应于JavaScript string。(不是说原始类型,但是是不可变的并且在Java中用作字符串文字的类)

转换的直接后果是,JSweet中并不总是可以使用数字或字符/字符串安全地重载方法。例如,方法pow(int, int)以及 pow(double, double)可能引起过载的问题。使用JSweet上下文,转换器将能够选择正确的方法,但JavaScript互操作性可能是一个问题。总之,由于没有区别n instanceof Integer和n instanceof Double (它既装置typeof n === ’number’)调用pow(number, number) 从JavaScript将随机选择一个实现或其他。这不应该总是一个问题,但在某些特殊情况下,它可能会产生微妙的错误。请注意,在这些情况下,程序员将能够调整JavaScript生成,如第6节中的完整解释 。

有效声明的示例:

// warning '==' behaves like JavaScript '===' at runtime

int i = 2;

assert i == 2;

double d = i + 4;

assert d == 6;

String s = "string" + '0' + i;

assert s == "string02";

boolean b = false;

assert !b;

该==运营商的行为类似于JavaScript的严格等于运算符 ===,使其接近Java语义。同样,!=映射到!==。在将对象与null文字进行比较时,该行为有一个例外。在这种情况下,JSweet转换为松散的相等运算符,以便程序员看不到null和之间的区别undefined(这在JavaScript中是不同的,但它可能会让Java程序员感到困惑)。要控制JSweet是否生成严格或松散的运算符,可以使用以下辅助方法:jsweet.util.Lang.$strict和 jsweet.util.Lang.$loose。在这样的宏中包装比较运算符将强制JSweet生成严格或松散的运算符。例如:

import static jsweet.util.Lang.$loose;

[...]

int i = 2;

assert i == 2; // generates i === 2

assert !((Object)"2" == i);

assert $loose((Object)"2" == i); // generates "2" == i

允许的Java对象

默认情况下,JSweet通过使用内置宏将核心Java对象和方法映射到JavaScript。这意味着Java代码直接替换为实现类似行为的有效JavaScript代码。为大多数有用的核心Java类(java.lang,java.util)实现了默认映射。在可能的情况下(当它有意义时),为其他JDK类实现一些部分映射,例如输入和输出流,区域设置,日历,反射等。

使用默认行为,我们可以指出以下限制:

除了某些特定的上下文之外,通常不可能扩展JDK类。如果需要扩展JDK类,应该考虑重构您的程序,或者使用允许它的JavaScript运行时(例如J4TS)。

Java反射API(java.lang.reflect)仅限于非常基本的操作。可以访问类和成员,但无法访问类型。可以使用更完整的Java反射支持,但需要JSweet扩展。

目前还不支持Java 8流,但部分支持它们很简单(欢迎贡献)。

有效声明的示例:

Integer i = 2;

assert i == 2;

Double d = i + 4d;

assert d.toString() == "6";

assert !((Object) d == "6");

BiFunction<String, Integer, String> f = (s, i) -> { return s.substring(i); };

assert "bc" == f.apply("abc", 1);

获得更多Java API

使用JSweet,可以添加在JavaScript中实现Java API的运行时,以便程序员可以访问更多Java API,从而在Java和JavaScript之间共享相同的代码。为JSweet实现Java API的核心项目是J4TS( https://github.com/cincheo/j4ts ),它包含一个非常完整的java.util.*类和其他核心包的实现。J4TS基于GWT的JRE仿真的一个分支,但它适合用JSweet编译。程序员可以将J4TS用作Maven存储库中的常规JavaScript库。

虽然J4TS不能直接执行Java核心类型的使用JavaScript的人发生冲突(Boolean,Byte,Short,Integer, Long,Float,Double,Character,String),J4TS有助于通过为每个类提供佣工(支持他们的静态部分javaemul.internal.BooleanHelper,javaemul.internal.ByteHelper...)。当JSweet转换器在java.lang.T不支持作为内置宏的类型上遇到静态Java方法时 ,它会委托给 javaemul.internal.THelper,它可以为给定的静态方法提供JavaScript实现。这样,通过使用J4TS,程序员可以使用更多的核心JRE API。

Java数组

数组可以在JSweet中使用,并转换为JavaScript数组。数组初始化,访问和迭代都是有效的语句。

int[] arrayOfInts = { 1, 2, 3, 4};

assert arrayOfInts.length == 4;

assert arrayOfInts[0] == 1;

int i = 0;

for (int intItem : arrayOfInts) {

assert arrayOfInts[i++] == intItem;

}

核心JavaScript API

核心JavaScript API已定义def.js(完整文档可在 http://www.jsweet.org/core-ap... )。主要的JavaScript类是:

def.js.Object:JavaScript Object类。JavaScript对象函数和属性的共同祖先。

def.js.Boolean:JavaScript布尔类。布尔值的包装器。

def.js.Number:JavaScript Number类。数值的包装器。

def.js.String:JavaScript String类。字符串的包装器和构造函数。

def.js.Function:JavaScript函数类。函数的构造函数。

def.js.Date:JavaScript Date类,它支持基本存储和检索日期和时间。

def.js.Array<T>:JavaScript Array类。它用于构造数组,它是高级的,类似列表的对象。

def.js.Error:JavaScript错误类。这个类实现 java.lang.RuntimeException并且可以被抛出并被try ... catch语句捕获。

使用JavaScript框架时,程序员应该在大多数时间使用此API,这与HTML5兼容并遵循JavaScript最新支持的版本。但是,对于需要与Java文字(数字,布尔值和字符串)一起使用的对象,java.lang 建议使用包类。例如,jQuery API声明 $(java.lang.String)而不是$(def.js.String)。这允许程序员使用文字来编写表达式,例如$("a")(用于选择文档中的所有链接)。

使用JSweet,程序员可以根据需要轻松地从Java切换到JavaScript API(反之亦然)。在jsweet.util.Lang 类定义方便的静态方法投来回核心Java对象到其相应的JavaScript对象。例如,该 string(...)方法将允许程序员从Java切换到JavaScript字符串,反之亦然。

import static jsweet.util.Lang.string;

// str is a Java string, but is actually a JavaScript string at runtime

String str = "This is a test string";

// str is exactly the same string object, but shown through the JS API

def.js.String str2 = string(str);

// valid: toLowerCase it defined both in Java and JavaScript

str.toLowerCase();

// this method is not JS-compatible, so a macro generates the JS code

str.equalsIgnoreCase("abc");

// direct call to the JS substr method on the JavaScript string

string(str).substr(1);

// or

str2.substr(1);

注意:例如,对于JavaScript客户端和Java服务器之间的代码共享,最好只使用Java API并避免使用JavaScript API。JavaScript API将编译有效的Java字节码,但尝试在JVM上执行它们会引起不满意的链接错误。

这是另一个示例,显示了使用该array方法访问pushJavaScript数组中可用的方法。

import static jsweet.util.Lang.array;

String[] strings = { "a", "b", "c" };

array(strings).push("d");

assert strings[3] == "d";

JSweet中的类完全支持所有类型的Java类声明。例如:

public class BankAccount {

public double balance = 0;
public double deposit(double credit) {
    balance += credit;
    return this.balance;
}

}

这被转换为以下JavaScript代码:

var BankAccount = (function () {

function BankAccount() {
    this.balance = 0;
}
BankAccount.prototype.deposit = function(credit) {
    this.balance += credit;
    return this.balance;
};
return BankAccount;

})();

类可以定义构造函数,具有超类,并且可以像在Java中一样实例化。与Java类似,JSweet中允许使用内部类和匿名类(从1.1.0版开始)。JSweet支持静态和常规内部/匿名类,它们可以与封闭类共享状态。仍然像在Java中一样,匿名类可以访问其作用域中声明的最终变量。例如,以下声明在JSweet中有效,并且将在运行时模仿Java语义,以便Java程序员可以受益于Java语言的所有功能。

abstract class C {

public abstract int m();

}

public class ContainerClass {

// inner class
public class InnerClass {
    public I aMethod(final int i) {
        // anonymous class
        return new C() {
            @Override
            public int m() {
                // access to final variable i
                return i;
            }
        }
    }
}

}

接口

在JSweet中,可以像在Java中一样使用接口。但是,与Java相反,没有关联的类可用作运行时。使用接口时,JSweet会生成代码来模拟特定的Java行为(例如instanceof在接口上)。

JSweet支持Java 8静态和默认方法。但是,默认方法到目前为止都是实验性的,你应该自担风险使用它们。

在JSweet中,接口更类似于TypeScript中的接口而不是Java中的接口。这意味着它们必须被视为对象签名,它可以指定函数,还可以指定属性。为了在定义接口时允许使用字段作为属性,JSweet允许使用带注释的常规类@jsweet.lang.Interface。例如,以下接口键入Point具有2个属性的对象。

@Interface

public class Point {

public double x;
public double y;

}

对于Java程序员来说,这可能看起来像是一种非常奇怪的方法来定义一个对象,但是你必须记住它不是一个类,而是一个JavaScript对象的类型。因此,它不违反OOP原则。我们可以创建一个在界面后键入的JavaScript对象。请注意,以下代码实际上并未创建Point 接口的实例,而是创建符合接口的对象。

Point p1 = new Point() {{ x=1; y=1; }};

此对象创建机制是TypeScript / JavaScript机制,不应与匿名类混淆,匿名类是类似Java的构造。因为Point有注释@Interface,转换后的JavaScript代码类似于:

var p1 = Object.defineProperty({ x:1, y:1 }, "_interfaces", ["Point"]);

请注意,对于每个对象,JSweet会跟踪其创建的接口以及其类实现的所有可能接口。此接口跟踪系统实现为一个名为的特殊对象属性__interfaces。使用该属性,JSweet允许instanceof在Java之类的接口上使用运算符。

接口中的可选字段

接口可以定义可选字段,用于在程序员忘记初始化对象中的必填字段时报告错误。在JSweet中支持可选字段是通过使用 @jsweet.lang.Optional注释完成的。例如:

@Interface

public class Point {

public double x;
public double y;
@Optional
public double z = 0;

}

在从接口构造对象时,JSweet编译器将检查字段是否已正确初始化。

// no errors (z is optional)

Point p1 = new Point() {{ x=1; y=1; }};

// JSweet reports a compile error since y is not optional

Point p2 = new Point() {{ x=1; z=1; }};

接口中的特殊JavaScript函数

在JavaScript中,对象可以具有属性和函数,但也可以(非排他地)用作构造函数和函数本身。这在Java中是不可能的,因此JSweet定义了用于处理这些情况的特殊函数。

$apply 用于表示该对象可以用作函数。

$new 用于声明该对象可以用作构造函数。

例如,如果一个对象o是O定义 的接口$apply(),则写:

o.$apply();

将转变为:

o();

同样,如果O定义$new():

o.$new();

将转变为:

new o();

是的,它在Java中没有意义,但在JavaScript中确实如此!

无类型对象(地图)

在JavaScript中,对象可以看作包含键值对的映射(键通常称为索引,尤其是当它是数字时)。因此,在JSweet中,所有对象都定义了特殊函数(定义于 def.js.Object):

$get(key) 使用给定键访问值。

$set(key,value) 设置或替换给定键的值。

$delete(key) 删除给定键的值。

反射/无类型访问

的功能$get(key),$set(key,value)并且$delete(key)可以被看作是一个简单的反射API来访问对象字段和状态。还要注意静态方法def.js.Object.keys(object),它返回给定对象上定义的所有键。

以下代码使用此API来内省对象的状态 o。

for(String key : def.js.Object.keys(o)) {

console.log("key=" + key + " value=" + o.$get(key));

});

当没有给定对象的类型化API时,此API可用于以无类型方式操作对象(当然应尽可能避免使用)。

无类型对象初始化

可以使用该$set(key,value)函数创建新的无类型对象。例如:

Object point = new def.js.Object() {{ $set("x", 1); $set("y", 1); }};

它也转化为:

var point = { "x": 1, "y": 1};

作为一种快捷方式,可以使用该jsweet.util.Lang.$map函数,该函数转换为完全相同的JavaScript代码:

import static jsweet.util.Lang.$map;

[...]

Object point = $map("x", 1, "y", 1);

索引对象

可以为每个对象重载键和值的类型。例如,Array<T>类将键作为数字和值定义为符合类型的对象T。

对于使用数字键索引的对象,允许实现java.lang.Iterable接口,以便可以在foreach循环中使用它们。例如,NodeList类型(来自DOM)定义了一个索引函数:

@Interface

class NodeList implements java.lang.Iterable {

public double length;
public Node item(double index);
public Node $get(double index);

}

在JSweet中,您可以使用该$get 函数访问节点列表元素,也可以使用foreach语法进行迭代。以下代码生成完全有效的JavaScript代码。

NodeList nodes = ...

for (int i = 0; i < nodes.length; i++) {

HTMLElement element = (HTMLElement) nodes.$get(i);
[...]

}

// same as:

NodeList nodes = ...

for (Node node : nodes) {

HTMLElement element = (HTMLElement) node;
[...]

}

枚举

JSweet允许类似于Java定义枚举。下面的代码声明与树可能值的枚举(A,B,和C)。

enum MyEnum {

A, B, C

}

以下语句是JSweet中的有效语句。

MyEnum e = MyEnum.A;

assert MyEnum.A == e;

assert e.name() == "A";

assert e.ordinal() == 0;

assert MyEnum.valueOf("A") == e;

assert array(MyEnum.values()).indexOf(MyEnum.valueOf("C")) == 2;

与Java枚举一样,可以在枚举中添加其他方法,构造函数和字段。

enum ScreenRatio {

FREE_RATIO(null),
RATIO_4_3(4f / 3),
RATIO_3_2(1.5f),
RATIO_16_9(16f / 9),
RATIO_2_1(2f / 1f),
SQUARE_RATIO(1f);

private final Float value;

private MyComplexEnum(Float value) {
    this.value = value;
}

public Float getValue() {
    return value;
}

}

枚举便携性说明

简单的枚举被转换为常规的TypeScript枚举,即数字。在JavaScript中,在运行时,枚举实例是简单编码的序数。因此,JSweet枚举很容易与TypeScript枚举共享,即使使用枚举,JSweet程序也可以与TypeScript程序进行互操作。

具有其他成员的枚举也会映射到TypeScript枚举,但会生成另一个类来存储其他信息。与TypeScript互操作时,序号将保留,但附加信息将丢失。想要与TypeScript共享枚举的程序员应该知道这种行为。

全局

在Java中,与JavaScript相反,没有全局变量或函数(只有静态成员,但即使那些必须属于一个类)。因此,JSweet引入了保留的Globals 类和globals包。这有两个目的:

生成具有全局变量和函数的代码(在Java中不鼓励这样做)

绑定到定义全局变量和函数的现有JavaScript代码(尽可能多的JavaScript框架)

在Globals类中,只允许使用静态字段(全局变量)和静态方法(全局函数)。以下是适用于Globals类的主要约束:

没有非静态成员

没有超级课程

不能延长

不能用作常规类的类型

没有公共构造函数(空私有构造函数可以)

不能在方法中使用$ get,$ set和$ delete

例如,以下代码片段将引发转换错误。

class Globals {

public int a;
// error: public constructors are not allowed
public Globals() {
    this.a = 3;
}
public static void test() {
    // error: no instance is available
    $delete("key");
}

}

// error: Globals classes cannot be used as types

Globals myVariable = null;

必须记住,Globals类和global包在运行时被擦除,以便可以直接访问它们的成员。例如mypackage.Globals.m(),在JSweet程序中,对应mypackage.m()于生成的代码中的函数以及运行时的JavaScript VM中的 函数。此外,mypackage.globals.Globals.m()对应于m()。

为了擦除生成代码中的包,程序员也可以使用@Root注释,这将在第5节中解释 。

可选参数和重载

In JavaScript, parameters can be optional, in the sense that a parameter value does not need to be provided when calling a function. Except for varargs, which are fully supported in JSweet, the general concept of an optional parameter does not exist in Java. To simulate optional parameters, JSweet programmers can use method overloading, which is supported in Java. Here are some examples of supported overloads in JSweet:

String m(String s, double n) { return s + n; }

// simple overloading (JSweet transpiles to optional parameter)

String m(String s) { return m(s, 0); }

// complex overloading (JSweet generates more complex code to mimic the Java behavior)

String m(String s) { return s; }

Bridging to external JavaScript elements

It can be the case that programmers need to use existing libraries from JSweet. In most cases, one should look up in the available candies, a.k.a. bridges at http://www.jsweet.org/jsweet-... When the candy does not exist, or does not entirely cover what is needed, one can create new definitions in the program just by placing them in the def.libname package. Definitions only specify the types of external libraries, but no implementations. Definitions are similar to TypeScript’s .d.ts definition files (actually JSweet generates intermediate TypeScript definition files for compilation purposes). Definitions can also be seen as similar to .h C/C++ header files.

Examples

以下示例显示了使用简单定义可供JSweet程序员访问的主干存储类。此类仅用于键入,将作为TypeScript定义生成,并在JavaScript生成期间擦除。

package def.backbone;

class Store {

public Store(String dbName) {}

}

请注意,定义类构造函数必须具有空体。此外,定义类方法必须是native。例如:

package def.mylib;

class MyExternalJavaScriptClass {

public native myExternalJavaScriptMethod();

}

可以在定义中定义属性,但是,无法初始化这些属性。

写定义规则(又名桥梁)

按照惯例,将类放在def.libname包中定义了一组libname名为的外部JavaScript库 的定义libname。请注意,此机制类似于TypeScript d.ts 定义文件。

Candies(外部JavaScript库的桥梁)使用定义。例如,jQuery candy定义了def.jquery包中的所有jQuery API 。

以下是编写定义时需要遵循的规则和约束的列表。

接口比类更受欢迎,因为接口可以合并,类可以实例化。仅当API定义显式构造函数(可以使用其创建对象new)时,才应使用类。要在JSweet中定义接口,只需使用注释类@jsweet.lang.Interface。

必须将顶级函数和变量定义为类中的public static 成员Globals。

所有类,接口和包都应记录在Javadoc标准之后的注释。

当函数参数有多种类型时,方法重载应优先于使用union类型。当无法进行方法重载时,简单地使用Object类型会更方便。键入的强度较低,但更容易使用。

可以使用字符串类型来提供函数重载,具体取决于字符串参数值。

在方法签名中,可以使用@jsweet.lang.Optional注释定义可选参数 。

在界面中,可以使用@jsweet.lang.Optional注释定义可选字段 。

定义可以直接嵌入到JSweet项目中,以便以类型化的方式访问外部库。

定义也可以打包成糖果(Maven工件),以便它们可以被其他项目共享。有关如何创建糖果的完整详细信息,请参阅“ 包装”部分。请注意,在使用JSweet编写库时不需要编写定义,因为Java API可以直接访问,并且可以使用该declaration选项由JSweet自动生成TypeScript定义。

无法访问

有时,定义不可用或不正确,只需要一个小补丁即可访问功能。程序员必须记住,JSweet只是一个语法层,并且总是可以绕过键入以访问未在API中明确指定的字段或函数。

虽然具有良好类型的API是首选和建议的方式,但是当这样的API不可用时,使用def.js.Object.$get允许反射访问方法和属性,然后可以将其转换为正确的类型。为了以无类型方式访问函数,可以强制转换 def.js.Function并调用泛型和非类型化方法$apply。例如,以下是$在jQuery API不可用时如何调用jQuery 方法:

import def.dom.Globals.window;

[...]

Function $ = (Function)window.$get("$");

$.$apply("aCssSelector"):

该$get函数可用于def.js.Object(或子类)的实例。对于a def.js.Object,您可以使用jsweet.util.Lang.object辅助方法强制转换它 。例如:

import static jsweet.dom.Lang.object;

[...]

object(anyObject).$get("$");

最后,jsweet.util.Lang.$inserthelper方法允许用户在程序中插入任何TypeScript表达式。无效的表达式将引发TypeScript编译错误,但不建议使用此技术。

import static jsweet.dom.Lang.$get;

import static jsweet.dom.Lang.$apply;

[...]

// generate anyObject;

$apply($get(anyObject, "prop"), "param");

最后,还要注意使用jsweet.util.Lang.any辅助方法,这对于擦除键入非常有用。由于该any方法any在TypeScript中生成类型的强制转换,因此它比例如强制转换更激进Object。以下示例说明如何使用该any方法将Int32ArrayJava转换为Java int[](然后允许对其进行直接索引访问)。

ArrayBuffer arb = new ArrayBuffer(2 2 4);

int[] array = any(new Int32Array(arb));

int whatever = array[0];

混入

在JavaScript中,通常的做法是使用新闻元素(字段和方法)来增强现有类。它是框架定义插件时使用的扩展机制。通常,jQuery插件会向JQuery类中添加新元素。例如,jQuery计时器插件timer向JQuery该类添加一个字段。因此,JQuery如果您单独使用jQuery,或者使用其计时器插件增强jQuery ,则 该类没有相同的原型。

在Java中,此扩展机制存在问题,因为Java语言默认情况下不支持mixins或任何类型的扩展。

无法访问mixins

程序员可以使用访问$get器和/或强力转换来访问添加的元素。

以下是$get用于计时器插件的示例:

((Timer)$("#myId").$get("timer")).pause();

这是另一种通过使用jQuery UI插件来实现它的方法(请注意,此解决方案强制使用def.jqueryui.JQuery 而不是def.jquery.JQuery为了访问menu()由UI插件添加的功能):

import def.jqueryui.JQuery;

[...]

Object obj = $("#myMenu");

JQuery jq = (JQuery) obj;

jq.menu();

然而,这些解决方案并不完全令人满意,因为在打字方面明显不安全。

使用mixins键入访问

当需要交叉糖果动态扩展时,JSweet定义了mixin的概念。mixin是一个定义成员的类,最终可以在目标类(mixin-ed类)中直接访问。Mixins使用@Mixin注释定义。这是def.jqueryui.JQuerymixin 的摘录 :

package def.jqueryui;

import def.dom.MouseEvent;

import def.js.Function;

import def.js.Date;

import def.js.Array;

import def.js.RegExp;

import def.dom.Element;

import def.jquery.JQueryEventObject;

@jsweet.lang.Interface

@jsweet.lang.Mixin(target=def.jquery.JQuery.class)

public abstract class JQuery extends def.jquery.JQuery {

native public JQuery accordion();
native public void accordion(jsweet.util.StringTypes.destroy methodName);
native public void accordion(jsweet.util.StringTypes.disable methodName);
native public void accordion(jsweet.util.StringTypes.enable methodName);
native public void accordion(jsweet.util.StringTypes.refresh methodName);
...
native public def.jqueryui.JQuery menu();
...

人们可以注意到@jsweet.lang.Mixin(target=def.jquery.JQuery.class) ,这个mixin将被合并到一起,def.jquery.JQuery以便用户能够直接以一种良好的方式使用所有UI插件成员。

如何使用

TBD。

从现有的TypeScript定义生成JSweet糖果

TBD。

辅助类型

JSweet使用大多数Java输入功能(包括功能类型),但也使用所谓的辅助类型扩展Java类型系统。辅助类型背后的想法是创建可以通过使用类型参数(也称为泛型)来保存键入信息的类或接口 ,以便JSweet转换器可以涵盖更多的键入方案。这些类型已经从TypeScript类型系统映射,它比Java更丰富(主要是因为JavaScript是一种动态语言,需要比Java更多的打字场景)。

功能类型

用于功能类型,JSweet重用java.Runnable和 java.util.function爪哇8的功能接口,这些接口是通用的,但只支持高达2参数的功能。因此,JSweet为更多参数添加了一些支持jsweet.util.function,因为它是JavaScript API中的常见情况。

以下是使用Function通用功能类型的示例:

import java.util.function.Function;

public class C {

String test(Function<String, String> f) {
    f.apply("a");
}

public static void main(String[] args) {
    String s = new C().test(p -> p);
    assert s == "a";
}

}

我们鼓励程序员使用jsweet.util.function和java.util.function(以及 java.lang.Runnable)中定义的通用功能接口。当需要具有更多参数的函数时,程序员可以jsweet.util.function通过遵循与现有函数相同的模板来定义他们自己的通用函数类型 。

在某些情况下,程序员更愿意定义自己的特定功能接口。这得到了JSweet的支持。例如:

@FunctionalInterface

interface MyFunction {

void run(int i, String s);

}

public class C {

void m(MyFunction f) {
    f.run(1, "test");
}
public static void main(String[] args) {
    new C().m((i, s) -> {
        // do something with i and s
    });
}

}

重要警告:这里要注意的是,与Java相反,@FunctionInterface注释的使用是强制性的。

还要注意apply函数的可能用途,按照惯例,该函数始终是目标对象的功能定义(除非使用apply注释进行@Name注释)。定义/调用 apply可以在任何类/对象上完成(因为在JavaScript中任何对象都可以成为一个功能对象)。

对象类型

对象类型与接口类似:它们定义了一组适用于对象的字段和方法(但请记住它是一个编译时合同)。在TypeScript中,对象类型是内联的和匿名的。例如,在TypeScript中,以下方法m接受一个参数,该参数是包含index字段的对象:

// TypeScript:

public class C {

public m(param : { index : number }) { ... }

}

对象类型是编写较短代码的便捷方式。可以通过动态构建对象来传递正确键入的对象:

// TypeScript:

var c : C = ...;

c.m({ index : 2 });

显然,对象类型是一种使程序员很容易输入JavaScript程序的方法,这是TypeScript的主要目标之一。它使得JavaScript程序员的输入简洁,直观,直观。在Java / JSweet中,不存在类似的内联类型,Java程序员用于为这种情况定义类或接口。因此,在JSweet中,程序员必须定义用@ObjectType对象类型注释的辅助类。这可能看起来更复杂,但它有利于强制程序员命名所有类型,最终可以根据上下文导致更易读和可维护的代码。请注意,与接口类似,对象类型在运行时被擦除。另外@ObjectType 注解的类可以内部类,使他们在本地使用。

这是以前的TypeScript程序的JSweet版本。

public class C {

@ObjectType
public static class Indexed {
    int index;
}
public void m(Indexed param) { ... }

}

使用对象类型与使用接口类似:

C c = ...;

c.m(new Indexed() {{ index = 2; }});

当对象类型是共享对象并表示可以在多个上下文中使用的键入实体时,建议使用 @Interface注释而不是@ObjectType。这是基于界面的版本。

@Interface

public class Indexed {

int index;

}

public class C {

public m(Indexed param) { ... }

}

C c = ...;

c.m(new Indexed {{ index = 2; }});

字符串类型

在TypeScript中,字符串类型是一种根据字符串参数的值来模拟函数重载的方法。例如,这是DOM TypeScript定义文件的简化摘录:

// TypeScript:

interface Document {

[...]
getElementsByTagName(tagname: "a"): NodeListOf<HTMLAnchorElement>;
getElementsByTagName(tagname: "b"): NodeListOf<HTMLPhraseElement>;
getElementsByTagName(tagname: "body"): NodeListOf<HTMLBodyElement>;
getElementsByTagName(tagname: "button"): NodeListOf<HTMLButtonElement>;
[...]

}

在此代码中,getElementsByTagName函数都是依赖于传递给tagname参数的字符串的重载。不仅字符串类型允许函数重载(通常在TypeScript / JavaScript中不允许),但它们也约束字符串值(类似于枚举),因此编译器可以自动检测字符串值中的拼写错误并引发错误。

此功能对代码质量很有用,JSweet提供了一种机制来模拟具有相同级别类型安全性的字符串类型。字符串类型是使用注释的公共静态字段@StringType。必须使用在同一容器类型中声明的同名接口键入它。

对于JSweet翻译库(糖果),所有字符串类型都在类中声明jsweet.util.StringTypes,因此程序员很容易找到它们。举例来说,如果一个"body"字符串类型需要定义,一个名为Java接口body和一个静态的最终场被称为body在一个定义jsweet.util.StringTypes。

请注意,每个糖果可能在jsweet.util.StringTypes类中定义了自己的字符串类型 。JSweet转换器在字节码级别合并所有这些类,以便所有糖果的所有字符串类型在同一个jsweet.util.StringTypes实用程序类中可用。因此,JSweet DOM API将如下所示:

@Interface

public class Document {

[...]
public native NodeListOf<HTMLAnchorElement> getElementsByTagName(a tagname);
public native NodeListOf<HTMLPhraseElement> getElementsByTagName(b tagname);
public native NodeListOf<HTMLBodyElement> getElementsByTagName(body tagname);
public native NodeListOf<HTMLButtonElement> getElementsByTagName(button tagname);
[...]

}

在此API中a,b,body和button是在定义的接口 jsweet.util.StringTypes类。当使用一种方法时 Document,程序员只需要使用相应的类型实例(同名)。例如:

Document doc = ...;

NodeListOf<HTMLAnchorElement> elts = doc.getElementsByTagName(StringTypes.a);

注意:如果字符串值不是有效的Java标识符(例如 "2d"或者"string-with-dashes"),则将其转换为有效的标识符并使​​用注释@Name("originalName"),以便JSweet转换器知道必须在生成的代码中使用什么实际字符串值。例如,默认情况下,"2d"和"string-with-dashes"将对应于接口StringTypes._2d和 StringTypes.string_with_dashes与@Name注解。

程序员可以根据自己的需要定义字符串类型,如下所示:

import jsweet.lang.Erased;

import jsweet.lang.StringType;

public class CustomStringTypes {

@Erased
public interface abc {}

@StringType
public static final abc abc = null;

// This method takes a string type parameter
void m2(abc arg) {
}

public static void main(String[] args) {
    new CustomStringTypes().m2(abc);
}

}

注意使用@Erased注释,它允许声明abc内部接口。此接口用于键入字符串类型字段abc。通常,我们建议程序员将程序的所有字符串类型分组到同一个实用程序类中,以便于查找它们。

元组类型

元组类型表示具有单独跟踪的元素类型的JavaScript数组。对于的元组类型,JSweet定义参数化辅助类TupleN<T0, ... TN-1>,它定义$0,$1... $N-1 公共字段来模拟访问类型数组(字段$i中键入与 Ti)。

例如,给定以下大小为2的元组:

Tuple2<String, Integer> tuple = new Tuple2<String, Integer>("test", 10);

我们可以期待以下(良好类型)行为:

assert tuple.$0 == "test";

assert tuple.$1 == 10;

tuple.$0 = "ok";

tuple.$1--;

assert tuple.$0 == "ok";

assert tuple.$1 == 9;

元组类型都在jsweet.util.tuple包中定义(并且必须定义) 。默认情况下Tuple[2..6],定义了类。当在糖果API中遇到时,会自动生成其他元组(> 6)。当然,当需要在jsweet.util.tuple包中找不到更大的元组时,程序员可以根据需要在该包中添加自己的元组,只需遵循与现有元组相同的模板即可。

联盟类型

联合类型表示可能具有多个不同表示之一的值。当这种情况发生在方法签名中时(例如,允许给定参数的几种类型的方法),JSweet利用了Java中可用的方法重载机制。例如,以下m方法接受参数p,该参数可以是a String或a Integer。

public void m(String p) {...}

public void m(Integer p) {...}

在前一种情况下,不需要使用显式联合类型。对于更一般的情况,JSweet 在 包中定义了一个辅助接口 Union<T1, T2>(和UnionN<T1, ... TN>)jsweet.util.union。通过使用此辅助类型和union实用程序方法,程序员可以在union类型和union-ed类型之间来回转换,以便JSweet可以确保与TypeScript联合类型类似的属性。

以下代码显示了JSweet中union类型的典型用法。它只是将一个变量声明为一个字符串和一个数字之间的联合,这意味着该变量实际上可以是其中一种类型(但没有其他类型)。从联合类型到常规类型的切换是通过jsweet.util.Lang.union辅助方法完成的。这个辅助方法是完全无类型的,允许从Java角度将任何联合转换为另一种类型。它实际上是JSweet转换器,它检查是否一直使用了union类型。

import static jsweet.util.Lang.union;

import jsweet.util.union.Union;

[...]

Union<String, Number> u = ...;

// u can be used as a String

String s = union(u);

// or a number

Number n = union(u);

// but nothing else

Date d = union(u); // JSweet error

如果union需要,也可以使用其他方式将助手从常规类型切换回联合类型。

import static jsweet.util.Lang.union;

import jsweet.util.union.Union3;

[...]

public void m(Union3<String, Number, Date>> u) { ... }

[...]

// u can be a String, a Number or a Date

m(union("a string"));

// but nothing else

m(union(new RegExp(".*"))); // compile error

注意:在键入函数参数时,优先使用Java函数重载而不是union类型。例如:

// with union types (discouraged)

native public void m(Union3<String, Number, Date>> u);

// with overloading (preferred way)

native public void m(String s);

native public void m(Number n);

native public void m(Date d);

交叉类型

TypeScript定义了类型交集的概念。当类型相交时,意味着结果类型是更大的类型,它是所有相交类型的总和。例如,在TypeScript中, A & B对应于定义两者A和B成员的类型。

由于许多原因,Java中的交集类型无法轻松实现。因此,这里做出的实际选择是使用联合类型代替交集类型。A & B因此,在JSweet中定义为 Union<A, B>,这意味着程序员可以使用辅助方法访问这两者A和 B成员jsweet.util.Lang.union。它当然不如TypeScript版本方便,但它仍然是类型安全的。

语义

语义指定给定程序在执行时的行为方式。虽然JSweet依赖于Java语法,但程序被转换为JavaScript并且不在JRE中运行。因此,与Java程序相比,JavaScript语义将影响JSweet程序的最终语义。在本节中,我们将通过关注Java / JavaSript和JSweet之间的差异或共性来讨论语义。

主要方法

主要方法是程序执行入口点,并且在main评估包含方法的类时将全局调用。例如:

public class C {

private int n;
public static C instance;
public static void main(String[] args) {
    instance = new C();
    instance.n = 4;
}
public int getN() {
    return n;
}

}

// when the source file containing C has been evaluated:

assert C.instance != null;

assert C.instance.getN() == 4;

全局调用main方法的方式取决于程序的打包方式。有关详细信息,请参阅附录。

初始化器

初始化器的行为与Java类似。

例如:

public class C1 {

int n;
{
    n = 4;
}

}

assert new C1().n == 4;

与静态初始化器类似:

public class C2 {

static int n;
static {
    n = 4;
}

}

assert C2.n == 4;

虽然在实例化类时会评估常规初始值设定项,但是为了避免前向依赖性问题而懒惰地评估静态初始化程序,并模拟初始化程序的Java行为。使用JSweet,程序员可以定义静态字段或静态初始化程序,它依赖于尚未初始化的静态字段。

有关此行为的更多详细信息,请参阅附录。

数组初始化和分配

数组可以像Java一样使用。

String[] strings = { "a", "b", "c" };

assert strings[1] == "b";

指定维度时,数组是预先分配的(如Java中所示),因此它们使用正确的长度进行初始化,并在多维数组的情况下使用正确的子数组进行初始化。

String[][] strings = new String2;

assert strings.length == 2;

assert strings[0].length == 2;

strings0 = "a";

assert strings0 == "a";

通过强制转换为def.js.Arraywith,可以在数组上使用JavaScript API jsweet.util.Lang.array。

import static jsweet.util.Lang.array;

[...]

String[] strings = { "a", "b", "c" };

assert strings.length == 3;

array(strings).push("d");

assert strings.length == 4;

assert strings[3] == "d";

在某些情况下,最好def.js.Array直接使用该类。

Array<String> strings = new Array<String>("a", "b", "c");

// same as: Array<String> strings = array(new String[] { "a", "b", "c" });

// same as: Array<String> strings = new Array<String>(); strings.push("a", "b", "c");

assert strings.length == 3;

strings.push("d");

assert strings.length == 4;

assert strings.$get(3) == "d";

异步编程

在ES2015 + Promise API的帮助下,JSweet支持基本回调概念之外的高级异步编程。

承诺

通过声明Promise返回类型来定义异步方法非常简单 。当毫秒毫秒过去时,Promise将 满足以下方法。

Promise<Void> delay(int millis) {

return new Promise<Void>((Consumer<Void> resolve, Consumer<Object> reject) -> {

setTimeout(resolve, millis);

});

}

然后,您可以在履行承诺后链接同步和异步操作。

delay(1000)

// chain with a synchronous action with "then". Here we just return a constant.

.then(() -> {

System.out.println("wait complete");
return 42;

})

// chain with an asynchronous action with "thenAsync". Here it is implied that anotherAsyncAction(String) returns a Promise<...>

.thenAsync((Integer result) -> {

System.out.println("previous task result: " + result); // will print "previous task result: 42"

return anotherAsyncAction("param");

})

// this chained action will be executed once anotherAsyncAction finishes its execution.

.then((String result) -> {

System.out.println("anotherAsyncAction returned " + result);

})

// catch errors during process using this method

.Catch(error -> {

System.out.println("error is " + error);

});

这允许完全类型安全和流畅的异步编程模型。

异步/ AWAIT

Promises非常有趣,以避免回调,但编写它仍然需要大量的样板代码。它比纯回调更好,但比线性编程更不易读和直接。这就是async/await帮助的地方 。

使用await关键字,您可以告诉运行时等待 Promise实现,而无需编写then方法。await“是” then部分之后的代码。结果是您可以使用线性编程编写异步代码。

import static jsweet.util.Lang.await;

// wait for the Promise returned by the delay method to be fulfilled

await(delay(1000));

System.out.println("wait complete");

错误处理也是如此。您可以使用普通的 try / catch习语来处理异常。

import static jsweet.util.Lang.await;

import def.js.Error;

try {

Integer promiseResult = await(getANumber());

assert promiseResult == 42;

} catch(Error e) {

System.err.println("something unexpected happened: " + e);

}

你必须声明为async每个异步方法/ lambda(即每个等待某事的方法)。

import static jsweet.util.Lang.await;

import static jsweet.util.Lang.async;

import static jsweet.util.Lang.function;

import jsweet.lang.Async;

import def.js.Function;

@Async

Promise<Integer> findAnswer() {

await(delay(1000)); // won't compile if the enclosing method isn't @Async

return asyncReturn(42); // converts to Promise

}

@Async

void askAnswerThenVerifyAndPrintIt() {

try {

Integer answer = await(findAnswer());

// lambda expressions can be async
Function verifyAnswerAsync = async(function(() -> {
  return await(answerService.verifyAnswer(answer));
}))

Boolean verified = await(verifyAnswerAsync.$apply());

if (!verified) {
  throw new Error("cannot verify this answer");
}

console.log("answer found: " + answer);

} catch (Error e) {

console.error(e, "asynchronous process failed");

}

}

甜,不是吗?;)

姓名冲突

与TypeScript / JavaScript相反,Java在方法,字段和包之间存在根本区别。Java还支持方法重载(具有相同名称的不同签名的方法)。在JavaScript中,对象变量和函数存储在同一个对象映射中,这基本上意味着您不能为多个对象成员使用相同的密钥(这也解释了Java中无法实现Java语义中的方法重载)。因此,在TypeScript中生成时,某些Java代码可能包含名称冲突。JSweet将尽可能自动避免名称冲突,并在其他情况下报告声音错误。

方法和字段名称冲突

JSweet执行转换以自动允许方法和私有字段具有相同的名称。另一方面,同一个类或具有子类链接的类中不允许使用相同名称的方法和公共字段。

为了避免由于这种JavaScript行为导致编程错误,JSweet添加了一个语义检查来检测类中的重复名称(这也考虑了在父类中定义的成员)。举个例子:

public class NameClashes {

// error: field name clashes with existing method name
public String a;

// error: method name clashes with existing field name
public void a() {
    return a;
}

}

方法重载

与TypeScript和JavaScript相反(但与Java类似),JSweet中可能有多个具有相同名称但具有不同参数的方法(所谓的重载)。我们区分了简单的重载和复杂的重载。简单重载是使用方法重载来定义可选参数。JSweet允许这个习惯用语,它对应于以下模板:

String m(String s, double n) { return s + n; }

// valid overloading (JSweet transpiles to optional parameter)

String m(String s) { return m(s, 0); }

在这种情况下,JSweet将生成JavaScript代码,只有一个方法具有可选参数的默认值,因此生成的程序的行为对应于原始程序。在这种情况下:

function m(s, n = 0) { return s + n; }

如果程序员尝试以不同方式使用重载,例如通过为同一方法名定义两个不同的实现,JSweet将回退复杂的重载,包括生成根实现(包含更多参数的方法)和一个辅助实现per overloading方法(用表示方法签名的后缀命名)。根实现是通用的,并通过测试给定参数的值和类型调度到其他实现。例如:

String m(String s, double n) { return s + n; }

String m(String s) { return s; }

生成以下(略微简化的)JavaScript代码:

function m(s, n) {

if(typeof s === 'string' && typeof n === 'number') {
    return s + n;
} else if(typeof s === 'string' && n === undefined) {
    return this.m$java_lang_String(s);
} else {
    throw new Error("invalid overload");
}

}

function m$java_lang_String(s) { return s; }

局部变量名称

在TypeScript / JavaScript中,局部变量可能与使用全局方法冲突。例如,使用alertDOM(jsweet.dom.Globals.alert)中的全局方法要求没有局部变量隐藏它:

import static jsweet.dom.Globals.alert;

[...]

public void m1(boolean alert) {

// JSweet compile error: name clash between parameter and method call
alert("test");

}

public void m2() {

// JSweet compile error: name clash between local variable and method call
String alert = "test";
alert(alert);

}

请注意,在调用全局方法时使用完全限定名称时也会发生此问题(这是因为限定条件在TypeScript / JavaScript中被删除)。在任何情况下,JSweet都会在发生此类问题时报告声音错误,以便程序员可以调整局部变量名称以避免与全局变量发生冲突。

测试对象的类型

要在运行时测试给定对象的类型,可以使用instanceofJava运算符,也可以使用 Object.getClass()函数。

instanceof

这instanceof是在运行时测试类型的建议和首选方法。JSweet将transpile到常规instanceof或一个 typeof取决于所测试的类型的操作(这将在回退 typeof对number,string和boolean核心类型)。

尽管不是必需的,但也可以使用实用方法直接使用typeof JSweet中的运算符jsweet.util.Lang.typeof。以下是有效类型测试的一些示例:

import static jsweet.util.Lang.typeof;

import static jsweet.util.Lang.equalsStrict;

[...]

Number n1 = 2;

Object n2 = 2;

int n3 = 2;

Object s = "test";

MyClass c = new MyClass();

assert n1 instanceof Number; // transpiles to a typeof

assert n2 instanceof Number; // transpiles to a typeof

assert n2 instanceof Integer; // transpiles to a typeof

assert !(n2 instanceof String); // transpiles to a typeof

assert s instanceof String; // transpiles to a typeof

assert !(s instanceof Integer); // transpiles to a typeof

assert c instanceof MyClass;

assert typeof(n3) == "number";

从JSweet版本1.1.0开始,instanceof接口上也允许运算符,因为JSweet会跟踪所有对象的所有实现接口。通过调用的对象中的附加隐藏属性来确保此接口跟踪,__interfaces并且包含对象实现的所有接口的名称(直接或通过在编译时确定的类继承树)。因此,如果instanceof 运算符的类型参数是接口,JSweet只是检查对象的 __interfaces字段是否存在并包含给定的接口。例如,当Point接口是这个代码在JSweet中完全有效:

Point p1 = new Point() {{ x=1; y=1; }};

[...]

assert p1 instanceof Point

Object.getClass() 和 X.class

在JSweet中,可以使用Object.getClass()on any实例。它实际上将返回类的构造函数。X.class如果X是类,using 也将返回构造函数。因此,以下断言将在JSweet中保留:

String s = "abc";

assert String.class == s.getClass()

在课堂上,您可以调用getSimpleName()或getName()函数。

String s = "abc";

assert "String" == s.getClass().getSimpleName()

assert String.class.getSimpleName() == s.getClass().getSimpleName()

请注意,getSimpleName()或者getName()函数也适用于接口。但是,您必须知道,X.class如果X是接口,将以字符串形式编码(保存接口的名称)。

限制和约束

由于所有数字都映射到JavaScript数字,因此JSweet不区分整数和浮点数。因此, 无论实际类型是什么n instanceof Integer,n instanceof Float都会给出相同的结果n。对于字符串和字符存在相同的限制,这些字符串和字符在运行时无法区分,但也适用于具有相同参数数量的函数。例如,一个实例IntFunction<R>将无法在运行时与a区分开Function<String,R>。

这些限制对函数重载有直接影响,因为重载使用instanceof运算符来决定调用哪个重载。

就像在JavaScript中工作时一样,序列化对象必须与其实际类正确“复活”,以便 instanceof操作员可以再次工作。例如,通过创建的点对象Point p = (Point)JSON.parse("{x:1,y:1}")不会对instanceof运算符起作用。如果您遇到这样的用例,您可以联系我们获取一些有用的JSweet代码以正确恢复对象类型。

lambda表达式中的变量作用域

已知JavaScript变量作用域给程序员带来了一些问题,因为可以从使用此变量的lambda外部更改对变量的引用。因此,JavaScript程序员不能依赖于在lambda范围之外声明的变量,因为当执行lambda时,该变量可能已在程序中的其他位置被修改。例如,以下程序显示了一个典型案例:

NodeList nodes = document.querySelectorAll(".control");

for (int i = 0; i < nodes.length; i++) {

HTMLElement element = (HTMLElement) nodes.$get(i); // final
element.addEventListener("keyup", (evt) -> {
    // this element variable will not change here
    element.classList.add("hit");
});

}

在JavaScript中(注意EcmaScript 6修复了这个问题),这样的程序将无法实现其目的,因为element事件监听器中使用的变量被for循环修改并且不保持期望值。在JSweet中,这些问题与最终的Java变量类似。在我们的示例中,element变量在lambda表达式中重新定义,以便封闭循环不会更改其值,因此程序的行为与Java类似(正如大多数程序员所预期的那样)。

这个范围

与JavaScript相反,与Java类似,将方法用作lambda将防止丢失对引用的引用this。例如,在action以下程序的方法中this,即使在方法中action被称为lambda 时,也保持正确的值main。虽然这对Java程序员来说似乎是合乎逻辑的,但JavaScript语义并不能确保这种行为。

package example;

import static jsweet.dom.Globals.console;

public class Example {

private int i = 8;
public Runnable getAction() {
    return this::action;
}
public void action() {
    console.log(this.i); // this.i is 8
}
public static void main(String[] args) {
    Example instance = new Example();
    instance.getAction().run();
}

}

重要的是要强调this通过与ES5 bind功能类似的机制确保正确的值。结果是函数引用被包装在函数中,这意味着函数指针(例如this::action)动态地创建包装函数。它在操作函数指针时有副作用,这个问题在本期 https://github.com/cincheo/js... 。

打包

打包是JavaScript的复杂点之一,尤其是来自Java时。JavaScript包装的复杂性归结为JavaScript本身没有定义任何包装这一事实。因此,多年来出现了许多事实上的解决方案和指南,使得对常规Java程序员的包装理解不安。JSweet提供了有用的选项并生成代码,以便通过使包装问题更加透明和大多数情况下的Java“简单”来简化Java程序员的生活。在本节中,我们将描述和解释典型的包装方案。

使用您的文件,无需任何包装

运行程序最常见和最简单的情况就是将每个生成的文件包含在HTML页面中。这不是任何包装选项的默认模式。例如,当您的程序定义两个类x.y.z.A并x.y.z.B在两个单独的文件中时,您可以按如下方式使用它们:

<script type="text/javascript" src="target/js/x/y/z/A.js"></script>

<script type="text/javascript" src="target/js/x/y/z/B.js"></script>

[...]

<!-- access a method later in the file -->

<script type="text/javascript">x.y.z.B.myMethod()</script>

这样做时,程序员需要非常谨慎,以避免文件之间的正向静态依赖关系。换句话说,A 类不能B在静态字段,静态初始化程序或静态导入中使用任何内容,否则在尝试加载页面时会导致运行时错误。此外,A该类不能扩展B该类。这些约束来自JavaScript / TypeScript,与JSweet无关。

可以想象,使用这种手动技术运行简单的程序很好,但是对于开发复杂的应用程序会变得非常不舒服。复杂的应用程序大多数时候使用适当的工具捆绑和/或打包程序,以避免必须手动处理JavaScript文件之间的依赖关系。

为浏览器创建捆绑包

为了避免必须手动处理依赖项,程序员使用捆绑工具将其类捆绑到一个文件中。这样的包使用以下内容包含在任何网页中:

<script type="text/javascript" src="target/js/bundle.js"></script>

[...]

<!-- access a method later in the file -->

<script type="text/javascript">x.y.z.B.myMethod()</script>

JSweet带有这样的捆绑设施。要创建一个包文件,只需设定true的bundleJSweet的选项。请注意,您还可以设置true的declaration,询问JSweet生成打字稿定义文件(可选bundle.d.ts)。此文件允许您以类型合适的方式使用/编译来自TypeScript的JSweet程序。

JSweet捆绑选项的“神奇之处”在于它分析源代码中的依赖关系,并在构建捆绑时负责解决前向引用。特别是,JSweet为静态字段和初始化器实现了一个惰性初始化机制,以便分解跨类的静态前向引用。程序员没有具体的附加声明使其工作(与TypeScript相反)。

请注意,它仍然存在一些小的限制(例如,在使用内部和匿名类时),但很少遇到这些限制,并且将在以后的版本中删除。

另请注意,如果您随module 选项一起指定选项,JSweet将引发错误bundle。

包装模块

首先,让我们从解释模块开始,重点关注Java 包(或TypeScript 命名空间)和模块之间的区别。如果您对差异感到满意,请跳过此部分。

包和模块是两个相似的概念,但适用于不同的上下文。必须将Java包理解为编译时命名空间。它们允许通过名称路径对程序进行编译时结构化,具有隐式或显式可见性规则。软件包通常对程序实际捆绑和部署的方式影响不大。

必须将模块理解为部署/运行时“捆绑”,这可以required由其他模块实现。与Java世界中最接近模块的概念可能是OSGi包。模块定义导入和导出的元素,以便它们创建强大的运行时结构,可用于独立部署软件组件,从而避免名称冲突。例如,对于模块,两个不同的库可以定义一个util.List类,并且实际上在同一个VM上运行和使用,没有命名问题(只要库被捆绑在不同的模块中)。

如今,许多库都是通过模块打包和访问的。在浏览器中使用模块的标准方法是AMD,但在Node.js中它是commonjs模块系统。

JSweet中的模块

JSweet支持用于打包的AMD,commonjs和UMD模块系统。JSweet定义了一个module选项(值:amd,commonjs或umd)。指定此选项时,JSweet会根据简单规则自动创建默认模块组织:一个文件=一个模块。

例如,当将module选项设置为打包时commonjs,可以编写:

node target/js/x/y/z/MyMainClass.js

模块系统将自动处理参考,并在需要时需要其他模块。在引擎盖下,JSweet分析了Java import语句并将它们转换为require指令。

注意:一旦使用该module选项编译程序,就可以使用适当的工具(如Browserify)将其打包为捆绑包,这样可以提供与使用bundleJSweet选项类似的输出。还要注意,同时指定时JSweet会引发错误module和bundle,这是排斥的选项。

外部模块

使用module选项编译JSweet程序时,必须将所有外部库和组件作为外部模块。只需使用@Module(name) 注释,JSweet就可以自动需要模块。在JSweet中,导入或使用带有注释的类或成员@Module(name)将在运行时自动需要相应的模块。请注意,只有使用该module选项生成代码时才会出现这种情况。如果该module选项处于禁用状态,@Module 则会忽略注释。

package def.jquery;

public final class Globals extends def.js.Object {

...
@jsweet.lang.Module("jquery")
native public static def.jquery.JQuery $(java.lang.String selector);
...

}

上面的代码显示了JSweet jQuery API的摘录。我们可以注意到,该$函数带有注释@Module("jquery")。因此,对此函数的任何调用都将触发jquery模块的要求 。

注意:未来版本中可能会提供模块的手动要求概念。但是,自动需求对于大多数程序员来说已经足够,并且隐藏了必须明确要求模块的复杂性。无论是否使用模块,它还具有使用相同代码的优点。

故障排除:当糖果没有正确定义@Module 注释时,可以在一个名为的特殊文件的注释中强制声明module_defs.java。例如,要强制将BABYLONBabylonjs candy 的 名称空间导出为 babylonjs模块,可以编写以下文件:

package myprogram;

// declare module "babylonjs" {

// export = BABYLON;

// }

请注意,JSweet项目只能定义一个module_defs.java文件,该文件应包含注释中的所有模块声明。另请注意,这是一个黑客攻击,首选方法是为糖果做出贡献来解决问题。

根包

Root包是一种调整生成的代码的方法,以便在生成的代码中擦除JSweet包,从而在运行时删除。要设置根包,只需定义一个package-info.java文件并使用@Root 包上的注释,如下所示:

@Root

package a.b.c;

上述声明意味着该c包是一个根包,即它将在生成的代码及其所有父包中被删除。因此,如果c包含一个包d和一个类C,它们将是运行时的顶级对象。换句话说,a.b.c.d成为 d,a.b.c.C变成C。

请注意,由于在封装之前放置的@Root封装被擦除,因此在封装之前不能定义任何类型@Root。在前面的示例中,a和b包必须是空包。

不使用模块时的行为(默认)

默认情况下,root包不会更改生成的文件的文件夹层次结构。例如,a.b.c.C该类仍将在<jsout>/a/b/c/C.js文件中生成(相对于<jsout> 输出目录)。但是,打开该noRootDirectories选项将删除根目录,以便为该文件a.b.c.C生成类<jsout>/C.js。

不使用模块时(默认),可以有多个@Root 包(但@Root包不能包含另一个@Root 包)。

使用模块时的行为

使用模块时(请参阅模块选项),只@Root允许一个@Root包,当有一个包时,其他包或类型不能超出该@Root包的范围。然后,生成的文件夹/文件层次结构从根包开始,以便实际擦除之前的所有文件夹。

包装JSweet jar(糖果)

糖果是一种Maven工件,包含从JSweet客户端程序轻松访问JavaScript库所需的所有内容。该库可以是外部JavaScript库,TypeScript程序或其他JSweet程序。

糖果的解剖学

与任何Maven工件一样,糖果具有组ID,工件ID(名称)和版本。此外,典型的糖果应包含以下元素:

编译的Java文件(* .class),以便您使用candy的客户端程序可以编译。

一个META-INF/candy-metadata.json包含transpiler的预期目标版本的文件(必须适应你的目标transpiler版)。

程序在d.ts文件中的声明,放在src/typingsjar 的 目录中。请注意,如果您打算使用JSweet生成TypeScript源代码(tsOnly选项),则这些定义不是必需的。在这种情况下,您可以将JavaScript生成委派给外部tsc编译器,并从另一个源访问TypeScript定义。

(可选)库的JavaScript包,它可以由JSweet客户端程序自动提取和使用。JSweet希望JavaScript遵循Webjars约定打包:http://www.webjars.org/。以这种方式打包时,使用糖果的JSweet转换器将自动在该candiesJsOut选项给出的目录中提取捆绑的JavaScript (默认值:) js/candies。

以下是该META-INF/candy-metadata.json文件的示例:

{

"transpilerVersion": "2.0.0"

}

如何从JSweet程序创建糖果

使用JSweet构建应用程序时的典型用例是在多个其他JSweet模块/应用程序之间共享公共库或模块。请注意,由于JSweet糖果是常规的Maven工件,只要它不使用任何JavaScript API,它也可以由常规Java程序使用。

因此,项目中的典型示例是拥有包含DTO和公共实用程序函数的公共库,这些库可以在用JSweet编写的Web客户端(例如使用angular或knockout库)和在JSweet中编写的移动客户端之间共享。 (例如使用离子库)。好消息是这个公地 库也可以由Java服务器(JEE,Spring,...)使用,因为DTO不使用任何JavaScript,并且包装在糖果中的已编译Java代码可以在Java VM上运行。这非常有用,因为这意味着当您在自己喜欢的IDE中开发此项目时,您将能够重构某些DTO和通用API,它将直接影响您的Java服务器代码,Web客户端代码和移动客户端码!

我们提供了一个快速入门项目来帮助您从这样的用例开始:https://github.com/cincheo/jsweet-candy-quickstart

如何为现有的JavaScript或TypeScript库创建糖果

我们提供了一个快速入门项目来帮助您从这样的用例开始:https://github.com/cincheo/jsweet-candy-js-quickstart

扩展转换器

JSweet是一个从Java到TypeScript的Open Transpiler。这意味着它为程序员提供了调整/扩展JSweet如何生成中间TypeScript代码的方法。调整转换器是一种避免重复性任务并使其自动化的解决方案。

例如,假设您有一个遗留Java代码,它使用Java API来序列化对象(writeObject/ readObject)。使用JSweet,您可以轻松地从程序中删除这些方法,以便生成的JavaScript代码不受任何特定于Java的序列化习惯用法的影响。

作为另一个例子,假设您有一个使用Java API的Java遗留代码库,它接近(但不完全)您希望在最终JavaScript代码中使用的JavaScript API。使用JSweet,您可以编写一个适配器,它将自动将所有Java调用映射到相应的JavaScript调用。

最后但并非最不重要的是,您可以根据上下文调整JSweet以利用某些特定的API。例如,如果您知道目标浏览器支持ES6映射,则可以使用ES6映射,或者在其他情况下仅使用仿真或更简单的实现。您可以调整代码以避免在知道给定移动浏览器不能很好地支持时使用某些画布或WebGL基元。依此类推......使用像JSweet这样的Open Transpiler有许多实际的应用程序,您可能需要或不必使用它们,但无论如何最好知道什么是可能的。

可以使用注释以声明方式(而不是以编程方式)完成调整。注释可以添加到Java程序(硬编码),也可以集中在一个唯一的配置文件中,这样它们甚至不会出现在Java代码中(我们称之为软注释)。使用注释非常简单直观。但是,当需要对转换器进行复杂的定制时,很可能注释不再足够。在这种情况下,程序员应该使用JSweet扩展API,这需要所有可以通过注释完成的调优等等。扩展API允许在所谓的打印机适配器的上下文中访问Java AST(抽象语法树) 。打印机适配器遵循装饰器模式,以便它们可以链接以扩展和/或覆盖JSweet打印出中间TypeScript代码的方式。

核心注释

该包jsweet.lang定义了各种注释,可用于调整JSweet生成中间TypeScript代码的方式。在这里,我们解释这些注释,并举例说明如何使用它们。

@Erased:此注释类型用于应在生成时擦除的元素。它可以应用于任何程序元素。如果应用于类型,则会自动删除强制转换和构造函数调用,从而可能导致程序不一致。如果应用于方法,则将删除调用,可能导致程序不一致,尤其是在表达式中使用调用的结果时。由于潜在的不一致性,程序员应仔细使用此注释,并应用于未使用的元素(也使用元素擦除)。

@Root:此包注释用于为已转换的TypeScript / JavaScript指定根包,这意味着此包和子包中的所有已转换引用将与此根包相关。作为示例,给定 org.mycompany.mylibrary根包(带注释@Root),该类org.mycompany.mylibrary.MyClass实际上将对应 MyClass于JavaScript运行时。同样, org.mycompany.mylibrary.mypackage.MyClass将会转变为 mypackage.MyClass。

@Name(String value):此批注允许定义将用于最终生成的代码(而不是Java名称)的名称。当元素的名称不是有效的Java标识符时,可以使用它。按照惯例,JSweet实现了一个内置的约定,以节省使用@Name注解:Keyword在Java中transpiles到keyword,当keyword是一个Java的关键字(如 catch,finally,int,long,等等)。

@Replace(String value):此注释允许程序员通过TypeScript实现替换方法体实现。注释的值包含由JSweet转换器生成的TypeScript。代码将由TypeScript转换器检查。替换代码可以包含使用类似胡须的约定({{variableName}} 替换的变量。以下是支持的变量列表:

{{className}}:当前的课程。

{{methodName}}:当前方法名称。

{{body}}:当前方法的主体。此变量的典型用法是将原始行为包装在lambda中。例如: / before code / let _result = () => { {{body}} }(); / after code / return _result;。

{{baseIndent}}:替换方法的缩进。可用于生成格式良好的代码。

{{indent}}:用缩进代替。可用于生成格式良好的代码。

以下示例说明了@Erased和 @Replace注释的使用。这里,@Erased注释用于readObject从生成的代码中删除方法,因为它在JavaScript中没有意义(它是一种特定于Java序列化的方法)。该@Replace注释允许定义直接打字稿/ JavaScript实现的searchAddress方法。

class Person {

List<String> addresses = new ArrayList<String>();

@Erased

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {

[...]

}

@Replace("return this.addresses.filter(address => address.match(regex))[0]")

public String searchAddress(String regex) {

Optional<String> match = addresses.stream().filter(address -> address.matches(regex)).findFirst();
      return match.isPresent()?match.get():null;

}

}

使用JSweet注释可以灵活地在Java和JavaScript之间共享类。JavaScript中的无用方法被擦除,并且一些方法可以具有针对Java和JavaScript的不同实现。

集中注释 jsweetconfig.json

JSweet支持在唯一配置文件(jsweetconfig.json)中定义注释。程序员想要在该文件中定义注释而不是在Java源代码中定义注释的原因有很多。

注释或注释内容可能因上下文而异。根据上下文,具有不同的配置文件可能是方便的。使用其他配置文件切换配置文件比更改程序中的所有注释更容易。

向Java程序添加注释可以方便地在本地调整程序(在给定元素上)。但是,在某些情况下,类似的注释应该适用于一组程序元素,以便自动化程序的全局调整。在这种情况下,通过使用表达式来安装注释会更方便,该表达式将立即匹配一组程序元素。这种机制类似于面向方面的软件设计中可以找到的切入点机制。它允许以声明方式捕获全局修改。

在Java源代码中使用注释需要引用JSweet API(jsweet.lang包),对于希望Java代码尽可能保持“纯”的程序员来说,这可能被视为不必要的依赖。

JSweet配置文件(jsweetconf.json)是一个包含配置条目列表的JSON文件。在配置条目中,可以使用以下结构定义所谓的全局过滤器:

<annotation>: {

"include": <match_expressions>,
  "exclude": <match_expressions>

}

<annotation>要添加的注释在哪里,具有潜在参数,include以及exclude是匹配表达式的列表。如果程序中的元素的签名与include列表中的任何表达式匹配,并且与列表中的任何表达式都不匹配,则程序中的元素将使用批注进行批注exclude。

匹配表达式是一种简化的正则表达式,支持以下通配符:

*匹配在程序元素的签名任何令牌或令牌的子部分(令牌是签名的识别符的一部分,例如A.m(java.lang.String)包含标记A,m和 java.lang.String)。

** 匹配程序元素签名中的任何令牌列表。

..匹配程序元素签名中的任何令牌列表。(同**)

! 否定表达式(仅限第一个字符)。

例如:

// all the elements and subelements (fields, methods, ...) in the x.y.z package

x.y.z.**

// all the methods in the x.y.z.A class

x.y.z.A.*(..)

// all the methods taking 2 arguments in the texttt{x.y.z.A} class

x.y.z.A. ( ,*)

// all fields called aField in all the classes of the program

**.aField

这是一个包含完整jsweetconfig.json 配置文件的更完整示例。

{

// all classes and packages in x.y.z will become top level

"@Root": {

"include": [ "x.y.z" ]

},

// do not generate any TypeScript code for Java-specific methods

"@Erased": {

"include": [ "**.writeObject(..)", "**.readObject(..)", "**.hashCode(..)" ]

},

// inject logging in all setters and getters of the x.y.z.A class

"@Replace('console.info('entering {{methodName}}'); let _result = () => { {{body}} }(); console.info('returning '+_result); return _result;')": {

"include": [ "x.y.z.A.set*(*)", "x.y.z.A.get*()", "x.y.z.A.is*()" ]

}

}

请注意,注释仅使用简单名称定义。那是因为它们是核心JSweet注释(定义于jsweet.lang)。非核心注释可以以相同的方式添加,但程序员必须使用完全限定的名称。

使用适配器进行编程调整

在为特定目的调整生成时(通常在支持其他Java库时),通过注释进行声明性调整会快速达到限制。因此,JSweet提供了一个API,以便程序员可以扩展JSweet生成中间TypeScript代码的方式。编写这样的适应程序类似于编写常规Java程序,除了它将应用于您的程序来转换它们。因此,它属于所谓的元程序类别(即程序使用其他程序作为数据)。由于程序员可能会编写导致无效代码的扩展,因此拥有中间编译层变得非常方便。如果生成的代码无效,则TypeScript到JavaScript编译将引发错误,从而允许程序员修复扩展代码。

介绍扩展API

org.jsweet.transpiler.extension 包中提供了扩展API 。它基于工厂模式(org.jsweet.transpiler.JSweetFactory),允许程序员通过子类化来调整转换器的所有主要组件。实际上,大多数调整都可以通过创建新的打印机适配器来完成,作为子类org.jsweet.transpiler.extension.PrinterAdapter。适配器是核心扩展机制,因为它们是可链接的并且可以组合(它是一种装饰模式)。JSweet在默认适配链中使用默认适配器,然后调整JSweet将包括向链添加新适配器。

适配器通常会执行三种操作来调整生成的代码:

将Java类型映射到TypeScript类型。

以声明方式(使用全局过滤器)或以编程方式(使用注释管理器)向程序添加注释。

覆盖定义的打印方法,PrinterAdapter以覆盖默认生成的TypeScript核心。打印方法采用基于标准javax.lang.model.elementAPI的程序元素 。它为表达式和语句(org.jsweet.transpiler.extension.model)的程序元素提供了该API的扩展。

以下模板显示了编程适配器时的典型部分。首先,适配器必须扩展PrinterAdapter或任何其他适配器。它必须定义一个构造函数来获取父适配器,当在链中插入适配器时,它将由JSweet设置。

public class MyAdapter extends PrinterAdapter {

public MyAdapter(PrinterAdapter parent) {
    super(parent);
    ...

在构造函数中,适配器通常将Java类型映射到TypeScript类型。

// will change the type in variable/parameters declarations
    addTypeMapping("AJavaType", "ATypeScriptType");
    // you may want to erase type checking by mapping to 'any'
    addTypeMapping("AJavaType2", "any");
    [...]

在构造函数中,适配器还可以比使用jsweetconfig.json语法时更灵活的方式添加注释。

// add annotations dynamically to the AST, with global filters
    addAnnotation("jsweet.lang.Erased", //
            "**.readObject(..)", //
            "**.writeObject(..)", //
            "**.hashCode(..)");
    // or with annotation managers (see the Javadoc and the example below)
    addAnnotationManager(new AnnotationManager() { ... });
}

最重要的是,适配器可以覆盖最重要的AST元素的替换方法。通过重写这些方法,适配器将改变JSweet生成中间TypeScript代码的方式。要打印代码,可以使用print在根PrinterAdapter类中定义的方法。例如,下面的代码将取代所有new AJavaType(...)带new ATypeScriptType(...)。

@Override
public boolean substituteNewClass(NewClassElement newClass) {
    // check if the 'new' applies to the right class
    if ("AJavaType".equals(newClass.getTypeAsElement().toString())) {
        // the 'print' method will generate intermediate TypeScript code
        print("new ATypeScriptType(")
                .printArgList(newClass.getArguments()).print(")");
        // once some code has been printed, you should return true to break
        // the adapter chain, so your code will replace the default one
        return true;
    }
    // if not substituted, delegate to the adapter chain
    return super.substituteNewClass(newClass);
}

最有用的替换方法仍然是调用替换,它通常用于将Java API映射到类似的JavaScript API。

@Override
public boolean substituteMethodInvocation(MethodInvocationElement invocation) {
    // substitute potential method invocation here
    [...]
    // delegate to the adapter chain
    return super.substituteMethodInvocation(invocation);
}

}

还要注意在打印Java类型后插入代码的特殊方法:

@Override
public void afterType(TypeElement type) {
    super.afterType(type);
    // insert whatever TypeScript you need here
    [...]
}

适配器有许多应用(参见下面的示例)。除了在编译时调整代码生成和支持Java API之外,根据目标上下文,当编译的代码不符合预期标准时,适配器也可用于引发错误。另一个非常有用的用例,它允许生成代理。例如,可以编写一个适配器来生成JavaScript存根,以调用使用JAX-RS部署的Java服务。

安装和激活适配器

编写适配器后,需要编译它并将其添加到适配器链中。使用JSweet执行此操作的最简单方法是将其放在jsweet_extension需要在项目JSweet的根目录中创建的目录中。在该目录中,您可以直接为适配器添加Java源文件,这些文件将由JSweet动态编译。例如,您可以添加两个自定义的适配器CustomAdapter1.java和 CustomAdapter2.java在jsweet_extension/com/mycompany/。

然后,为了激活该适配器,您只需要jsweetconfig.json在项目的根目录中添加该 文件并定义 adapters配置选项,如下所示:

{

// JSweet will add the declared adapters at the beginning of the default

// chain... you can add as many adapters as you need

adapters: [ "com.mycompany.CustomAdapter1", "com.mycompany.CustomAdapter2" ]

}

Hello世界适配器

在这里,我们将逐步介绍如何java.util.Date在Java程序中查找类型时调整JSweet生成以生成字符串来代替日期。

首先,HelloWorldAdapter.java在jsweet_extension项目根目录的目录中创建文件 。将以下代码复制并粘贴到该文件中:

import org.jsweet.transpiler.extension.PrinterAdapter;

public class HelloWorldAdapter extends PrinterAdapter {

public HelloWorldAdapter(PrinterAdapter parent) {
    super(parent);
    addTypeMapping(java.util.Date.class.getName(), "string");
}

}

其次,在项目的根目录中,jsweetconfig.json 使用以下配置创建文件:

{

adapters: [ "HelloWorldAdapter" ]

}

完成。现在,您可以在以下简单的Java DTO上尝试此扩展:

package source.extension;

import java.util.Date;

/**

  • A Hello World DTO.

*

  • @author Renaud Pawlak

*/

public class HelloWorldDto {

private Date date;
/**
 * Gets the date.
 */
public Date getDate() {
    return date;
}
/**
 * Sets the date.
 */
public void setDate(Date date) {
    this.date = date;
}

}

生成的代码应如下所示:

/ Generated from Java with JSweet 2.XXX - http://www.jsweet.org /

namespace source.extension {

/**
 * A Hello World DTO.
 *
 * @author Renaud Pawlak
 * @class
 */
export class HelloWorldDto {
    /*private*/ date : string;
    public constructor() {
        this.date = null;
    }
    /**
     * Gets the date.
     * @return {string}
     */
    public getDate() : string {
        return this.date;
    }
    /**
     * Sets the date.
     * @param {string} date
     */
    public setDate(date : string) {
        this.date = date;
    }
}
HelloWorldDto["__class"] = "source.extension.HelloWorldDto";

}

请注意,所有日期类型都已按预期转换为字符串。顺便说一下,还要注意JSDoc支持,这使得JSweet成为一个强大的工具,可以从Java创建记录良好的JavaScript API(文档注释也可以在适配器中调整!)。

扩展示例

以下部分说明了使用JSweet适配器和5个实际示例。大多数这些适配器都内置了JSweet(在org.jsweet.transpiler.extension包中),可以通过将它们添加到适配器链来激活,如上所述。如果要修改适配器,只需将代码复制粘贴到jsweet_extension 目录中并更改名称即可。

示例1:用于重命名私有字段的适配器

这个简单的适配器通过添加两个下划线作为前缀来重命名非公共成员。请注意,如果希望从其他JSweet项目中声明的子类访问受保护的字段,则这可能是危险的。因此,您可能需要谨慎使用或根据自己的需要修改代码。

此适配器是演示如何使用注释管理器的一个很好的示例。注释管理器用于将(软)注释添加到由某些Java代码(以编程方式)驱动的程序元素。注释管理器将添加到上下文中,并将链接到其他现有注释管理器(可能由其他适配器添加)。注释管理器必须实现该manageAnnotation方法,该方法将告知是否应在给定元素上添加,移除或保持给定注释不变。如果注释具有参数,则注释管理器应实现该注释getAnnotationValue以指定值。

在此示例中,注释管理器将注释添加@jsweet.lang.Name 到所有非公共元素,以便重命名它们并将下划线添加到初始名称。

package org.jsweet.transpiler.extension;

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 org.jsweet.transpiler.util.Util;

public class AddPrefixToNonPublicMembersAdapter extends PrinterAdapter {

public AddPrefixToNonPublicMembersAdapter(PrinterAdapter parentAdapter) {
    super(parentAdapter);
    // add a custom annotation manager to the chain
    addAnnotationManager(new AnnotationManager() {

        @Override
        public Action manageAnnotation(Element element, String annotationType) {
            // add the @Name annotation to non-public elements
            return "jsweet.lang.Name".equals(annotationType)
                    && isNonPublicMember(element) ? Action.ADD : Action.VOID;
        }

        @Override
        public <T> T getAnnotationValue(Element element,
                String annotationType, String propertyName,
                Class<T> propertyClass, T defaultValue) {
            // set the name of the added @Name annotation (value)
            if ("jsweet.lang.Name".equals(annotationType) && isNonPublicMember(element)) {
                return propertyClass.cast("__" + element.getSimpleName());
            } else {
                return null;
            }
        }

        private boolean isNonPublicMember(Element element) {
            return (element instanceof VariableElement || element instanceof ExecutableElement)
                    && element.getEnclosingElement() instanceof TypeElement
                    && !element.getModifiers().contains(Modifier.PUBLIC)
                    && Util.isSourceElement(element);
        }
    });
}

}

示例2:使用ES6 Maps的适配器

JSweet映射的默认实现如下:

如果键类型是字符串,则映射将转换为常规JavaScript对象,其中属性名称将是键。

如果键类型是对象(除字符串以外的任何对象),则映射将转换为条目列表。实现效率非常低,因为查找密钥需要遍历条目以找到正确的条目密钥。

在某些情况下,您可能希望获得更有效的地图实现。如果你瞄准现代浏览器(或期望有适当的polyfill),你可以简单地使用Map通过ES6标准化的对象。这样做需要适配器执行以下操作:

擦除Java Map类型并将其替换为JavaScript Map 类型或实际any类型,因为您可能希望Object在键是字符串时保留 实现。

用相应的JavaScript构造替换地图的构造。

用相应的JavaScript调用替换Java映射上的调用。

请注意,以下适配器是部分实现,应扩展为支持更多案例并根据您自己的要求进行调整。此外,此实现生成非类型化JavaScript,以避免在编译路径中使用ES6 API。

package org.jsweet.transpiler.extension;

import java.util.Arrays;

import java.util.HashMap;

import java.util.Hashtable;

import java.util.Map;

import java.util.TreeMap;

import javax.lang.model.element.Element;

import org.jsweet.transpiler.model.MethodInvocationElement;

import org.jsweet.transpiler.model.NewClassElement;

public class MapAdapter extends PrinterAdapter {

// all the Java types that will be translated to ES6 maps
static String[] mapTypes = {
    Map.class.getName(), HashMap.class.getName(),
    TreeMap.class.getName(), Hashtable.class.getName() };

public MapAdapter(PrinterAdapter parent) {
    super(parent);
    // rewrite all Java map and compatible map implementations types
    // note that we rewrite to 'any' because we don't want to require the
    // ES6 API to compile (all subsequent accesses will be untyped)
    for (String mapType : mapTypes) {
        addTypeMapping(mapType, "any");
    }
}

@Override
public boolean substituteNewClass(NewClassElement newClass) {
    String className = newClass.getTypeAsElement().toString();
    // map the map constructor to the global 'Map' variable (untyped access)
    if (Arrays.binarySearch(mapTypes, className) >= 0) {
        // this access is browser/node-compatible
        print("new (typeof window == 'undefined'?global:window)['Map'](")
                .printArgList(newClass.getArguments()).print(")");
        return true;
    }
    // delegate to the adapter chain
    return super.substituteNewClass(newClass);
}

@Override
public boolean substituteMethodInvocation(MethodInvocationElement invocation) {
    if (invocation.getTargetExpression() != null) {
        Element targetType = invocation.getTargetExpression().getTypeAsElement();
        if (Arrays.binarySearch(mapTypes, targetType.toString()) >= 0) {
            // Java Map methods are mapped to their JavaScript equivalent
            switch (invocation.getMethodName()) {
            case "put":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression()).print(".set(")
                        .printArgList(invocation.getArguments())
                        .print(")");
                return true;
            // although 'get' has the same name, we still rewrite it in case
            // another extension would provide it's own implementation
            case "get":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression()).print(".get(")
                        .printArgList(invocation.getArguments())
                        .print(")");
                return true;
            case "containsKey":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression()).print(".has(")
                        .printArgList(invocation.getArguments())
                        .print(")");
                return true;
            // we use the ES6 'Array.from' method in an untyped way to
            // transform the iterator in an array
            case "keySet":
                printMacroName(invocation.getMethodName());
                print("(<any>Array).from(")
                        .print(invocation.getTargetExpression()).print(".keys())");
                return true;
            case "values":
                printMacroName(invocation.getMethodName());
                print("(<any>Array).from(")
                        .print(invocation.getTargetExpression()).print(".values())");
                return true;
            // in ES6 maps, 'size' is a property, not a method
            case "size":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression()).print(".size");
                return true;
            }
        }

    }
    // delegate to the adapter chain
    return super.substituteMethodInvocation(invocation);
}

}

示例3:支持Java BigDecimal的适配器

Java的BigDecimal API是一个非常好的API,可以避免典型的浮点精度问题,尤其是在处理货币时。默认情况下,此API在JavaScript中不可用,并且很难模拟。GWT提供了使用Java实现的BigDecimal API的仿真,但JSweet提出了另一种方法,包括将BigDecimal API映射到名为Big.js的现有JavaScript API。与模拟API相比,映射到现有JS库有几个优点:

该实现已经在JavaScript中提供,因此模拟Java库的工作量较少。

该实现是纯JavaScript,专为JavaScript而设计。因此我们可以假设仿真效率更高,甚至更便携。

生成的代码不受任何Java API的影响,这使得JavaScript更加友好,并且可以与现有的JavaScript程序更加互操作(遗留的JavaScript显然使用Big.js对象,如果没有,我们可以决定调整适配器)。

以下代码显示了调整JavaScript生成以将Java的BigDecimal API映射到Big JavaScript库的适配器。此扩展需要在JSweet类路径中提供big.js糖果:https://github.com/jsweet-candies/candy-bigjs。

package org.jsweet.transpiler.extension;

import java.math.BigDecimal;

import javax.lang.model.element.Element;

import org.jsweet.transpiler.extension.PrinterAdapter;

import org.jsweet.transpiler.model.MethodInvocationElement;

import org.jsweet.transpiler.model.NewClassElement;

public class BigDecimalAdapter extends PrinterAdapter {

public BigDecimalAdapter(PrinterAdapter parent) {
    super(parent);
    // all BigDecimal types are mapped to Big
    addTypeMapping(BigDecimal.class.getName(), "Big");
}

@Override
public boolean substituteNewClass(NewClassElement newClass) {
    String className = newClass.getTypeAsElement().toString();
    // map the BigDecimal constructors
    if (BigDecimal.class.getName().equals(className)) {
        print("new Big(").printArgList(newClass.getArguments()).print(")");
        return true;
    }
    // delegate to the adapter chain
    return super.substituteNewClass(newClass);
}

@Override
public boolean substituteMethodInvocation(MethodInvocationElement invocation) {
    if (invocation.getTargetExpression() != null) {
        Element targetType = invocation.getTargetExpression().getTypeAsElement();
        if (BigDecimal.class.getName().equals(targetType.toString())) {
            // BigDecimal methods are mapped to their Big.js equivalent
            switch (invocation.getMethodName()) {
            case "multiply":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression())
                        .print(".times(").printArgList(invocation.getArguments())
                        .print(")");
                return true;
            case "add":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression())
                        .print(".plus(").printArgList(invocation.getArguments())
                        .print(")");
                return true;
            case "scale":
                printMacroName(invocation.getMethodName());
                // we assume that we always have a scale of 2, which is a
                // good default if we deal with currencies...
                // to be changed/implemented further
                print("2");
                return true;
            case "setScale":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression())
                        .print(".round(").print(invocation.getArguments().get(0))
                        .print(")");
                return true;
            case "compareTo":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression()).print(".cmp(")
                        .print(invocation.getArguments().get(0))
                        .print(")");
                return true;
            case "equals":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression()).print(".eq(")
                        .print(invocation.getArguments().get(0))
                        .print(")");
                return true;
            }
        }

    }
    // delegate to the adapter chain
    return super.substituteMethodInvocation(invocation);
}

}

示例4:将枚举映射到字符串的适配器

此示例调整JavaScript生成以删除枚举并将其替换为字符串。它仅适用于使用@注释的枚举jsweet.lang.StringType。

例如:@StringType enum MyEnum { A, B, C }将被擦除,并且对枚举常量的所有后续访问将映射到简单的字符串(MyEnum.A => "A", MyEnum.B => "B", MyEnum.C => "C")。通常,方法声明如void m(MyEnum e) {...}将打印为void m(e : string) {...}。当然,调用 xxx.m(MyEnum.A)将打印为xxx.m("A")。

package org.jsweet.transpiler.extension;

import javax.lang.model.element.Element;

import javax.lang.model.element.ElementKind;

import org.jsweet.JSweetConfig;

import org.jsweet.transpiler.model.CaseElement;

import org.jsweet.transpiler.model.ExtendedElement;

import org.jsweet.transpiler.model.MethodInvocationElement;

import org.jsweet.transpiler.model.VariableAccessElement;

public class StringEnumAdapter extends PrinterAdapter {

private boolean isStringEnum(Element element) {
    // note: this function could be improved to exclude enums that have
    // fields or methods other than the enum constants
    return element.getKind() == ElementKind.ENUM
            && hasAnnotationType(element, JSweetConfig.ANNOTATION_STRING_TYPE);
}

public StringEnumAdapter(PrinterAdapter parent) {
    super(parent);
    // eligible enums will be translated to string in JS
    addTypeMapping((typeTree, name) ->
            isStringEnum(typeTree.getTypeAsElement()) ? "string" : null);

    // ignore enum declarations with a programmatic annotation manager
    addAnnotationManager(new AnnotationManager() {
        @Override
        public Action manageAnnotation(Element element, String annotationType) {
            // add the @Erased annotation to string enums
            return JSweetConfig.ANNOTATION_ERASED.equals(annotationType)
                    && isStringEnum(element) ? Action.ADD : Action.VOID;
        }
    });

}

@Override
public boolean substituteMethodInvocation(MethodInvocationElement invocation) {
    if (invocation.getTargetExpression() != null) {
        Element targetType = invocation.getTargetExpression().getTypeAsElement();
        // enum API must be erased and use plain strings instead
        if (isStringEnum(targetType)) {
            switch (invocation.getMethodName()) {
            case "name":
                printMacroName(invocation.getMethodName());
                print(invocation.getTargetExpression());
                return true;
            case "valueOf":
                printMacroName(invocation.getMethodName());
                print(invocation.getArgument(0));
                return true;
            case "equals":
                printMacroName(invocation.getMethodName());
                print("(").print(invocation.getTargetExpression()).print(" == ")
                        .print(invocation.getArguments().get(0)).print(")");
                return true;
            }
        }
    }
    return super.substituteMethodInvocation(invocation);
}

@Override
public boolean substituteVariableAccess(VariableAccessElement variableAccess) {
    // accessing an enum field is replaced by a simple string value
    // (MyEnum.A => "A")
    if (isStringEnum(variableAccess.getTargetElement())) {
        print("/"" + variableAccess.getVariableName() + "/"");
        return true;
    }
    return super.substituteVariableAccess(variableAccess);
}

@Override
public boolean substituteCaseStatementPattern(CaseElement caseStatement,
        ExtendedElement pattern) {
    // map enums to strings in case statements
    if (isStringEnum(pattern.getTypeAsElement())) {
        print("/"" + pattern + "/"");
        return true;
    }
    return super.substituteCaseStatementPattern(caseStatement, pattern);
}

}

示例5:用于生成JavaScript JAX-RS代理/存根的适配器

在服务器上使用Java和在客户端上使用JavaScript实现WEB或移动应用程序是一种常见的用例。通常,JEE / Jackson服务器将通过JAX-RS规范公开REST API,HTML5客户端必须使用XMLHttpRequest或更高级别的库(如jQuery)调用此API 。但是,手动编写HTTP调用编码有许多缺点:

它需要使用特定的API(XHR,jQuery),这对所有程序员来说都不容易,并且可能意味着不同的编程风格会使代码更难以阅读和维护。

它要求程序员手动处理序列化/反序列化,同时可以通过使用注释驱动的生成编程自动完成。

它导致未经检查的调用,这意味着程序员很容易在服务/路径/参数的名称和预期的DTO中产生错误。没有重构和内容辅助。

使用JSweet适配器,使用 afterType方法可以轻松自动生成类型良好的TypeScript存根,并通过使用服务API和JAX-RS注释执行调用REST服务所需的操作。这种类型的工具属于所谓的生成编程。

以下代码仅是适配器的部分实现,它将内省程序的模型并在TypeScript中生成相应的存根。它并不意味着可操作,因此您需要修改以适合您自己的用例。

import javax.lang.model.element.Element;

import javax.lang.model.element.ExecutableElement;

import javax.lang.model.element.TypeElement;

import javax.lang.model.element.VariableElement;

import javax.lang.model.type.TypeKind;

import javax.ws.rs.GET;

import javax.ws.rs.POST;

import javax.ws.rs.PUT;

import javax.ws.rs.Path;

class JaxRSStubAdapter extends PrinterAdapter {

public JaxRSStubAdapter(PrinterAdapter parent) {
    super(parent);
    // erase service classes (server-side only)
    addAnnotationManager(new AnnotationManager() {
        @Override
        public Action manageAnnotation(Element element, String annotationType) {
            return JSweetConfig.ANNOTATION_ERASED.equals(annotationType)
                    && hasAnnotationType(element, Path.class.getName()) ?
                    Action.ADD : Action.VOID;
        }
    });
}

@Override
public void afterType(TypeElement type) {
    super.afterType(type);
    if (hasAnnotationType(type, Path.class.getName())) {
        // actually generates the JAX-RS stub
        println().printIndent();
        print("class ").print(type.getSimpleName()).print(" {");
        startIndent();
        String typePathAnnotationValue = getAnnotationValue(type,
                Path.class.getName(), String.class, null);
        String typePath = typePathAnnotationValue != null ? typePathAnnotationValue : "";
        for (Element e : type.getEnclosedElements()) {
            if (e instanceof ExecutableElement
                    && hasAnnotationType(e, GET.class.getName(),
                            PUT.class.getName(), Path.class.getName())) {
                ExecutableElement method = (ExecutableElement) e;
                println().printIndent().print(method.getSimpleName().toString())
                        .print("(");
                for (VariableElement parameter : method.getParameters()) {
                    print(parameter.getSimpleName())
                            .print(" : ").print(getMappedType(parameter.asType()))
                            .print(", ");
                }
                print("successHandler : (");
                if (method.getReturnType().getKind() != TypeKind.VOID) {
                    print("result : ").print(getMappedType(method.getReturnType()));
                }
                print(") => void, errorHandler?: () => void").print(") : void");
                print(" {").println().startIndent().printIndent();
                String pathAnnotationValue = getAnnotationValue(e, Path.class.getName(),
                        String.class, null);
                String path = pathAnnotationValue != null ? pathAnnotationValue : "";
                String httpMethod = "POST";
                if(hasAnnotationType(e, GET.class.getName())) {
                    httpMethod = "GET";
                }
                if(hasAnnotationType(e, POST.class.getName())) {
                    httpMethod = "POST";
                }
                String[] consumes = getAnnotationValue(e, "javax.ws.rs.Consumes",
                        String[].class, null);
                if (consumes == null) {
                    consumes = new String[] { "application/json" };
                }
                // actual code to be done
                print("// modify JaxRSStubAdapter to generate an HTTP invocation here")
                        .println().printIndent();
                print("//   - httpMethod: " + httpMethod).println().printIndent();
                print("//   - path: " + typePath + path).println().printIndent();
                print("//   - consumes: " + consumes[0]);
                println().endIndent().printIndent().print("}");
            }
        }
        println().endIndent().printIndent().print("}");
    }
}

}

注意:对于编译,您需要在类路径中使用JAX-RS API。

<dependency>

<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1-m07</version>

</dependency>

作为示例,让我们考虑以下JAX-RS服务。

@Path("/hello")

public class HelloWorldService {

@GET
@Path("/{param}")
@Produces(MediaType.APPLICATION_JSON)
public HelloWorldDto getMsg(@PathParam("param") String msg) {
    String output = "service says : " + msg;
    return new HelloWorldDto(output);
}

}

使用以下DTO:

public class HelloWorldDto {

private String msg;
public HelloWorldDto(String msg) {
    super();
    this.msg = msg;
}
public String getMsg() {
    return msg;
}
public void setMsg(String msg) {
    this.msg = msg;
}

}

如果您应用JSweet增强版JaxRSStubAdapter,您将获得以下TypeScript代码(和相应的JavaScript):

export class HelloWorldDto {

/*private*/ msg : string;
public constructor(msg : string) {
    this.msg = msg;
}
public getMsg() : string {
    return this.msg;
}
public setMsg(msg : string) {
    this.msg = msg;
}

}

HelloWorldDto["__class"] = "HelloWorldDto";

class HelloWorldService {

getMsg(msg : string,
        successHandler : (result : HelloWorldDto) => void,
        errorHandler?: () => void) : void {
    // modify JaxRSStubAdapter to generate an HTTP invocation here
    //   - httpMethod: GET
    //   - path: /hello/{param}
    //   - consumes: application/json
}

}

因此,您需要做的就是修改适配器的代码以生成实际的调用代码来代替注释。完成后,您可以使用生成的JavaScript代码作为捆绑包以良好的方式访问您的服务。此外,您可以使用JSweet生成服务和DTO的TypeScript定义,以便您的TypeScript客户端输入良好(请参阅JSweet的declaration 选项)。

示例6:禁用全局变量的适配器

这是一个非常特殊的适配器,因为它不会真正生成任何代码,但它会在源代码不符合某些编码标准时报告错误。在这里,我们实现了一个简单的约束,当用户尝试声明全局变量时(即在Globals类中声明的JSweet非最终静态字段中)报告错误。

package org.jsweet.transpiler.extension;

import javax.lang.model.element.Element;

import javax.lang.model.element.ElementKind;

import javax.lang.model.element.Modifier;

import javax.lang.model.element.TypeElement;

import javax.lang.model.element.VariableElement;

import org.jsweet.transpiler.JSweetProblem;

public class DisallowGlobalVariablesAdapter extends PrinterAdapter {

public DisallowGlobalVariablesAdapter(PrinterAdapter parentAdapter) {
    super(parentAdapter);
}

@Override
public void afterType(TypeElement type) {
    // we only check for static variables that are in a Globals class but
    // this could be generalized to any static variable
    if (!type.getQualifiedName().toString().startsWith("def.")
            && type.getSimpleName().toString().equals("Globals")) {
        for (Element member : type.getEnclosedElements()) {
            if (member.getKind() == ElementKind.FIELD) {
                VariableElement field = (VariableElement) member;
                // only non-final static variable have side effect
                if (field.getModifiers().contains(Modifier.STATIC)
                        && !field.getModifiers().contains(Modifier.FINAL)) {
                    report(field, JSweetProblem.USER_ERROR, "global variables are not allowed");
                }
            }
        }
    }
    super.afterType(type);
}

}

此适配器属于静态分析类别,可用于(与代码生成一起)检查输入是否符合预期的编码指南。增强此适配器以添加对任何静态字段的检查将很容易。一个很好的功能是在使用特定注释(例如@AllowSideEffect)注释字段时禁用检查。

附录1:JSweet转换器选项

[-h|--help]

[-w|--watch]

Start a process that watches the input directories for changes and
    re-run transpilation on-the-fly.

[-v|--verbose]

Turn on all levels of logging.

[--encoding <encoding>]

Force the Java compiler to use a specific encoding (UTF-8, UTF-16, ...).
    (default: UTF-8)

[--jdkHome <jdkHome>]

Set the JDK home directory to be used to find the Java compiler. If not
    set, the transpiler will try to use the JAVA_HOME environment variable.
    Note that the expected JDK version is greater or equals to version 8.

(-i|--input) input1:input2:...:inputN

An input directory (or column-separated input directories) containing
    Java files to be transpiled. Java files will be recursively looked up in
    sub-directories. Inclusion and exclusion patterns can be defined with
    the 'includes' and 'excludes' options.

[--includes includes1:includes2:...:includesN ]

A column-separated list of expressions matching files to be included
    (relatively to the input directory).

[--excludes excludes1:excludes2:...:excludesN ]

A column-separated list of expressions matching files to be excluded
    (relatively to the input directory).

[(-d|--defInput) defInput1:defInput2:...:defInputN ]

An input directory (or column-separated input directories) containing
    TypeScript definition files (*.d.ts) to be used for transpilation.
    Definition files will be recursively looked up in sub-diredctories.

[--noRootDirectories]

Skip the root directories (i.e. packages annotated with
    @jsweet.lang.Root) so that the generated file hierarchy starts at the
    root directories rather than including the entire directory structure.

[--tsout <tsout>]

Specify where to place generated TypeScript files. (default: .ts)

[(-o|--jsout) <jsout>]

Specify where to place generated JavaScript files (ignored if jsFile is
    specified). (default: js)

[--disableSinglePrecisionFloats]

By default, for a target version >=ES5, JSweet will force Java floats to
    be mapped to JavaScript numbers that will be constrained with ES5
    Math.fround function. If this option is true, then the calls to
    Math.fround are erased and the generated program will use the JavaScript
    default precision (double precision).

[--tsOnly]

Do not compile the TypeScript output (let an external TypeScript
    compiler do so).

[--ignoreDefinitions]

Ignore definitions from def.* packages, so that they are not generated
    in d.ts definition files. If this option is not set, the transpiler
    generates d.ts definition files in the directory given by the tsout
    option.

[--declaration]

Generate the d.ts files along with the js files, so that other programs
    can use them to compile.

[--dtsout <dtsout>]

Specify where to place generated d.ts files when the declaration option
    is set (by default, d.ts files are generated in the JavaScript output
    directory - next to the corresponding js files).

[--candiesJsOut <candiesJsOut>]

Specify where to place extracted JavaScript files from candies.
    (default: js/candies)

[--sourceRoot <sourceRoot>]

Specify the location where debugger should locate Java files instead of
    source locations. Use this flag if the sources will be located at
    run-time in a different location than that at design-time. The location
    specified will be embedded in the sourceMap to direct the debugger where
    the source files will be located.

[--classpath <classpath>]

The JSweet transpilation classpath (candy jars). This classpath should
    at least contain the core candy.

[(-m|--module) <module>]

The module kind (none, commonjs, amd, system or umd). (default: none)

[-b|--bundle]

Bundle up all the generated code in a single file, which can be used in
    the browser. The bundle files are called 'bundle.ts', 'bundle.d.ts', or
    'bundle.js' depending on the kind of generated code. NOTE: bundles are
    not compatible with any module kind other than 'none'.

[(-f|--factoryClassName) <factoryClassName>]

Use the given factory to tune the default transpiler behavior.

[--sourceMap]

Generate source map files for the Java files, so that it is possible to
    debug Java files directly with a debugger that supports source maps
    (most JavaScript debuggers).

[--enableAssertions]

Java 'assert' statements are transpiled as runtime JavaScript checks.

[--header <header>]

A file that contains a header to be written at the beginning of each
    generated file. If left unspecified, JSweet will generate a default
    header.

[--workingDir <workingDir>]

The directory JSweet uses to store temporary files such as extracted
    candies. JSweet uses '.jsweet' if left unspecified.

[--targetVersion <targetVersion>]

The EcmaScript target (JavaScript) version. Possible values: [ES3, ES5,
    ES6] (default: ES3)

附录2:包装和静态行为

本附录解释了有关包装的一些静态行为。

调用main方法时

调用main方法时,取决于程序的打包方式。

module:关闭,bundle:关闭。使用默认打包时,一个Java源文件对应于一个生成的JavaScript文件。在这种情况下,在浏览器中加载文件时,将在文件末尾调用所有主要方法。

module:关闭,bundle开启。当bundle选项打开和 module选项关闭,主要方法称为在束的端部。

module:开,bundle关。使用模块打包(module 选项),一个Java包对应一个模块。对于模块,必须在程序中只有一个main方法,它将是从中计算模块依赖图的全局入口点。主模块(具有主方法的模块)将直接或可传递地使用所有其他模块。主模块评估结束时将调用main方法。

由于模块,最好在应用程序中只有一个主要方法。

静态和继承依赖项

在TypeScript中,程序员需要处理与静态字段和初始化程序有关的类的排序。通常,无法使用尚未定义的类的静态成员初始化静态成员。此外,类不能扩展尚未定义的类。这个前向依赖性问题在评估生成的JavaScript代码时会触发运行时错误,这对程序员来说可能非常烦人,并且可能需要使用外部JavaScript捆绑工具,例如Br​​owserify。

JSweet的静态延迟初始化允许在给定文件中的静态前向引用,并在bundle 设置选项时在整个包内。此外,在捆绑一组文件时,JSweet会分析继承树并执行部分顺序排列以消除继承树中的前向引用。请注意,TypeScript包提供了类似的功能,但需要手动声明引用,这对程序员来说不方便。

总结一下,以下是程序员遵循的指导方针,具体取决于打包方法:

module:关闭,bundle:关闭。每个Java文件生成一个JavaScript文件。程序员必须注意在HTML页面中以正确的顺序包含文件,这样就没有关于继承和静态的前向引用。在给定文件中,允许静态转发引用,但尚不支持继承转发引用(这将在即将发布的版本中支持)。

module:关闭,bundle开启。此配置生成一个独特的浏览器兼容的捆绑文件,该文件可以包含在HTML页面中。在这里,程序员不必关注文件中的所有前向引用。与Java完全一样,顺序无关紧要。在一个文件中,程序员仍然需要处理继承转发引用(换句话说,子类必须在其父类之后声明)(这将在即将发布的版本中得到支持)。

module:commonjs,amd或umd,bundle:off。此配置为每个Java包生成一个模块文件,以便可以在模块系统中使用它们。例如,使用commonjs模块类将允许程序在Node.js上运行。在该配置中,程序应该包含一个main方法,并且只应加载包含main方法的模块文件(因为它会小心加载所有其他模块)。此配置在单个文件中强制使用相同的约束(继承中没有前向引用)。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<h1><a href="#jsweet-language-specifications" id="jsweet-language-specifications">JSweet Language Specifications</a>
</h1>
<p>Version: 2.x (snapshot)</p>
<p>Author : Renaud Pawlak</p>
<p>Author assistant: Louis Grignon</p>
<p>JSweet JavaDoc API: http://www.jsweet.org/core-api-javadoc/</p>
<p>Note: this markdown is automatically generated from the Latex source file. Do not modify directly.</p>
<h2><a href="#content" id="content">Content</a></h2>
<ul>
  <li><a href="#basic-concepts">Basic concepts</a>
    <ul>
      <li><a href="#core-types-and-objects">Core types and objects</a></li>
      <li><a href="#classes">Classes</a></li>
      <li><a href="#interfaces">Interfaces</a></li>
      <li><a href="#untyped-objects-maps">Untyped objects (maps)</a></li>
      <li><a href="#enums">Enums</a></li>
      <li><a href="#globals">Globals</a></li>
      <li><a href="#optional-parameters-and-overloading">Optional parameters and
        overloading</a></li>
    </ul>
  </li>
  <li><a href="#bridging-to-external-javascript-elements">Bridging to external JavaScript
    elements</a>
    <ul>
      <li><a href="#examples">Examples</a></li>
      <li><a href="#rules-for-writing-definitions-a.k.a.-bridges">Rules for writing definitions (a.k.a.
        bridges)</a></li>
      <li><a href="#untyped-accesses">Untyped accesses</a></li>
      <li><a href="#mixins">Mixins</a></li>
      <li><a href="#generating-jsweet-candies-from-existing-typescript-definitions">Generating JSweet candies from
        existing TypeScript
        definitions</a></li>
    </ul>
  </li>
  <li><a href="#auxiliary-types">Auxiliary types</a>
    <ul>
      <li><a href="#functional-types">Functional types</a></li>
      <li><a href="#object-types">Object types</a></li>
      <li><a href="#string-types">String types</a></li>
      <li><a href="#tuple-types">Tuple types</a></li>
      <li><a href="#union-types">Union types</a></li>
      <li><a href="#intersection-types">Intersection types</a></li>
    </ul>
  </li>
  <li><a href="#semantics">Semantics</a>
    <ul>
      <li><a href="#main-methods">Main methods</a></li>
      <li><a href="#initializers">Initializers</a></li>
      <li><a href="#arrays-initialization-and-allocation">Arrays initialization and
        allocation</a></li>
      <li><a href="#asynchronous-programming">Asynchronous programming</a></li>
      <li><a href="#name-clashes">Name clashes</a></li>
      <li><a href="#testing-the-type-of-an-object">Testing the type of an object</a></li>
      <li><a href="#variable-scoping-in-lambda-expressions">Variable scoping in lambda
        expressions</a></li>
      <li><a href="#scope-of-this">Scope of <em>this</em></a></li>
    </ul>
  </li>
  <li><a href="#packaging">Packaging</a>
    <ul>
      <li><a href="#use-your-files-without-any-packaging">Use your files without any
        packaging</a></li>
      <li><a href="#creating-a-bundle-for-a-browser">Creating a bundle for a
        browser</a></li>
      <li><a href="#packaging-with-modules">Packaging with modules</a></li>
      <li><a href="#root-packages">Root packages</a></li>
      <li><a href="#packaging-a-jsweet-jar-candy">Packaging a JSweet jar (candy)</a></li>
    </ul>
  </li>
  <li><a href="#extending-the-transpiler">Extending the transpiler</a>
    <ul>
      <li><a href="#core-annotations">Core annotations</a></li>
      <li><a href="#centralizing-annotations-in-jsweetconfig.json">Centralizing annotations in
        <code>jsweetconfig.json</code></a></li>
      <li><a href="#programmatic-tuning-with-adapters">Programmatic tuning with
        adapters</a></li>
      <li><a href="#extension-examples">Extension examples</a></li>
    </ul>
  </li>
  <li><a href="#appendix-1-jsweet-transpiler-options">Appendix 1: JSweet transpiler
    options</a></li>
  <li><a href="#appendix-2-packaging-and-static-behavior">Appendix 2: packaging and static
    behavior</a>
    <ul>
      <li><a href="#when-main-methods-are-invoked">When main methods are invoked</a></li>
      <li><a href="#static-and-inheritance-dependencies">Static and inheritance
        dependencies</a></li>
    </ul>
  </li>
</ul>
<h2><a href="#basic-concepts" id="basic-concepts">Basic concepts</a></h2>
<p>This section presents the JSweet language basic concepts. One must keep
  in mind that JSweet, as a Java-to-JavaScript transpiler, is an extension
  of Java at compile-time, and executes as JavaScript at runtime. JSweet
  aims at being a trade-off between Java and JavaScript, by respecting as
  much as possible the Java semantics, but without loosing
  interoperability with JavaScript. So, in a way, JSweet can be seen as a
  fusion between Java and JavaScript, trying to get the best of both
  worlds in one unique and consistent language. In some cases, it is hard
  to get the best of both worlds and JSweet makes convenient and practical
  choices.</p>
<p>Because JSweet is an open JavaScript transpiler, the user can tune the
  JavaScript generation without much efforts, thus making other choices
  than default ones to map Java to JavaScript. For example, if the way
  JSweet implements Java maps does not suit your context or use case, you
  can program a JSweet extension to override the default strategy.
  Programming and activating a JSweet extension is fully explained in
  Section
  <a href="#extending-the-transpiler" data-reference-type="ref" data-reference="extending-the-transpiler">6</a>.</p>
<h3><a href="#core-types-and-objects" id="core-types-and-objects">Core types and objects</a></h3>
<p>JSweet allows the use of primitive Java types, core Java objects
  (defined in <code>java.lang</code>, many JDK classes (especially <code>java.util</code> but
  not only), and of core JavaScript objects, which are defined in the
  <code>def.js</code> package. Next, we describe the use of such core types and
  objects.</p>
<h4><a href="#primitive-java-types" id="primitive-java-types">Primitive Java types</a></h4>
<p>JSweet allows the use of Java primitive types (and associated literals).</p>
<ul>
  <li>
    <p><code>int</code>, <code>byte</code>, <code>short</code>, <code>double</code>, <code>float</code> are all
      converted to
      JavaScript numbers (TypeScript <code>number</code> type). Precision usually
      does not matter in JSweet, however, casting to <code>int</code>, <code>byte</code>, or
      <code>short</code> forces the number to be rounded to the right-length integer.</p>
  </li>
  <li>
    <p><code>char</code> follows the Java typing rules but is converted to a
      JavaScript <code>string</code> by the transpiler.</p>
  </li>
  <li>
    <p><code>boolean</code> corresponds to the JavaScript <code>boolean</code>.</p>
  </li>
  <li>
    <p><code>java.lang.String</code> corresponds to the JavaScript <code>string</code>. (not per
      say a primitive type, but is immutable and used as the class of
      string literals in Java)</p>
  </li>
</ul>
<p>A direct consequence of that conversion is that it is not always
  possible in JSweet to safely overload methods with numbers or
  chars/strings. For instance, the methods <code>pow(int, int)</code> and
  <code>pow(double, double)</code> may raise overloading issues. With a JSweet
  context, the transpiler will be able to select the right method, but the
  JavaScript interoperability may be a problem. In short, since there is
  no difference between <code>n instanceof Integer</code> and <code>n instanceof Double</code>
  (it both means <code>typeof n === ’number’</code>) calling <code>pow(number, number)</code>
  from JavaScript will randomly select one implementation or the other.
  This should not be always a problem, but in some particular cases, it
  can raise subtle errors. Note that in these cases, the programmers will
  be able to tune the JavaScript generation, as it is fully explained in
  Section
  <a href="#extending-the-transpiler" data-reference-type="ref" data-reference="extending-the-transpiler">6</a>.</p>
<p>Examples of valid statements:</p>
<pre><code class="java">// warning '==' behaves like JavaScript '===' at runtime
int i = 2;
assert i == 2;
double d = i + 4;
assert d == 6;
String s = "string" + '0' + i;
assert s == "string02";
boolean b = false;
assert !b;
</code></pre>
<p>The <code>==</code> operator behaves like the JavaScript strict equals operator
  <code>===</code> so that it is close to the Java semantics. Similarly, <code>!=</code> is
  mapped to <code>!==</code>. There is an exception to that behavior which is when
  comparing an object to a <code>null</code> literal. In that case, JSweet translates
  to the loose equality operators so that the programmers see no
  distinction between <code>null</code> and <code>undefined</code> (which are different in
  JavaScript but it may be confusing to Java programmers). To control
  whether JSweet generates strict or loose operators, you can use the
  following helper methods: <code>jsweet.util.Lang.$strict</code> and
  <code>jsweet.util.Lang.$loose</code>. Wrapping a comparison operator in such a
  macro will force JSweet to generate a strict or loose operator. For
  example:</p>
<pre><code class="java">import static jsweet.util.Lang.$loose;
[...]
int i = 2;
assert i == 2; // generates i === 2
assert !((Object)"2" == i);
assert $loose((Object)"2" == i); // generates "2" == i
</code></pre>
<h4><a href="#allowed-java-objects" id="allowed-java-objects">Allowed Java objects</a></h4>
<p>By default, JSweet maps core Java objects and methods to JavaScript
  through the use of built-in macros. It means that the Java code is
  directly substituted with a valid JavaScript code that implements
  similar behavior. A default mapping is implemented for most useful core
  Java classes (<code>java.lang</code>, <code>java.util</code>). When possible (and when it
  makes sense), some partial mapping is implemented for other JDK classes
  such as input and output streams, locales, calendars, reflection, etc.</p>
<p>With the default behavior, we can point the following limitations:</p>
<ul>
  <li>
    <p>Extending a JDK class is in general not possible, except for some
      particular contexts. If extending a JDK class is required, should
      should consider to refactor your program, or use a JavaScript
      runtime (such as J4TS), which would allow it.</p>
  </li>
  <li>
    <p>The Java reflection API (<code>java.lang.reflect</code>) is limited to very
      basic operations. It is possible to access the classes and the
      members, but it is not possible to access types. A more complete
      support of Java reflection would be possible, but it would require a
      JSweet extension.</p>
  </li>
  <li>
    <p>Java 8 streams are not supported yet, but it would be simple to
      support them partially (contributions are welcome).</p>
  </li>
</ul>
<p>Examples of valid statements:</p>
<pre><code class="java">Integer i = 2;
assert i == 2;
Double d = i + 4d;
assert d.toString() == "6";
assert !((Object) d == "6");
BiFunction<String, Integer, String> f = (s, i) -> { return s.substring(i); };
assert "bc" == f.apply("abc", 1);
</code></pre>
<h4><a href="#getting-more-java-apis" id="getting-more-java-apis">Getting more Java APIs</a></h4>
<p>With JSweet, it is possible to add a runtime that implements Java APIs
  in JavaScript, so that programmers can access more Java APIs and thus
  share the same code between Java and JavaScript. The core project for
  implementing Java APIs for JSweet is J4TS
  (<a href="https://github.com/cincheo/j4ts">https://github.com/cincheo/j4ts</a>) and contains a quite complete
  implementation of <code>java.util.*</code> classes and other core package. J4TS is
  based on a fork of the GWT’s JRE emulation, but it is adapted to be
  compiled with JSweet. Programmers can use J4TS as a regular JavaScript
  library available in our Maven repository.</p>
<p>Although J4TS cannot directly implement the Java core types that
  conflict with JavaScript ones (<code>Boolean</code>, <code>Byte</code>, <code>Short</code>, <code>Integer</code>,
  <code>Long</code>, <code>Float</code>, <code>Double</code>, <code>Character</code>, <code>String</code>), J4TS
  contributes to
  supporting the static part of them by providing helpers for each class
  (<code>javaemul.internal.BooleanHelper</code>, <code>javaemul.internal.ByteHelper</code>,
  ...). When the JSweet transpiler meets a static Java method on a type
  <code>java.lang.T</code> that is not supported as a built-in macro, it delegates to
  <code>javaemul.internal.THelper</code>, which can provide a JavaScript
  implementation for the given static method. That way, by using J4TS,
  programmers can use even more of the core JRE API.</p>
<h4><a href="#java-arrays" id="java-arrays">Java arrays</a></h4>
<p>Arrays can be used in JSweet and are transpiled to JavaScript arrays.
  Array initialization, accesses and and iteration are all valid
  statements.</p>
<pre><code class="java">int[] arrayOfInts = { 1, 2, 3, 4};
assert arrayOfInts.length == 4;
assert arrayOfInts[0] == 1;
int i = 0;
for (int intItem : arrayOfInts) {
    assert arrayOfInts[i++] == intItem;
}
</code></pre>
<h4><a href="#core-javascript-api" id="core-javascript-api">Core JavaScript API</a></h4>
<p>The core JavaScript API is defined in <code>def.js</code> (the full documentation
  can be found at <a href="http://www.jsweet.org/core-api-javadoc/">http://www.jsweet.org/core-api-javadoc/</a>). Main
  JavaScript classes are:</p>
<ul>
  <li>
    <p><code>def.js.Object</code>: JavaScript Object class. Common ancestor for
      JavaScript objects functions and properties.</p>
  </li>
  <li>
    <p><code>def.js.Boolean</code>: JavaScript Boolean class. A wrapper for boolean
      values.</p>
  </li>
  <li>
    <p><code>def.js.Number</code>: JavaScript Number class. A wrapper for numerical
      values.</p>
  </li>
  <li>
    <p><code>def.js.String</code>: JavaScript String class. A wrapper and constructor
      for strings.</p>
  </li>
  <li>
    <p><code>def.js.Function</code>: JavaScript Function class. A constructor for
      functions.</p>
  </li>
  <li>
    <p><code>def.js.Date</code>: JavaScript Date class, which enables basic storage
      and retrieval of dates and times.</p>
  </li>
  <li>
    <p><code>def.js.Array<T></code>: JavaScript Array class. It is used in the
      construction of arrays, which are high-level, list-like objects.</p>
  </li>
  <li>
    <p><code>def.js.Error</code>: JavaScript Error class. This class implements
      <code>java.lang.RuntimeException</code> and can be thrown and caught with <code>try</code>
      ... <code>catch</code> statements.</p>
  </li>
</ul>
<p>When using JavaScript frameworks, programmers should use this API most
  of the time, which is HTML5 compatible and follows the JavaScript latest
  supported versions. However, for objects that need to be used with Java
  literals (numbers, booleans, and strings), the use of the <code>java.lang</code>
  package classes is recommended. For instance, the jQuery API declares
  <code>$(java.lang.String)</code> instead of <code>$(def.js.String)</code>. This allows the
  programmer to write expressions using literals, such as <code>$("a")</code> (for
  selecting all links in a document).</p>
<p>With JSweet, programmers can easily switch from the Java to JavaScript
  API (and conversely) depending on their needs. The <code>jsweet.util.Lang</code>
  class defines convenient static methods to cast back and forth core Java
  objects to their corresponding JavaScript objects. For instance the
  <code>string(...)</code> method will allow the programmer to switch from the Java
  to the JavaScript strings and conversely.</p>
<pre><code class="java">import static jsweet.util.Lang.string;
// str is a Java string, but is actually a JavaScript string at runtime
String str = "This is a test string";
// str is exactly the same string object, but shown through the JS API
def.js.String str2 = string(str);
// valid: toLowerCase it defined both in Java and JavaScript
str.toLowerCase();
// this method is not JS-compatible, so a macro generates the JS code
str.equalsIgnoreCase("abc");
// direct call to the JS substr method on the JavaScript string
string(str).substr(1);
// or
str2.substr(1);
</code></pre>
<p>Note: for code sharing between a JavaScript client and a Java server for
  instance, it is better to use Java APIs only and avoid JavaScript ones.
  JavaScript API will compile valid Java bytecode but trying to execute
  them on a JVM will raise unsatisfied link errors.</p>
<p>Here is another example that shows the use of the <code>array</code> method to
  access the <code>push</code> method available on JavaScript arrays.</p>
<pre><code class="java">import static jsweet.util.Lang.array;
String[] strings = { "a", "b", "c" };
array(strings).push("d");
assert strings[3] == "d";
</code></pre>
<h3><a href="#classes" id="classes">Classes</a></h3>
<p>Classes in JSweet fully support all types of Java classes declarations.
  For example:</p>
<pre><code class="java">public class BankAccount {
    public double balance = 0;
    public double deposit(double credit) {
        balance += credit;
        return this.balance;
    }
}
</code></pre>
<p>Which is transpiled to the following JavaScript code:</p>
<pre><code class="java">var BankAccount = (function () {
    function BankAccount() {
        this.balance = 0;
    }
    BankAccount.prototype.deposit = function(credit) {
        this.balance += credit;
        return this.balance;
    };
    return BankAccount;
})();
</code></pre>
<p>Classes can define constructors, have super classes and be instantiated
  exactly like in Java. Similarly to Java, inner classes and anonymous
  classes are allowed in JSweet (since version 1.1.0). JSweet supports
  both static and regular inner/anonymous classes, which can share state
  with enclosing classes. Still like in Java, anonymous classes can access
  final variables declared in their scope. For example, the following
  declarations are valid in JSweet and will mimic the Java semantics at
  runtime so that Java programmers can benefit all the features of the
  Java language.</p>
<pre><code class="java">abstract class C {
    public abstract int m();
}
public class ContainerClass {
    // inner class
    public class InnerClass {
        public I aMethod(final int i) {
            // anonymous class
            return new C() {
                @Override
                public int m() {
                    // access to final variable i
                    return i;
                }
            }
        }
    }
}
</code></pre>
<h3><a href="#interfaces" id="interfaces">Interfaces</a></h3>
<p>In JSweet, an interface can be use like in Java. However, on contrary to
  Java, there is no associated class available as runtime. When using
  interfaces, JSweet generates code to emulate specific Java behaviors
  (such as <code>instanceof</code> on interfaces).</p>
<p>JSweet supports Java 8 static and default methods. However default
  methods are experimental so far and you should use them at your own
  risks.</p>
<p>In JSweet, interfaces are more similar to interfaces in TypeScript than
  in Java. It means that they must be seen as object signatures, which can
  specify functions, but also properties. In order to allow using fields
  as properties when defining interfaces, JSweet allows the use of regular
  classes annotated with <code>@jsweet.lang.Interface</code>. For example, the
  following interface types an object <code>Point</code> with 2 properties.</p>
<pre><code class="java">@Interface
public class Point {
    public double x;
    public double y;
}
</code></pre>
<p>To Java programmers, this may look like a very odd way to define an
  object, but you must remember that it is not a class, but a type for a
  JavaScript object. As such, it does not go against the OOP principles.
  We can create a JavaScript object typed after the interface. Note that
  the following code is not actually creating an instance of the <code>Point</code>
  interface, it is creating an object that conforms to the interface.</p>
<pre><code class="java">Point p1 = new Point() {{ x=1; y=1; }};
</code></pre>
<p>This object creation mechanism is a TypeScript/JavaScript mechanism and
  shall not be confused with anonymous classes, which is a Java-like
  construction. Because <code>Point</code> is annotated with <code>@Interface</code>, the
  transpiled JavaScript code will be similar to:</p>
<pre><code class="java">var p1 = Object.defineProperty({ x:1, y:1 }, "_interfaces", ["Point"]);
</code></pre>
<p>Note that, for each object, JSweet keeps track of which interface it was
  created from and of all the potential interfaces implemented by its
  class. This interface tracking system is implemented as a special object
  property called <code>__interfaces</code>. Using that property, JSweet allows the
  use of the <code>instanceof</code> operator on interfaces like in Java.</p>
<h4><a href="#optional-fields-in-interfaces" id="optional-fields-in-interfaces">Optional fields in interfaces</a></h4>
<p>Interfaces can define <em>optional fields</em>, which are used to report errors
  when the programmer forgets to initialize a mandatory field in an
  object. Supporting optional fields in JSweet is done through the use of
  <code>@jsweet.lang.Optional</code> annotations. For instance:</p>
<pre><code class="java">@Interface
public class Point {
    public double x;
    public double y;
    @Optional
    public double z = 0;
}
</code></pre>
<p>It is the JSweet compiler that will check that the fields are correctly
  initialized, when constructing an object from an interface.</p>
<pre><code class="java">// no errors (z is optional)
Point p1 = new Point() {{ x=1; y=1; }};
// JSweet reports a compile error since y is not optional
Point p2 = new Point() {{ x=1; z=1; }};
</code></pre>
<h4><a href="#special-javascript-functions-in-interfaces" id="special-javascript-functions-in-interfaces">Special
  JavaScript functions in interfaces</a></h4>
<p>In JavaScript, objects can have properties and functions, but can also
  (not exclusively), be used as constructors and functions themselves.
  This is not possible in Java, so JSweet defines special functions for
  handling these cases.</p>
<ul>
  <li>
    <p><code>$apply</code> is used to state that the object can be used as a function.</p>
  </li>
  <li>
    <p><code>$new</code> is used to state that the object can be used as a
      constructor.</p>
  </li>
</ul>
<p>For instance, if an object <code>o</code> is of interface <code>O</code> that defines
  <code>$apply()</code>, writing:</p>
<pre><code class="java">o.$apply();
</code></pre>
<p>Will transpile to:</p>
<pre><code class="java">o();
</code></pre>
<p>Similarly, if <code>O</code> defines <code>$new()</code>:</p>
<pre><code class="java">o.$new();
</code></pre>
<p>Will transpile to:</p>
<pre><code class="java">new o();
</code></pre>
<p>Yes, it does not make sense in Java, but in JavaScript it does!</p>
<h3><a href="#untyped-objects-maps" id="untyped-objects-maps">Untyped objects (maps)</a></h3>
<p>In JavaScript, object can be seen as maps containing key-value pairs
  (key is often called <em>index</em>, especially when it is a number). So, in
  JSweet, all objects define the special functions (defined on
  <code>def.js.Object</code>):</p>
<ul>
  <li>
    <p><code>$get(key)</code> accesses a value with the given key.</p>
  </li>
  <li>
    <p><code>$set(key,value)</code> sets or replace a value for the given key.</p>
  </li>
  <li>
    <p><code>$delete(key)</code> deletes the value for the given key.</p>
  </li>
</ul>
<h4><a href="#reflectiveuntyped-accesses" id="reflectiveuntyped-accesses">Reflective/untyped accesses</a></h4>
<p>The functions <code>$get(key)</code>, <code>$set(key,value)</code> and <code>$delete(key)</code> can be
  seen as a simple reflective API to access object fields and state. Note
  also the static method <code>def.js.Object.keys(object)</code>, which returns all
  the keys defined on a given object.</p>
<p>The following code uses this API to introspect the state of an object
  <code>o</code>.</p>
<pre><code class="java">for(String key : def.js.Object.keys(o)) {
  console.log("key=" + key +  " value=" + o.$get(key));
});
</code></pre>
<p>When not having the typed API of a given object, this API can be useful
  to manipulate the object in an untyped way (of course it should be
  avoided as much as possible).</p>
<h4><a href="#untyped-objects-initialization" id="untyped-objects-initialization">Untyped objects initialization</a>
</h4>
<p>One can use the <code>$set(key,value)</code> function to create new untyped object.
  For instance:</p>
<pre><code class="java">Object point = new def.js.Object() {{ $set("x", 1); $set("y", 1); }};
</code></pre>
<p>It transpiles also to:</p>
<pre><code class="java">var point = { "x": 1, "y": 1};
</code></pre>
<p>As a shortcut, one can use the <code>jsweet.util.Lang.$map</code> function, which
  transpiles to the exact same JavaScript code:</p>
<pre><code class="java">import static jsweet.util.Lang.$map;
[...]
Object point = $map("x", 1, "y", 1);
</code></pre>
<h4><a href="#indexed-objects" id="indexed-objects">Indexed objects</a></h4>
<p>The type of keys and values can be overloaded for every object. For
  example, the <code>Array<T></code> class, will define keys as numbers and values as
  objects conforming to type <code>T</code>.</p>
<p>In the case of objects indexed with number keys, it is allowed to
  implement the <code>java.lang.Iterable</code> interface so that it is possible to
  use they in <em>foreach</em> loops. For instance, the <code>NodeList</code> type (from the
  DOM) defines an indexed function:</p>
<pre><code class="java">@Interface
class NodeList implements java.lang.Iterable {
    public double length;
    public Node item(double index);
    public Node $get(double index);
}
</code></pre>
<p>In JSweet, you can access the node list elements with the <code>$get</code>
  function, and you can also iterate with the <em>foreach</em> syntax. The
  following code generates fully valid JavaScript code.</p>
<pre><code class="java">NodeList nodes = ...
for (int i = 0; i < nodes.length; i++) {
    HTMLElement element = (HTMLElement) nodes.$get(i);
    [...]
}
// same as:
NodeList nodes = ...
for (Node node : nodes) {
    HTMLElement element = (HTMLElement) node;
    [...]
}
</code></pre>
<h3><a href="#enums" id="enums">Enums</a></h3>
<p>JSweet allows the definition of enums similarly to Java. The following
  code declares an enum with tree possible values (<code>A</code>, <code>B</code>, and <code>C</code>).</p>
<pre><code class="java">enum MyEnum {
    A, B, C
}
</code></pre>
<p>The following statements are valid statements in JSweet.</p>
<pre><code class="java">MyEnum e = MyEnum.A;
assert MyEnum.A == e;
assert e.name() == "A";
assert e.ordinal() == 0;
assert MyEnum.valueOf("A") == e;
assert array(MyEnum.values()).indexOf(MyEnum.valueOf("C")) == 2;
</code></pre>
<p>Like Java enums, additional methods, constructors and fields can be
  added to enums.</p>
<pre><code class="java">enum ScreenRatio {
    FREE_RATIO(null),
    RATIO_4_3(4f / 3),
    RATIO_3_2(1.5f),
    RATIO_16_9(16f / 9),
    RATIO_2_1(2f / 1f),
    SQUARE_RATIO(1f);

    private final Float value;

    private MyComplexEnum(Float value) {
        this.value = value;
    }

    public Float getValue() {
        return value;
    }
}
</code></pre>
<h4><a href="#enums-portability-notes" id="enums-portability-notes">Enums portability notes</a></h4>
<p>Simple enums are translated to regular TypeScript enums, that is to say
  numbers. In JavaScript, at runtime, an enum instance is simple encode as
  its ordinal. So, JSweet enums are easy to share with TypeScript enums
  and a JSweet program can interoperate with a TypeScript program even
  when using enums.</p>
<p>Enums with additional members are also mapped to TypeScript enums, but
  an additional class is generated to store the additional information.
  When interoperating with TypeScript, the ordinal will remain, but the
  additional information will be lost. The programmers wanting to share
  enums with TypeScript should be aware of that behavior.</p>
<h3><a href="#globals" id="globals">Globals</a></h3>
<p>In Java, on contrary to JavaScript, there is no such thing as global
  variables or functions (there are only static members, but even those
  must belong to a class). Thus, JSweet introduces reserved <code>Globals</code>
  classes and <code>globals</code> packages. These have two purposes:</p>
<ul>
  <li>
    <p>Generate code that has global variables and functions (this is
      discouraged in Java)</p>
  </li>
  <li>
    <p>Bind to existing JavaScript code that defines global variables and
      functions (as many JavaScript frameworks do)</p>
  </li>
</ul>
<p>In Globals classes, only static fields (global variables) and static
  methods (global functions) are allowed. Here are the main constraints
  applying to Globals classes:</p>
<ul>
  <li>
    <p>no non-static members</p>
  </li>
  <li>
    <p>no super class</p>
  </li>
  <li>
    <p>cannot be extended</p>
  </li>
  <li>
    <p>cannot be used as types like regular classes</p>
  </li>
  <li>
    <p>no public constructor (empty private constructor is OK)</p>
  </li>
  <li>
    <p>cannot use $get, $set and $delete within the methods</p>
  </li>
</ul>
<p>For instance, the following code snippets will raise transpilation
  errors.</p>
<pre><code class="java">class Globals {
    public int a;
    // error: public constructors are not allowed
    public Globals() {
        this.a = 3;
    }
    public static void test() {
        // error: no instance is available
        $delete("key");
    }
}
</code></pre>
<pre><code class="java">// error: Globals classes cannot be used as types
Globals myVariable = null;
</code></pre>
<p>One must remember that <code>Globals</code> classes and <code>global</code> packages are
  erased at runtime so that their members will be directly accessible. For
  instance <code>mypackage.Globals.m()</code> in a JSweet program corresponds to the
  <code>mypackage.m()</code> function in the generated code and in the JavaScript VM
  at runtime. Also, <code>mypackage.globals.Globals.m()</code> corresponds to <em>m()</em>.</p>
<p>In order to erase packages in the generated code, programmers can also
  use the <code>@Root</code> annotation, which will be explained in Section
  <a href="#packaging" data-reference-type="ref" data-reference="packaging">5</a>.</p>
<h3><a href="#optional-parameters-and-overloading" id="optional-parameters-and-overloading">Optional parameters and
  overloading</a></h3>
<p>In JavaScript, parameters can be optional, in the sense that a parameter
  value does not need to be provided when calling a function. Except for
  varargs, which are fully supported in JSweet, the general concept of an
  optional parameter does not exist in Java. To simulate optional
  parameters, JSweet programmers can use method overloading, which is
  supported in Java. Here are some examples of supported overloads in
  JSweet:</p>
<pre><code class="java">String m(String s, double n) { return s + n; }
// simple overloading (JSweet transpiles to optional parameter)
String m(String s) { return m(s, 0); }
// complex overloading (JSweet generates more complex code to mimic the Java behavior)
String m(String s) { return s; }
</code></pre>
<h2><a href="#bridging-to-external-javascript-elements" id="bridging-to-external-javascript-elements">Bridging to
  external JavaScript elements</a></h2>
<p>It can be the case that programmers need to use existing libraries from
  JSweet. In most cases, one should look up in the available candies,
  a.k.a. bridges at <a href="http://www.jsweet.org/jsweet-candies/">http://www.jsweet.org/jsweet-candies/</a>. When the
  candy does not exist, or does not entirely cover what is needed, one can
  create new definitions in the program just by placing them in the
  <code>def.libname</code> package. Definitions only specify the types of external
  libraries, but no implementations. Definitions are similar to
  TypeScript’s <code>*.d.ts</code> definition files (actually JSweet generates
  intermediate TypeScript definition files for compilation purposes).
  Definitions can also be seen as similar to <code>*.h</code> C/C++ header files.</p>
<h3><a href="#examples" id="examples">Examples</a></h3>
<p>The following example shows the backbone store class made accessible to
  the JSweet programmer with a simple definition. This class is only for
  typing and will be generated as a TypeScript definition, and erased
  during the JavaScript generation.</p>
<pre><code class="java">package def.backbone;
class Store {
    public Store(String dbName) {}
}
</code></pre>
<p>Note that definition classes constructors must have an empty body. Also,
  definition classes methods must be <code>native</code>. For instance:</p>
<pre><code class="java">package def.mylib;
class MyExternalJavaScriptClass {
    public native myExternalJavaScriptMethod();
}
</code></pre>
<p>It is possible to define properties in definitions, however, these
  properties cannot be initialized.</p>
<h3><a href="#rules-for-writing-definitions-aka-bridges" id="rules-for-writing-definitions-aka-bridges">Rules for
  writing definitions (a.k.a. bridges)</a></h3>
<p>By convention, putting the classes in a <code>def.libname</code> package defines a
  set of definitions for the <code>libname</code> external JavaScript library called
  <code>libname</code>. Note that this mechanism is similar to the TypeScript <code>d.ts</code>
  definition files.</p>
<p>Candies (bridges to external JavaScript libraries) use definitions. For
  instance, the jQuery candy defines all the jQuery API in the
  <code>def.jquery</code> package.</p>
<p>Here is a list of rules and constraints that need to be followed when
  writing definitions.</p>
<ul>
  <li>
    <p>Interfaces are preferred over classes, because interfaces can be
      merged and classes can be instantiated. Classes should be used only
      if the API defines an explicit constructor (objects can be created
      with <code>new</code>). To define an interface in JSweet, just annotate a class
      with <code>@jsweet.lang.Interface</code>.</p>
  </li>
  <li>
    <p>Top-level functions and variables must be defined as <code>public static</code>
      members in a <code>Globals</code> class.</p>
  </li>
  <li>
    <p>All classes, interfaces and packages, should be documented with
      comments following the Javadoc standard.</p>
  </li>
  <li>
    <p>When several types are possible for a function parameter, method
      overloading should be preferred over using union types. When method
      overloading is not possible, it can be more convenient to simply use
      the <code>Object</code> type. It is less strongly typed but it is easier to
      use.</p>
  </li>
  <li>
    <p>One can use string types to provide function overloading depending
      on a string parameter value.</p>
  </li>
  <li>
    <p>In a method signature, optional parameters can be defined with the
      <code>@jsweet.lang.Optional</code> annotation.</p>
  </li>
  <li>
    <p>In an interface, optional fields can be defined with the
      <code>@jsweet.lang.Optional</code> annotation.</p>
  </li>
</ul>
<p>Definitions can be embedded directly in a JSweet project to access an
  external library in a typed way.</p>
<p>Definitions can also be packaged in a candy (a Maven artifact), so that
  they can be shared by other projects. See the <em>Packaging</em> section for
  full details on how to create a candy. Note that you do not need to
  write definitions when a library is written with JSweet because the Java
  API is directly accessible and the TypeScript definitions can be
  automatically generated by JSweet using the <code>declaration</code> option.</p>
<h3><a href="#untyped-accesses" id="untyped-accesses">Untyped accesses</a></h3>
<p>Sometimes, definitions are not available or are not correct, and just a
  small patch is required to access a functionality. Programmers have to
  keep in mind that JSweet is just a syntactic layer and that it is always
  possible to bypass typing in order to access a field or a function that
  is not explicitly specified in an API.</p>
<p>Although, having a well-typed API is the preferred and advised way, when
  such an API is not available, the use of <code>def.js.Object.$get</code> allows
  reflective access to methods and properties that can then be cast to the
  right type. For accessing functions in an untyped way, one can cast to
  <code>def.js.Function</code> and call the generic and untyped method <code>$apply</code> on
  it. For example, here is how to invoke the jQuery <code>$</code> method when the
  jQuery API is not available :</p>
<pre><code class="java">import def.dom.Globals.window;
[...]
Function $ = (Function)window.$get("$");
$.$apply("aCssSelector"):
</code></pre>
<p>The <code>$get</code> function is available on instances of <code>def.js.Object</code> (or
  subclasses). For a <code>def.js.Object</code>, you can cast it using the
  <code>jsweet.util.Lang.object</code> helper method. For example:</p>
<pre><code class="java">import static jsweet.dom.Lang.object;
[...]
object(anyObject).$get("$");
</code></pre>
<p>In last resort, the <code>jsweet.util.Lang.$insert</code> helper method allows the
  user to insert any TypeScript expression within the program. Invalid
  expressions will raise a TypeScript compilation error, but it is however
  not recommended to use this technique.</p>
<pre><code class="java">import static jsweet.dom.Lang.$get;
import static jsweet.dom.Lang.$apply;
[...]
// generate anyObject["prop"]("param");
$apply($get(anyObject, "prop"), "param");
</code></pre>
<p>Finally, note also the use of the <code>jsweet.util.Lang.any</code> helper method,
  which can be extremely useful to erase typing. Since the <code>any</code> method
  generates a cast to the <code>any</code> type in TypeScript, it is more radical
  than a cast to <code>Object</code> for instance. The following example shows how to
  use the <code>any</code> method to cast an <code>Int32Array</code> to a Java <code>int[]</code> (and then
  allow direct indexed accesses to it.</p>
<pre><code class="java">ArrayBuffer arb = new ArrayBuffer(2 * 2 * 4);
int[] array = any(new Int32Array(arb));
int whatever = array[0];
</code></pre>
<h3><a href="#mixins" id="mixins">Mixins</a></h3>
<p>In JavaScript, it is common practice to enhance an existing class with
  news elements (field and methods). It is an extension mechanism used
  when a framework defines plugins for instance. Typically, jQuery plugins
  add new elements to the <code>JQuery</code> class. For example the jQuery timer
  plugin adds a <code>timer</code> field to the <code>JQuery</code> class. As a consequence, the
  <code>JQuery</code> class does not have the same prototype if you are using jQuery
  alone, or jQuery enhanced with its timer plugin.</p>
<p>In Java, this extension mechanism is problematic because the Java
  language does not support mixins or any extension of that kind by
  default.</p>
<h4><a href="#untyped-accesses-to-mixins" id="untyped-accesses-to-mixins">Untyped accesses to mixins</a></h4>
<p>Programmers can access the added element with <code>$get</code> accessors and/or
  with brute-force casting.</p>
<p>Here is an example using <code>$get</code> for the timer plugin case:</p>
<pre><code class="java">((Timer)$("#myId").$get("timer")).pause();
</code></pre>
<p>Here is an other way to do it exampled through the use of the jQuery UI
  plugin (note that this solution forces the use of <code>def.jqueryui.JQuery</code>
  instead of <code>def.jquery.JQuery</code> in order to access the <code>menu()</code> function,
  added by the UI plugin):</p>
<pre><code class="java">import def.jqueryui.JQuery;
[...]
Object obj = $("#myMenu");
JQuery jq = (JQuery) obj;
jq.menu();
</code></pre>
<p>However, these solutions are not fully satisfying because clearly unsafe
  in terms of typing.</p>
<h4><a href="#typed-accesses-with-mixins" id="typed-accesses-with-mixins">Typed accesses with mixins</a></h4>
<p>When cross-candy dynamic extension is needed, JSweet defines the notion
  of a mixin. A mixin is a class that defines members that will end up
  being directly accessible within a target class (mixin-ed class). Mixins
  are defined with a <code>@Mixin</code> annotation. Here is the excerpt of the
  <code>def.jqueryui.JQuery</code> mixin:</p>
<pre><code class="java">package def.jqueryui;
import def.dom.MouseEvent;
import def.js.Function;
import def.js.Date;
import def.js.Array;
import def.js.RegExp;
import def.dom.Element;
import def.jquery.JQueryEventObject;
@jsweet.lang.Interface
@jsweet.lang.Mixin(target=def.jquery.JQuery.class)
public abstract class JQuery extends def.jquery.JQuery {
    native public JQuery accordion();
    native public void accordion(jsweet.util.StringTypes.destroy methodName);
    native public void accordion(jsweet.util.StringTypes.disable methodName);
    native public void accordion(jsweet.util.StringTypes.enable methodName);
    native public void accordion(jsweet.util.StringTypes.refresh methodName);
    ...
    native public def.jqueryui.JQuery menu();
    ...
</code></pre>
<p>One can notice the <code>@jsweet.lang.Mixin(target=def.jquery.JQuery.class)</code>
  that states that this mixin will be merged to the <code>def.jquery.JQuery</code> so
  that users will be able to use all the UI plugin members directly and in
  a well-typed way.</p>
<h4><a href="#how-to-use" id="how-to-use">How to use</a></h4>
<p>TBD.</p>
<h3><a href="#generating-jsweet-candies-from-existing-typescript-definitions"
       id="generating-jsweet-candies-from-existing-typescript-definitions">Generating JSweet candies from existing
  TypeScript definitions</a></h3>
<p>TBD.</p>
<h2><a href="#auxiliary-types" id="auxiliary-types">Auxiliary types</a></h2>
<p>JSweet uses most Java typing features (including functional types) but
  also extends the Java type system with so-called <em>auxiliary types</em>. The
  idea behind auxiliary types is to create classes or interfaces that can
  hold the typing information through the use of type parameters (a.k.a
  <em>generics</em>), so that the JSweet transpiler can cover more typing
  scenarios. These types have been mapped from TypeScript type system,
  which is much richer than the Java one (mostly because JavaScript is a
  dynamic language and requires more typing scenarios than Java).</p>
<h3><a href="#functional-types" id="functional-types">Functional types</a></h3>
<p>For functional types, JSweet reuses the <code>java.Runnable</code> and
  <code>java.util.function</code> functional interfaces of Java 8. These interfaces
  are generic but only support up to 2-parameter functions. Thus, JSweet
  adds some support for more parameters in <code>jsweet.util.function</code>, since
  it is a common case in JavaScript APIs.</p>
<p>Here is an example using the <code>Function</code> generic functional type:</p>
<pre><code class="java">import java.util.function.Function;

public class C {

    String test(Function<String, String> f) {
        f.apply("a");
    }

    public static void main(String[] args) {
        String s = new C().test(p -> p);
        assert s == "a";
    }
}
</code></pre>
<p>We encourage programmers to use the generic functional interfaces
  defined in the <code>jsweet.util.function</code> and <code>java.util.function</code> (besides
  <code>java.lang.Runnable</code>). When requiring functions with more parameters,
  programmers can define their own generic functional types in
  <code>jsweet.util.function</code> by following the same template as the existing
  ones.</p>
<p>In some cases, programmers will prefer defining their own specific
  functional interfaces. This is supported by JSweet. For example:</p>
<pre><code class="java">@FunctionalInterface
interface MyFunction {
    void run(int i, String s);
}

public class C {
    void m(MyFunction f) {
        f.run(1, "test");
    }
    public static void main(String[] args) {
        new C().m((i, s) -> {
            // do something with i and s
        });
    }
}
</code></pre>
<p>Important warning: it is to be noted here that, on contrary to Java, the
  use of the <code>@FunctionInterface</code> annotation is mandatory.</p>
<p>Note also the possible use of the <code>apply</code> function, which is by
  convention always a functional definition on the target object (unless
  if <code>apply</code> is annotated with the <code>@Name</code> annotation). Defining/invoking
  <code>apply</code> can done on any class/object (because in JavaScript any object
  can become a functional object).</p>
<h3><a href="#object-types" id="object-types">Object types</a></h3>
<p>Object types are similar to interfaces: they define a set of fields and
  methods that are applicable to an object (but remember that it is a
  compile-time contract). In TypeScript, object types are inlined and
  anonymous. For instance, in TypeScript, the following method <code>m</code> takes a
  parameter, which is an object containing an <code>index</code> field:</p>
<pre><code class="java">// TypeScript:
public class C {
    public m(param : { index : number }) { ... }
}
</code></pre>
<p>Object types are a convenient way to write shorter code. One can pass an
  object that is correctly typed by constructing an object on the fly:</p>
<pre><code class="java">// TypeScript:
var c : C = ...;
c.m({ index : 2 });
</code></pre>
<p>Obviously, object types are a way to make the typing of JavaScript
  programs very easy to programmers, which is one of the main goals of
  TypeScript. It makes the typing concise, intuitive and straightforward
  to JavaScript programmers. In Java/JSweet, no similar inlined types
  exist and Java programmers are used to defining classes or interfaces
  for such cases. So, in JSweet, programmers have to define auxiliary
  classes annotated with <code>@ObjectType</code> for object types. This may seem
  more complicated, but it has the advantage to force the programmers to
  name all the types, which, in the end, can lead to more readable and
  maintenable code depending on the context. Note that similarily to
  interfaces, object types are erased at runtime. Also <code>@ObjectType</code>
  annotated classes can be inner classes so that they are used locally.</p>
<p>Here is the JSweet version of the previous TypeScript program.</p>
<pre><code class="java">public class C {
    @ObjectType
    public static class Indexed {
        int index;
    }
    public void m(Indexed param) { ... }
}
</code></pre>
<p>Using an object type is similar to using an interface:</p>
<pre><code class="java">C c = ...;
c.m(new Indexed() {{ index = 2; }});
</code></pre>
<p>When object types are shared objects and represent a typing entity that
  can be used in several contexts, it is recommended to use the
  <code>@Interface</code> annotation instead of <code>@ObjectType</code>. Here is the
  interface-based version.</p>
<pre><code class="java">@Interface
public class Indexed {
    int index;
}

public class C {
    public m(Indexed param) { ... }
}

C c = ...;
c.m(new Indexed {{ index = 2; }});
</code></pre>
<h3><a href="#string-types" id="string-types">String types</a></h3>
<p>In TypeScript, string types are a way to simulate function overloading
  depending on the value of a string parameter. For instance, here is a
  simplified excerpt of the DOM TypeScript definition file:</p>
<pre><code class="java">// TypeScript:
interface Document {
    [...]
    getElementsByTagName(tagname: "a"): NodeListOf<HTMLAnchorElement>;
    getElementsByTagName(tagname: "b"): NodeListOf<HTMLPhraseElement>;
    getElementsByTagName(tagname: "body"): NodeListOf<HTMLBodyElement>;
    getElementsByTagName(tagname: "button"): NodeListOf<HTMLButtonElement>;
    [...]
}
</code></pre>
<p>In this code, the <code>getElementsByTagName</code> functions are all overloads
  that depend on the strings passed to the <code>tagname</code> parameter. Not only
  string types allow function overloading (which is in general not allowed
  in TypeScript/JavaScript), but they also constrain the string values
  (similarly to an enumeration), so that the compiler can automatically
  detect typos in string values and raise errors.</p>
<p>This feature being useful for code quality, JSweet provides a mechanism
  to simulate string types with the same level of type safety. A string
  type is a public static field annotated with <code>@StringType</code>. It must be
  typed with an interface of the same name declared in the same container
  type.</p>
<p>For JSweet translated libraries (candies), all string types are declared
  in a the <code>jsweet.util.StringTypes</code> class, so that it is easy for the
  programmers to find them. For instance, if a <code>"body"</code> string type needs
  to be defined, a Java interface called <code>body</code> and a static final field
  called <code>body</code> are defined in a <code>jsweet.util.StringTypes</code>.</p>
<p>Note that each candy may have its own string types defined in the
  <code>jsweet.util.StringTypes</code> class. The JSweet transpiler merges all these
  classes at the bytecode level so that all the string types of all
  candies are available in the same <code>jsweet.util.StringTypes</code> utility
  class. As a result, the JSweet DOM API will look like:</p>
<pre><code class="java">@Interface
public class Document {
    [...]
    public native NodeListOf<HTMLAnchorElement> getElementsByTagName(a tagname);
    public native NodeListOf<HTMLPhraseElement> getElementsByTagName(b tagname);
    public native NodeListOf<HTMLBodyElement> getElementsByTagName(body tagname);
    public native NodeListOf<HTMLButtonElement> getElementsByTagName(button tagname);
    [...]
}
</code></pre>
<p>In this API, <code>a</code>, <code>b</code>, <code>body</code> and <code>button</code> are interfaces defined in the
  <code>jsweet.util.StringTypes</code> class. When using one the method of
  <code>Document</code>, the programmer just need to use the corresponding type
  instance (of the same name). For instance:</p>
<pre><code class="java">Document doc = ...;
NodeListOf<HTMLAnchorElement> elts = doc.getElementsByTagName(StringTypes.a);
</code></pre>
<p>Note: if the string value is not a valid Java identifier (for instance
  <code>"2d"</code> or <code>"string-with-dashes"</code>), it is then translated to a valid one
  and annotated with <code>@Name("originalName")</code>, so that the JSweet
  transpiler knows what actual string value must be used in the generated
  code. For instance, by default, <code>"2d"</code> and <code>"string-with-dashes"</code> will
  correspond to the interfaces <code>StringTypes._2d</code> and
  <code>StringTypes.string_with_dashes</code> with <code>@Name</code> annotations.</p>
<p>Programmers can define string types for their own needs, as shown below:</p>
<pre><code class="java">import jsweet.lang.Erased;
import jsweet.lang.StringType;

public class CustomStringTypes {
    @Erased
    public interface abc {}

    @StringType
    public static final abc abc = null;

    // This method takes a string type parameter
    void m2(abc arg) {
    }

    public static void main(String[] args) {
        new CustomStringTypes().m2(abc);
    }
}
</code></pre>
<p>Note the use of the <code>@Erased</code> annotation, which allows the declaration
  of the <code>abc</code> inner interface. This interface is used to type the string
  type field <code>abc</code>. In general, we advise the programmer to group all the
  string types of a program in the same utility class so that it is easy
  to find them.</p>
<h3><a href="#tuple-types" id="tuple-types">Tuple types</a></h3>
<p>Tuple types represent JavaScript arrays with individually tracked
  element types. For tuple types, JSweet defines parameterized auxiliary
  classes <code>TupleN<T0, ... TN-1></code>, which define <code>$0</code>, <code>$1</code>, ... <code>$N-1</code>
  public fields to simulate typed array accessed (field <code>$i</code> is typed with
  <code>Ti</code>).</p>
<p>For instance, given the following tuple of size 2:</p>
<pre><code class="java">Tuple2<String, Integer> tuple = new Tuple2<String, Integer>("test", 10);
</code></pre>
<p>We can expect the following (well-typed) behavior:</p>
<pre><code class="java">assert tuple.$0 == "test";
assert tuple.$1 == 10;
tuple.$0 = "ok";
tuple.$1--;
assert tuple.$0 == "ok";
assert tuple.$1 == 9;
</code></pre>
<p>Tuple types are all defined (and must be defined) in the
  <code>jsweet.util.tuple</code> package. By default classes <code>Tuple[2..6]</code> are
  defined. Other tuples ( > 6) are automatically generated when
  encountered in the candy APIs. Of course, when requiring larger tuples
  that cannot be found in the <code>jsweet.util.tuple</code> package, programmers can
  add their own tuples in that package depending on their needs, just by
  following the same template as existing tuples.</p>
<h3><a href="#union-types" id="union-types">Union types</a></h3>
<p>Union types represent values that may have one of several distinct
  representations. When such a case happens within a method signature (for
  instance a method allowing several types for a given parameter), JSweet
  takes advantage of the <em>method overloading</em> mechanism available in Java.
  For instance, the following <code>m</code> method accept a parameter <code>p</code>, which can
  be either a <code>String</code> or a <code>Integer</code>.</p>
<pre><code class="java">public void m(String p) {...}
public void m(Integer p) {...}
</code></pre>
<p>In the previous case, the use of explicit union types is not required.
  For more general cases, JSweet defines an auxiliary interface
  <code>Union<T1, T2></code> (and <code>UnionN<T1, ... TN></code>) in the <code>jsweet.util.union</code>
  package. By using this auxiliary type and a <code>union</code> utility method,
  programmers can cast back and forth between union types and union-ed
  type, so that JSweet can ensure similar properties as TypeScript union
  types.</p>
<p>The following code shows a typical use of union types in JSweet. It
  simply declares a variable as a union between a string and a number,
  which means that the variable can actually be of one of that types (but
  of no other types). The switch from a union type to a regular type is
  done through the <code>jsweet.util.Lang.union</code> helper method. This helper
  method is completely untyped, allowing from a Java perspective any union
  to be transformed to another type. It is actually the JSweet transpiler
  that checks that the union type is consistently used.</p>
<pre><code class="java">import static jsweet.util.Lang.union;
import jsweet.util.union.Union;
[...]
Union<String, Number> u = ...;
// u can be used as a String
String s = union(u);
// or a number
Number n = union(u);
// but nothing else
Date d = union(u); // JSweet error
</code></pre>
<p>The <code>union</code> helper can also be used the other way, to switch from a
  regular type back to a union type, when expected.</p>
<pre><code class="java">import static jsweet.util.Lang.union;
import jsweet.util.union.Union3;
[...]
public void m(Union3<String, Number, Date>> u) { ... }
[...]
// u can be a String, a Number or a Date
m(union("a string"));
// but nothing else
m(union(new RegExp(".*"))); // compile error
</code></pre>
<p>Note: the use of Java function overloading is preferred over union types
  when typing function parameters. For example:</p>
<pre><code class="java">// with union types (discouraged)
native public void m(Union3<String, Number, Date>> u);
// with overloading (preferred way)
native public void m(String s);
native public void m(Number n);
native public void m(Date d);
</code></pre>
<h3><a href="#intersection-types" id="intersection-types">Intersection types</a></h3>
<p>TypeScript defines the notion of type intersection. When types are
  intersected, it means that the resulting type is a larger type, which is
  the sum of all the intersected types. For instance, in TypeScript,
  <code>A & B</code> corresponds to a type that defines both <code>A</code> and <code>B</code> members.</p>
<p>Intersection types in Java cannot be implemented easily for many
  reasons. So, the practical choice being made here is to use union types
  in place of intersection types. In JSweet, <code>A & B</code> is thus defined as
  <code>Union<A, B></code>, which means that the programmer can access both <code>A</code> and
  <code>B</code> members by using the <code>jsweet.util.Lang.union</code> helper method. It is
  of course less convenient than the TypeScript version, but it is still
  type safe.</p>
<h2><a href="#semantics" id="semantics">Semantics</a></h2>
<p>Semantics designate how a given program behaves when executed. Although
  JSweet relies on the Java syntax, programs are transpiled to JavaScript
  and do not run in a JRE. As a consequence, the JavaScript semantics will
  impact the final semantics of a JSweet program compared to a Java
  program. In this section, we discuss the semantics by focusing on
  differences or commonalities between Java/JavaSript and JSweet.</p>
<h3><a href="#main-methods" id="main-methods">Main methods</a></h3>
<p>Main methods are the program execution entry points and will be invoked
  globally when a class containing a <code>main</code> method is evaluated. For
  instance:</p>
<pre><code class="java">public class C {
    private int n;
    public static C instance;
    public static void main(String[] args) {
        instance = new C();
        instance.n = 4;
    }
    public int getN() {
        return n;
    }
}
// when the source file containing C has been evaluated:
assert C.instance != null;
assert C.instance.getN() == 4;
</code></pre>
<p>The way main methods are globally invoked depends on how the program is
  packaged. See the appendixes for more details.</p>
<h3><a href="#initializers" id="initializers">Initializers</a></h3>
<p>Initializers behave like in Java.</p>
<p>For example:</p>
<pre><code class="java">public class C1 {
    int n;
    {
        n = 4;
    }
}
assert new C1().n == 4;
</code></pre>
<p>And similarly with static initializers:</p>
<pre><code class="java">public class C2 {
    static int n;
    static {
        n = 4;
    }
}
assert C2.n == 4;
</code></pre>
<p>While regular initializers are evaluated when the class is instantiated,
  static initializers are lazily evaluated in order to avoid
  forward-dependency issues, and mimic the Java behavior for initializers.
  With JSweet, it is possible for a programmer to define a static field or
  a static intializer that relies on a static field that has not yet been
  initialized.</p>
<p>More details on this behavior can be found in the appendixes.</p>
<h3><a href="#arrays-initialization-and-allocation" id="arrays-initialization-and-allocation">Arrays initialization and
  allocation</a></h3>
<p>Arrays can be used like in Java.</p>
<pre><code class="java">String[] strings = { "a", "b", "c" };
assert strings[1] == "b";
</code></pre>
<p>When specifying dimensions, arrays are pre-allocated (like in Java), so
  that they are initialized with the right length, and with the right
  sub-arrays in case of multiple-dimensions arrays.</p>
<pre><code class="java">String[][] strings = new String[2][2];
assert strings.length == 2;
assert strings[0].length == 2;
strings[0][0] = "a";
assert strings[0][0] == "a";
</code></pre>
<p>The JavaScript API can be used on an array by casting to a
  <code>def.js.Array</code> with <code>jsweet.util.Lang.array</code>.</p>
<pre><code class="java">import static jsweet.util.Lang.array;
[...]
String[] strings = { "a", "b", "c" };
assert strings.length == 3;
array(strings).push("d");
assert strings.length == 4;
assert strings[3] == "d";
</code></pre>
<p>In some cases it is preferable to use the <code>def.js.Array</code> class directly.</p>
<pre><code class="java">Array<String> strings = new Array<String>("a", "b", "c");
// same as: Array<String> strings = array(new String[] { "a", "b", "c" });
// same as: Array<String> strings = new Array<String>(); strings.push("a", "b", "c");
assert strings.length == 3;
strings.push("d");
assert strings.length == 4;
assert strings.$get(3) == "d";
</code></pre>
<h3><a href="#asynchronous-programming" id="asynchronous-programming">Asynchronous programming</a></h3>
<p>JSweet supports advanced asynchronous programming beyond the basic
  callback concepts with the help of the ES2015+ Promise API.</p>
<h4><a href="#promises" id="promises">Promises</a></h4>
<p>It is very simple to define an asynchronous method by declaring a
  <code>Promise</code> return type. The following method’s <code>Promise</code> will be
  <em>fulfilled</em> when millis milliseconds elapsed.</p>
<pre><code class="java">Promise<Void> delay(int millis) {
  return new Promise<Void>((Consumer<Void> resolve, Consumer<Object> reject) -> {
    setTimeout(resolve, millis);
  });
}
</code></pre>
<p>You can then chain synchronous and asynchronous actions to be executed
  once the promise is fulfilled.</p>
<pre><code class="java">delay(1000)
  // chain with a synchronous action with "then". Here we just return a constant.
  .then(() -> {
    System.out.println("wait complete");
    return 42;
  })
  // chain with an asynchronous action with "thenAsync". Here it is implied that anotherAsyncAction(String) returns a Promise<...>
  .thenAsync((Integer result) -> {
    System.out.println("previous task result: " + result); // will print "previous task result: 42"

    return anotherAsyncAction("param");
  })
  // this chained action will be executed once anotherAsyncAction finishes its execution.
  .then((String result) -> {

    System.out.println("anotherAsyncAction returned " + result);
  })
  // catch errors during process using this method
  .Catch(error -> {
    System.out.println("error is " + error);
  });
</code></pre>
<p>This allows a totally type-safe and fluent asynchronous programming
  model.</p>
<h4><a href="#asyncawait" id="asyncawait">async/await</a></h4>
<p><code>Promise</code>s are really interesting to avoid callback but writing it still
  requires a lot of boilerplate code. It is better than pure callbacks but
  less readable and straightforward than linear programming. That’s where
  <code>async/await</code> comes to help.</p>
<p>With the <code>await</code> keyword, you can tell the runtime to wait for a
  <code>Promise</code> to be fulfilled without having to write a then method. The
  code after the <code>await</code> "is" the <code>then</code> part. The result is that you can
  write your asynchronous code with linear programming.</p>
<pre><code class="java">import static jsweet.util.Lang.await;

// wait for the Promise returned by the delay method to be fulfilled
await(delay(1000));

System.out.println("wait complete");
</code></pre>
<p>It goes the same for error handling. You can just use the plain old
  <code>try / catch</code> idiom to handle your exceptions.</p>
<pre><code class="java">import static jsweet.util.Lang.await;
import def.js.Error;

try {
  Integer promiseResult = await(getANumber());
  assert promiseResult == 42;
} catch(Error e) {
  System.err.println("something unexpected happened: " + e);
}
</code></pre>
<p>You have to declare as <code>async</code> every asynchronous method / lambda (i.e.
  every method which would await something).</p>
<pre><code class="java">import static jsweet.util.Lang.await;
import static jsweet.util.Lang.async;
import static jsweet.util.Lang.function;

import jsweet.lang.Async;
import def.js.Function;

@Async
Promise<Integer> findAnswer() {
  await(delay(1000)); // won't compile if the enclosing method isn't @Async
  return asyncReturn(42); // converts to Promise
}

@Async
void askAnswerThenVerifyAndPrintIt() {

  try {

    Integer answer = await(findAnswer());

    // lambda expressions can be async
    Function verifyAnswerAsync = async(function(() -> {
      return await(answerService.verifyAnswer(answer));
    }))

    Boolean verified = await(verifyAnswerAsync.$apply());

    if (!verified) {
      throw new Error("cannot verify this answer");
    }

    console.log("answer found: " + answer);

  } catch (Error e) {
    console.error(e, "asynchronous process failed");
  }
}
</code></pre>
<p>Sweet, isn’t it? ;)</p>
<h3><a href="#name-clashes" id="name-clashes">Name clashes</a></h3>
<p>On contrary to TypeScript/JavaScript, Java makes a fundamental
  difference between methods, fields, and packages. Java also support
  method overloading (methods having different signatures with the same
  name). In JavaScript, object variables and functions are stored within
  the same object map, which basically means that you cannot have the same
  key for several object members (this also explains that method
  overloading in the Java sense is not possible in JavaScript). Because of
  this, some Java code may contain name clashes when generated as is in
  TypeScript. JSweet will avoid name clashes automatically when possible,
  and will report sound errors in the other cases.</p>
<h4><a href="#methods-and-fields-names-clashes" id="methods-and-fields-names-clashes">Methods and fields names
  clashes</a></h4>
<p>JSweet performs a transformation to automatically allow methods and
  private fields to have the same name. On the other hand, methods and
  public fields of the same name are not allowed within the same class or
  within classes having a subclassing link.</p>
<p>To avoid programming mistakes due to this JavaScript behavior, JSweet
  adds a semantics check to detect duplicate names in classes (this also
  takes into account members defined in parent classes). As an example:</p>
<pre><code class="java">public class NameClashes {

    // error: field name clashes with existing method name
    public String a;

    // error: method name clashes with existing field name
    public void a() {
        return a;
    }

}
</code></pre>
<h4><a href="#method-overloading" id="method-overloading">Method overloading</a></h4>
<p>On contrary to TypeScript and JavaScript (but similarly to Java), it is
  possible in JSweet to have several methods with the same name but with
  different parameters (so-called overloads). We make a distinction
  between simple overloads and complex overloads. Simple overloading is
  the use of method overloading for defining optional parameters. JSweet
  allows this idiom under the condition that it corresponds to the
  following template:</p>
<pre><code class="java">String m(String s, double n) { return s + n; }
// valid overloading (JSweet transpiles to optional parameter)
String m(String s) { return m(s, 0); }
</code></pre>
<p>In that case, JSweet will generate JavaScript code with only one method
  having default values for the optional parameters, so that the behavior
  of the generated program corresponds to the original one. In this case:</p>
<pre><code class="java">function m(s, n = 0) { return s + n; }
</code></pre>
<p>If the programmer tries to use overloading differently, for example by
  defining two different implementations for the same method name, JSweet
  will fallback on a complex overload, which consists of generating a root
  implementation (the method that hold the more parameters) and one
  subsidiary implementation per overloading method (named with a suffix
  representing the method signature). The root implementation is generic
  and dispatches to other implementations by testing the values and types
  of the given parameters. For example:</p>
<pre><code class="java">String m(String s, double n) { return s + n; }
String m(String s) { return s; }
</code></pre>
<p>Generates the following (slightly simplified) JavaScript code:</p>
<pre><code class="java">function m(s, n) {
    if(typeof s === 'string' && typeof n === 'number') {
        return s + n;
    } else if(typeof s === 'string' && n === undefined) {
        return this.m$java_lang_String(s);
    } else {
        throw new Error("invalid overload");
    }
}

function m$java_lang_String(s) { return s; }
</code></pre>
<h4><a href="#local-variable-names" id="local-variable-names">Local variable names</a></h4>
<p>In TypeScript/JavaScript, local variables can clash with the use of a
  global method. For instance, using the <code>alert</code> global method from the
  DOM (<code>jsweet.dom.Globals.alert</code>) requires that no local variable hides
  it:</p>
<pre><code class="java">import static jsweet.dom.Globals.alert;

[...]

public void m1(boolean alert) {
    // JSweet compile error: name clash between parameter and method call
    alert("test");
}

public void m2() {
    // JSweet compile error: name clash between local variable and method call
    String alert = "test";
    alert(alert);
}
</code></pre>
<p>Note that this problem also happens when using fully qualified names
  when calling the global methods (that is because the qualification gets
  erased in TypeScript/JavaScript). In any case, JSweet will report sound
  errors when such problems happen so that programmers can adjust local
  variable names to avoid clashes with globals.</p>
<h3><a href="#testing-the-type-of-an-object" id="testing-the-type-of-an-object">Testing the type of an object</a></h3>
<p>To test the type of a given object at runtime, one can use the
  <code>instanceof</code> Java operator, but also the <code>Object.getClass()</code> function.</p>
<h4><a href="#instanceof" id="instanceof"><code>instanceof</code></a></h4>
<p>The <code>instanceof</code> is the advised and preferred way to test types at
  runtime. JSweet will transpile to a regular <code>instanceof</code> or to a
  <code>typeof</code> operator depending on the tested type (it will fallback on
  <code>typeof</code> for <code>number</code>, <code>string</code>, and <code>boolean</code> core types).</p>
<p>Although not necessary, it is also possible to directly use the <code>typeof</code>
  operator from JSweet with the <code>jsweet.util.Lang.typeof</code> utility method.
  Here are some examples of valid type tests:</p>
<pre><code class="java">import static jsweet.util.Lang.typeof;
import static jsweet.util.Lang.equalsStrict;
[...]
Number n1 = 2;
Object n2 = 2;
int n3 = 2;
Object s = "test";
MyClass c = new MyClass();

assert n1 instanceof Number; // transpiles to a typeof
assert n2 instanceof Number; // transpiles to a typeof
assert n2 instanceof Integer; // transpiles to a typeof
assert !(n2 instanceof String); // transpiles to a typeof
assert s instanceof String; // transpiles to a typeof
assert !(s instanceof Integer); // transpiles to a typeof
assert c instanceof MyClass;
assert typeof(n3) == "number";
</code></pre>
<p>From JSweet version 1.1.0, the <code>instanceof</code> operator is also allowed on
  interfaces, because JSweet keeps track of all the implemented interfaces
  for all objects. This interface tracking is ensured through an
  additional hidden property in the objects called <code>__interfaces</code> and
  containing the names of all the interfaces implemented by the objects
  (either directly or through its class inheritance tree determined at
  compile time). So, in case the type argument of the <code>instanceof</code>
  operator is an interface, JSweet simply checks out if the object’s
  <code>__interfaces</code> field exists and contains the given interface. For
  example, this code is fully valid in JSweet when <code>Point</code> is an
  interface:</p>
<pre><code class="java">Point p1 = new Point() {{ x=1; y=1; }};
[...]
assert p1 instanceof Point
</code></pre>
<h4><a href="#objectgetclass-and-xclass" id="objectgetclass-and-xclass"><code>Object.getClass()</code> and
  <code>X.class</code></a></h4>
<p>In JSweet, using the <code>Object.getClass()</code> on any instance is possible. It
  will actually return the constructor function of the class. Using
  <code>X.class</code> will also return the constructor if <code>X</code> is a class. So the
  following assertion will hold in JSweet:</p>
<pre><code class="java">String s = "abc";
assert String.class == s.getClass()
</code></pre>
<p>On a class, you can call the <code>getSimpleName()</code> or <code>getName()</code> functions.</p>
<pre><code class="java">String s = "abc";
assert "String" == s.getClass().getSimpleName()
assert String.class.getSimpleName() == s.getClass().getSimpleName()
</code></pre>
<p>Note that <code>getSimpleName()</code> or <code>getName()</code> functions will also work on
  an interface. However, you have to be aware that <code>X.class</code> will be
  encoded in a string (holding the interface’s name) if <code>X</code> is is an
  interface.</p>
<h4><a href="#limitations-and-constraints" id="limitations-and-constraints">Limitations and constraints</a></h4>
<p>Since all numbers are mapped to JavaScript numbers, JSweet make no
  distinction between integers and floats for example. So,
  <code>n instanceof Integer</code> and <code>n instanceof Float</code> will always give the
  same result whatever the actual type of <code>n</code> is. The same limitation
  exists for strings and chars, which are not distinguishable at runtime,
  but also for functions that have the same number of parameters. For
  example, an instance of <code>IntFunction<R></code> will not be distinguishable at
  runtime from a <code>Function<String,R></code>.</p>
<p>These limitations have a direct impact on function overloading, since
  overloading uses the <code>instanceof</code> operator to decide which overload to
  be called.</p>
<p>Like it is usually the case when working in JavaScript, serialized
  objects must be properly "revived" with their actual classes so that the
  <code>instanceof</code> operator can work again. For example a point object created
  through <code>Point p = (Point)JSON.parse("{x:1,y:1}")</code> will not work with
  regard to the <code>instanceof</code> operator. In case you meet such a use case,
  you can contact us to get some useful JSweet code to properly revive
  object types.</p>
<h3><a href="#variable-scoping-in-lambda-expressions" id="variable-scoping-in-lambda-expressions">Variable scoping in
  lambda expressions</a></h3>
<p>JavaScript variable scoping is known to pose some problems to the
  programmers, because it is possible to change the reference to a
  variable from outside of a lambda that would use this variable. As a
  consequence, a JavaScript programmer cannot rely on a variable declared
  outside of a lambda scope, because when the lambda is executed, the
  variable may have been modified somewhere else in the program. For
  instance, the following program shows a typical case:</p>
<pre><code class="java">NodeList nodes = document.querySelectorAll(".control");
for (int i = 0; i < nodes.length; i++) {
    HTMLElement element = (HTMLElement) nodes.$get(i); // final
    element.addEventListener("keyup", (evt) -> {
        // this element variable will not change here
        element.classList.add("hit");
    });
}
</code></pre>
<p>In JavaScript (note that EcmaScript 6 fixes this issue), such a program
  would fail its purpose because the <code>element</code> variable used in the event
  listener is modified by the for loop and does not hold the expected
  value. In JSweet, such problems are dealt with similarly to final Java
  variables. In our example, the <code>element</code> variable is re-scoped in the
  lambda expression so that the enclosing loop does not change its value
  and so that the program behaves like in Java (as expected by most
  programmers).</p>
<h3><a href="#scope-of-this" id="scope-of-this">Scope of <em>this</em></a></h3>
<p>On contrary to JavaScript and similarly to Java, using a method as a
  lambda will prevent loosing the reference to <code>this</code>. For instance, in
  the <code>action</code> method of the following program, <code>this</code> holds the right
  value, even when <code>action</code> was called as a lambda in the <code>main</code> method.
  Although this seem logical to Java programmers, it is not a given that
  the JavaScript semantics ensures this behavior.</p>
<pre><code class="java">package example;
import static jsweet.dom.Globals.console;

public class Example {
    private int i = 8;
    public Runnable getAction() {
        return this::action;
    }
    public void action() {
        console.log(this.i); // this.i is 8
    }
    public static void main(String[] args) {
        Example instance = new Example();
        instance.getAction().run();
    }
}
</code></pre>
<p>It is important to stress that the <code>this</code> correct value is ensured
  thanks to a similar mechanism as the ES5 <code>bind</code> function. A consequence
  is that function references are wrapped in functions, which means that
  function pointers (such as <code>this::action</code>) create wrapping functions on
  the fly. It has side effects when manipulating function pointers, which
  are well described in this issue
  <a href="https://github.com/cincheo/jsweet/issues/65">https://github.com/cincheo/jsweet/issues/65</a>.</p>
<h2><a href="#packaging" id="packaging">Packaging</a></h2>
<p>Packaging is one of the complex point of JavaScript, especially when
  coming from Java. Complexity with JavaScript packaging boils down to the
  fact that JavaScript did not define any packaging natively. As a
  consequence, many <em>de facto</em> solutions and guidelines came up along the
  years, making the understanding of packaging uneasy for regular Java
  programmers. JSweet provides useful options and generates code in order
  to simplify the life of Java programmers by making the packaging issues
  much more transparent and as "easy" as in Java for most cases. In this
  section, we will describe and explain typical packaging scenarios.</p>
<h3><a href="#use-your-files-without-any-packaging" id="use-your-files-without-any-packaging">Use your files without any
  packaging</a></h3>
<p>The most common and simple case for running a program is just to include
  each generated file in an HTML page. This is the default mode when not
  precising any packaging options. For example, when your program defines
  two classes <code>x.y.z.A</code> and <code>x.y.z.B</code> in two separated files, you can use
  them as following:</p>
<pre><code><script type="text/javascript" src="target/js/x/y/z/A.js"></script>
<script type="text/javascript" src="target/js/x/y/z/B.js"></script>
[...]
<!-- access a method later in the file -->
<script type="text/javascript">x.y.z.B.myMethod()</script>
</code></pre>
<p>When doing so, programmers need to be extremely cautious to avoid
  forward static dependencies between the files. In other words, the <code>A</code>
  class cannot use anything from <code>B</code> in static fields, static
  initializers, or static imports, otherwise leading to runtime errors
  when trying to load the page. Additionally, the <code>A</code> class cannot extend
  the <code>B</code> class. These constraints come from JavaScript/TypeScript and
  have nothing to do with JSweet.</p>
<p>As you can imagine, running simple programs with this manual technique
  is fine, but can become really uncomfortable for developing complex
  applications. Complex applications most of the time bundle and/or
  package the program with appropriate tools in order to avoid having to
  manually handle dependencies between JavaScript files.</p>
<h3><a href="#creating-a-bundle-for-a-browser" id="creating-a-bundle-for-a-browser">Creating a bundle for a browser</a>
</h3>
<p>To avoid having to take care of the dependencies manually, programmers
  use bundling tools to bundle up their classes into a single file. Such a
  bundle is included in any web page using something like this:</p>
<pre><code><script type="text/javascript" src="target/js/bundle.js"></script>
[...]
<!-- access a method later in the file -->
<script type="text/javascript">x.y.z.B.myMethod()</script>
</code></pre>
<p>JSweet comes with such a bundling facility. To create a bundle file,
  just set to <code>true</code> the <code>bundle</code> option of JSweet. Note that you can also
  set to <code>true</code> the <code>declaration</code> option that will ask JSweet to generate
  the TypeScript definition file (<code>bundle.d.ts</code>). This file allows you to
  use/compile your JSweet program from TypeScript in a well-typed way.</p>
<p>The "magic" with JSweet bundling option is that it analyzes the
  dependencies in the source code and takes care of solving forward
  references when building the bundle. In particular, JSweet implements a
  lazy initialization mechanism for static fields and initializers in
  order to break down static forward references across the classes. There
  are no specific additional declarations to be made by the programmers to
  make it work (on contrary to TypeScript).</p>
<p>Note that there are still some minor limitations to it (when using inner
  and anonymous classes for instance), but these limitations will be
  rarely encountered and will be removed in future releases.</p>
<p>Note also that JSweet will raise an error if you specify the <code>module</code>
  option along with the <code>bundle</code> option.</p>
<h3><a href="#packaging-with-modules" id="packaging-with-modules">Packaging with modules</a></h3>
<p>First, let us start by explaining modules and focus on the difference
  between Java <em>packages</em> (or TypeScript <em>namespaces</em>) and <em>modules</em>. If
  you feel comfortable with the difference, just skip this section.</p>
<p>Packages and modules are two similar concepts but for different
  contexts. Java packages must be understood as compile-time <em>namespaces</em>.
  They allow a compile-time structuration of the programs through name
  paths, with implicit or explicit visibility rules. Packages have usually
  not much impact on how the program is actually bundled and deployed.</p>
<p>Modules must be understood as deployment / runtime "bundles", which can
  be <code>required</code> by other modules. The closest concept to a module in the
  Java world would probably be an OSGi bundle. A module defines imported
  and exported elements so that they create a strong runtime structure
  that can be used for deploying software components independently and
  thus avoiding name clashes. For instance, with modules, two different
  libraries may define a <code>util.List</code> class and be actually running and
  used on the same VM with no naming issues (as long as the libraries are
  bundled in different modules).</p>
<p>Nowadays, a lot of libraries are packaged and accessible through
  modules. The standard way to use modules in a browser is the AMD, but in
  Node.js it is the commonjs module system.</p>
<h4><a href="#modules-in-jsweet" id="modules-in-jsweet">Modules in JSweet</a></h4>
<p>JSweet supports AMD, commonjs, and UMD module systems for packaging.
  JSweet defines a <code>module</code> option (value: <code>amd</code>, <code>commonjs</code> or <code>umd</code>).
  When specifying this option, JSweet automatically creates a default
  module organization following the simple rule: one file = one module.</p>
<p>For example, when packaged with the <code>module</code> option set to <code>commonjs</code>,
  one can write:</p>
<pre><code>> node target/js/x/y/z/MyMainClass.js
</code></pre>
<p>Where <code>MyMainClass</code> contains a <code>main</code> method.</p>
<p>The module system will automatically take care of the references and
  require other modules when needed. Under the hood, JSweet analysis the
  Java import statements and transform them to <code>require</code> instructions.</p>
<p>Note: once the program has been compiled with the <code>module</code> option, it is
  easy to package it as a bundle using appropriate tools such as
  Browserify, which would give similar output as using the <code>bundle</code> option
  of JSweet. Note also that JSweet will raise an error when specifying
  both <code>module</code> and <code>bundle</code>, which are exclusive options.</p>
<h4><a href="#external-modules" id="external-modules">External modules</a></h4>
<p>When compiling JSweet programs with the <code>module</code> options, all external
  libraries and components must be required as external modules. JSweet
  can automatically require modules, simply by using the <code>@Module(name)</code>
  annotation. In JSweet, importing or using a class or a member annotated
  with <code>@Module(name)</code> will automatically require the corresponding module
  at runtime. Please not that it is true only when the code is generated
  with the <code>module</code> option. If the <code>module</code> option is off, the <code>@Module</code>
  annotations are ignored.</p>
<pre><code class="java">package def.jquery;
public final class Globals extends def.js.Object {
    ...
    @jsweet.lang.Module("jquery")
    native public static def.jquery.JQuery $(java.lang.String selector);
    ...
}
</code></pre>
<p>The above code shows an excerpt of the JSweet jQuery API. As we can
  notice, the <code>$</code> function is annotated with <code>@Module("jquery")</code>. As a
  consequence, any call to this function will trigger the require of the
  <code>jquery</code> module.</p>
<p>Note: the notion of manual require of a module may be available in
  future releases. However, automatic require is sufficient for most
  programmers and hides the complexity of having to require modules
  explicitly. It also brings the advantage of having the same code whether
  modules are used or not.</p>
<p>Troubleshooting: when a candy does not define properly the <code>@Module</code>
  annotation, it is possible to force the declaration within the comment
  of a special file called <code>module_defs.java</code>. For example, to force the
  <code>BABYLON</code> namespace of the Babylonjs candy to be exported as a
  <code>babylonjs</code> module, you can write the following file:</p>
<pre><code class="java">package myprogram;
// declare module "babylonjs" {
//    export = BABYLON;
// }
</code></pre>
<p>Note that a JSweet project can only define one <code>module_defs.java</code> file,
  which shall contain all the module declarations in a comment. Note also
  that it is a hack and the preferred method would be to contribute to the
  candy to fix the problem.</p>
<h3><a href="#root-packages" id="root-packages">Root packages</a></h3>
<p>Root packages are a way to tune the generated code so that JSweet
  packages are erased in the generated code and thus at runtime. To set a
  root package, just define a <code>package-info.java</code> file and use the <code>@Root</code>
  annotation on the package, as follows:</p>
<pre><code class="java">@Root
package a.b.c;
</code></pre>
<p>The above declaration means that the <code>c</code> package is a root package, i.e.
  it will be erased in the generated code, as well as all its parent
  packages. Thus, if <code>c</code> contains a package <code>d</code>, and a class <code>C</code>, these
  will be top-level objects at runtime. In other words, <code>a.b.c.d</code> becomes
  <code>d</code>, and <code>a.b.c.C</code> becomes <code>C</code>.</p>
<p>Note that since that packaged placed before the <code>@Root</code> package are
  erased, there cannot be any type defined before a <code>@Root</code> package. In
  the previous example, the <em>a</em> and <em>b</em> packages are necessarily empty
  packages.</p>
<h4><a href="#behavior-when-not-using-modules-default" id="behavior-when-not-using-modules-default">Behavior when not
  using modules (default)</a></h4>
<p>By default, root packages do not change the folder hierarchy of the
  generated files. For instance, the <code>a.b.c.C</code> class will still be
  generated in the <code><jsout>/a/b/c/C.js</code> file (relatively to the <code><jsout></code>
  output directory). However, switching on the <code>noRootDirectories</code> option
  will remove the root directories so that the <code>a.b.c.C</code> class gets
  generated to the <code><jsout>/C.js</code> file.</p>
<p>When not using modules (default), it is possible to have several <code>@Root</code>
  packages (but a <code>@Root</code> package can never contain another <code>@Root</code>
  package).</p>
<h4><a href="#behavior-when-using-modules" id="behavior-when-using-modules">Behavior when using modules</a></h4>
<p>When using modules (see the <em>module</em> option), only one <code>@Root</code> package
  is allowed, and when having one <code>@Root</code> package, no other package or
  type can be outside of the scope of that <code>@Root</code> package. The generated
  folder/file hierarchy then starts at the root package so that all the
  folders before it are actually erased.</p>
<h3><a href="#packaging-a-jsweet-jar-candy" id="packaging-a-jsweet-jar-candy">Packaging a JSweet jar (candy)</a></h3>
<p>A candy is a Maven artifact that contains everything required to easily
  access a JavaScript library from a JSweet client program. This library
  can be an external JavaScript library, a TypeScript program, or another
  JSweet program.</p>
<h4><a href="#anatomy-of-a-candy" id="anatomy-of-a-candy">Anatomy of a candy</a></h4>
<p>Like any Maven artifact, a candy has a group id, a artifact id (name),
  and a version. Besides, a typical candy should contain the following
  elements:</p>
<ol>
  <li>
    <p>The compiled Java files (*.class), so that your client program that
      uses the candy can compile.</p>
  </li>
  <li>
    <p>A <code>META-INF/candy-metadata.json</code> file that contains the expected
      target version of the transpiler (to be adapted to your target
      transpiler version).</p>
  </li>
  <li>
    <p>The program’s declarations in <code>d.ts</code> files, to be placed in the
      <code>src/typings</code> directory of the jar. Note that these definitions are
      not mandatory if you intend to use JSweet for generating TypeScript
      source code (<code>tsOnly</code> option). In that case, you may delegate the
      JavaScript generation to an external <code>tsc</code> compiler and access the
      TypeScript definitions from another source.</p>
  </li>
  <li>
    <p>Optionally, the JavaScript bundle of the library, which can in turn
      be automatically extracted and used by the JSweet client programs.
      JSweet expects the JavaScript to be packaged following the Webjars
      conventions: <a href="http://www.webjars.org/">http://www.webjars.org/</a>. When packaged this way, a
      JSweet transpiler using your candy will automatically extract the
      bundled JavaScript in a directory given by the <code>candiesJsOut</code> option
      (default: <code>js/candies</code>).</p>
  </li>
</ol>
<p>Here is an example of the <code>META-INF/candy-metadata.json</code> file:</p>
<pre><code class="java">{
   "transpilerVersion": "2.0.0"
}
</code></pre>
<h4><a href="#how-to-create-a-candy-from-a-jsweet-program" id="how-to-create-a-candy-from-a-jsweet-program">How to
  create a candy from a JSweet program</a></h4>
<p>A typical use case when building applications with JSweet, is to share a
  common library or module between several other JSweet
  modules/applications. Note that since a JSweet candy is a regular Maven
  artifact, it can also be used by a regular Java program as long as it
  does not use any JavaScript APIs.</p>
<p>So, a typical example in a project is to have a <em>commons</em> library
  containing DTOs and common utility functions, which can be shared
  between a Web client written in JSweet (for example using the angular or
  knockout libraries) and a mobile client written also in JSweet (for
  example using the ionic library). The great news is that this <em>commons</em>
  library can also be used by the Java server (JEE, Spring, ...) as is,
  because the DTOs do not use any JavaScript, and that the compiled Java
  code packaged in the candy can run on a Java VM. This this extremely
  helpful, because it means that when you develop this project in your
  favorite IDE, you will be able to refactor some DTOs and common APIs,
  and it will directly impact your Java server code, your Web client code,
  and your mobile client code!</p>
<p>We provide a quick start project to help you starting with such a use
  case: <a
          href="https://github.com/cincheo/jsweet-candy-quickstart">https://github.com/cincheo/jsweet-candy-quickstart</a>
</p>
<h4><a href="#how-to-create-a-candy-for-an-existing-javascript-or-typescript-library"
       id="how-to-create-a-candy-for-an-existing-javascript-or-typescript-library">How to create a candy for an existing
  JavaScript or TypeScript library</a></h4>
<p>We provide a quick start project to help you starting with such a use
  case: <a href="https://github.com/cincheo/jsweet-candy-js-quickstart">https://github.com/cincheo/jsweet-candy-js-quickstart</a>
</p>
<h2><a href="#extending-the-transpiler" id="extending-the-transpiler">Extending the transpiler</a></h2>
<p>JSweet is an Open Transpiler from Java to TypeScript. It means that it
  provides ways for programmers to tune/extend how JSweet generates the
  intermediate TypeScript code. Tuning the transpiler is a solution to
  avoid repetitive tasks and automatize them.</p>
<p>For instance, say you have a legacy Java code that uses the Java API for
  serializing objects (<code>writeObject</code>/<code>readObject</code>). With JSweet, you can
  easily erase these methods from your program, so that the generated
  JavaScript code is free from any Java-specific serialization idioms.</p>
<p>As another example, say you have a Java legacy code base that uses a
  Java API, which is close to (but not exactly) a JavaScript API you want
  to use in your final JavaScript code. With JSweet, you can write an
  adapter that will automatically map all the Java calls to the
  corresponding JavaScript calls.</p>
<p>Last but not least, you can tune JSweet to take advantage of some
  specific APIs depending on the context. For instance, you may use ES6
  maps if you know that your targeted browser supports them, or just use
  an emulation or a simpler implementation in other cases. You may adapt
  the code to avoid using some canvas or WebGL primitives when knowing
  they are not well supported for a given mobile browser. An so on...
  Using an Open Transpiler such as JSweet has many practical applications,
  which you may or may not have to use, but in any case it is good to be
  aware of what is possible.</p>
<p>Tuning can be done declaratively (as opposed to programmatically) using
  annotations. Annotations can be added to the Java program (hard-coded),
  or they can be centralized in a unique configuration file so that they
  don’t even appear in the Java code (we call them soft annotations).
  Using annotations is quite simple and intuitive. However, when complex
  customization of the transpiler is required, it is most likely that
  annotations are not sufficient anymore. In that case, programmers shall
  use the JSweet extension API, which entails all the tuning that can be
  done with annotations, and much more. The extension API gives access to
  a Java AST (Abstract Syntax Tree) within the context of so-called
  <em>printer adapters</em>. Printer adapters follow a decorator pattern so that
  they can be chained to extend and/or override the way JSweet will print
  out the intermediate TypeScript code.</p>
<h3><a href="#core-annotations" id="core-annotations">Core annotations</a></h3>
<p>The package <code>jsweet.lang</code> defines various annotations that can be used
  to tune the way JSweet generates the intermediate TypeScript code. Here
  we explain these annotations and give examples on how to use them.</p>
<ul>
  <li>
    <p><code>@Erased</code>: This annotation type is used on elements that should be
      erased at generation time. It can be applied to any program element.
      If applied to a type, casts and constructor invocations will
      automatically be removed, potentially leading to program
      inconsistencies. If applied to a method, invocations will be
      removed, potentially leading to program inconsistency, especially
      when the invocation’s result is used in an expression. Because of
      potential inconsistencies, programmers should use this annotation
      carefully, and applied to unused elements (also erasing using
      elements).</p>
  </li>
  <li>
    <p><code>@Root</code>: This package annotation is used to specify a root package
      for the transpiled TypeScript/JavaScript, which means that all
      transpiled references in this package and subpackages will be
      relative to this root package. As an example, given the
      <code>org.mycompany.mylibrary</code> root package (annotated with <code>@Root</code>), the
      class <code>org.mycompany.mylibrary.MyClass</code> will actually correspond to
      <code>MyClass</code> in the JavaScript runtime. Similarly, the
      <code>org.mycompany.mylibrary.mypackage.MyClass</code> will transpile to
      <code>mypackage.MyClass</code>.</p>
  </li>
  <li>
    <p><code>@Name(String value)</code>: This annotation allows the definition of a
      name that will be used for the final generated code (rather than the
      Java name). It can be used when the name of an element is not a
      valid Java identifier. By convention, JSweet implements a built-in
      convention to save the use of @Name annotations: <code>Keyword</code> in Java
      transpiles to <code>keyword</code>, when <code>keyword</code> is a Java keyword (such as
      <code>catch</code>, <code>finally</code>, <code>int</code>, <code>long</code>, and so forth).</p>
  </li>
  <li>
    <p><code>@Replace(String value)</code>: This annotation allows the programmer to
      substitute a method body implementation by a TypeScript
      implementation. The annotation’s value contains TypeScript which is
      generated as is by the JSweet transpiler. The code will be checked
      by the TypeScript transpiler. The replacing code can contain
      variables substituted using a mustache-like convention
      (<code>{{variableName</code>}}). Here is the list of supported variables:</p>
    <ul>
      <li>
        <p><code>{{className}}</code>: the current class.</p>
      </li>
      <li>
        <p><code>{{methodName}}</code>: the current method name.</p>
      </li>
      <li>
        <p><code>{{body}}</code>: the body of the current method. A typical use of
          this variable is to wrap the original behavior in a lambda. For
          instance:
          <code>/* before code */ let _result = () => { {{body}} }(); /* after code */ return _result;</code>.</p>
      </li>
      <li>
        <p><code>{{baseIndent}}</code>: the indentation of the replaced method. Can be
          used to generate well-formatted code.</p>
      </li>
      <li>
        <p><code>{{indent}}</code>: substituted with an indentation. Can be used to
          generate well-formatted code.</p>
      </li>
    </ul>
  </li>
</ul>
<h4><a href="#example" id="example">Example</a></h4>
<p>The following example illustrates the use of the <code>@Erased</code> and
  <code>@Replace</code> annotations. Here, the <code>@Erased</code> annotation is used to remove
  the <code>readObject</code> method from the generated code, because it does not
  make sense in JavaScript (it is a Java-serialization specific method).
  The <code>@Replace</code> annotation allows defining a direct TypeScript/JavaScript
  implementation for the <code>searchAddress</code> method.</p>
<pre><code class="java">class Person {

  List<String> addresses = new ArrayList<String>();

  @Erased
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    [...]
  }

  @Replace("return this.addresses.filter(address => address.match(regex))[0]")
  public String searchAddress(String regex) {
          Optional<String> match = addresses.stream().filter(address -> address.matches(regex)).findFirst();
          return match.isPresent()?match.get():null;
  }
}
</code></pre>
<p>Using JSweet annotations makes it possible to share classes between Java
  and JavaScript in a flexible way. Useless methods in JavaScript are
  erased, and some methods can have different implementations for Java and
  JavaScript.</p>
<h3><a href="#centralizing-annotations-in-jsweetconfigjson" id="centralizing-annotations-in-jsweetconfigjson">Centralizing
  annotations in <code>jsweetconfig.json</code></a></h3>
<p>JSweet supports the definition of annotations within a unique
  configuration file (<code>jsweetconfig.json</code>). There are many reasons why
  programmers would want to define annotations within that file instead of
  defining annotations in the Java source code.</p>
<ol>
  <li>
    <p>Annotations or annotation contents may differ depending on the
      context. It may be convenient to have different configuration files
      depending on the context. It is easier to switch a configuration
      file with another than having to change all the annotations in the
      program.</p>
  </li>
  <li>
    <p>Adding annotations to the Java program is convenient to tune the
      program locally (on a given element). However, in some cases,
      similar annotations should apply on a set of program elements, in
      order to automatize global tuning of the program. In that case, it
      is more convenient to install annotations by using an expression,
      that will match a set of program elements at once. This mechanism is
      similar to the <em>pointcut</em> mechanism that can be found in Aspect
      Oriented Software Design. It allows capturing a global modification
      in a declarative manner.</p>
  </li>
  <li>
    <p>Using annotations in the Java source code entails a reference to the
      JSweet API (the <code>jsweet.lang</code> package) that may be seen as an
      unwanted dependency for some programmers who want their Java code to
      remain as "pure" as possible.</p>
  </li>
</ol>
<p>The JSweet configuration file (<code>jsweetconf.json</code>) is a JSON file
  containing a list of configuration entries. Among the configuration
  entries, so-called global filters can be defined using the following
  structure:</p>
<pre><code class="java"><annotation>: {
      "include": <match_expressions>,
      "exclude": <match_expressions>
}
</code></pre>
<p>Where <code><annotation></code> is the annotation to be added, with potential
  parameters, and <code>include</code> and <code>exclude</code> are lists of match expressions.
  An element in the program will be annotated with the annotation, if its
  signature matches any of the expressions in the <code>include</code> list and does
  not match any the expressions in the <code>exclude</code> list.</p>
<p>A match expression is a sort of simplified regular expression,
  supporting the following wildcards:</p>
<ol>
  <li>
    <p><code>*</code> matches any token or token sub-part in the signature of the
      program element (a token is an identifier part of a signature, for
      instance <code>A.m(java.lang.String)</code> contains the tokens <code>A</code>, <code>m</code>, and
      <code>java.lang.String</code>).</p>
  </li>
  <li>
    <p><code>**</code> matches any list of tokens in signature of the program element.</p>
  </li>
  <li>
    <p><code>..</code> matches any list of tokens in signature of the program element.
      (same as <code>**</code>)</p>
  </li>
  <li>
    <p><code>!</code> negates the expression (first character only).</p>
  </li>
</ol>
<p>For example:</p>
<pre><code class="java">// all the elements and subelements (fields, methods, ...) in the x.y.z package
x.y.z.**

// all the methods in the x.y.z.A class
x.y.z.A.*(..)

// all the methods taking 2 arguments in the /texttt{x.y.z.A} class
x.y.z.A.*(*,*)

// all fields called aField in all the classes of the program
**.aField
</code></pre>
<p>Here is a more complete example with a full <code>jsweetconfig.json</code>
  configuration file.</p>
<pre><code class="java">{
  // all classes and packages in x.y.z will become top level
  "@Root": {
    "include": [ "x.y.z" ]
  },
  // do not generate any TypeScript code for Java-specific methods
  "@Erased": {
    "include": [ "**.writeObject(..)", "**.readObject(..)", "**.hashCode(..)" ]
  },
  // inject logging in all setters and getters of the x.y.z.A class
  "@Replace('console.info('entering {{methodName}}'); let _result = () => { {{body}} }(); console.info('returning '+_result); return _result;')": {
    "include": [ "x.y.z.A.set*(*)", "x.y.z.A.get*()", "x.y.z.A.is*()" ]
  }
}
</code></pre>
<p>Note that annotations are defined with simple names only. That’s because
  they are core JSweet annotations (defined in <code>jsweet.lang</code>). Non-core
  annotations can be added the same way, but the programmer must use fully
  qualified names.</p>
<h3><a href="#programmatic-tuning-with-adapters" id="programmatic-tuning-with-adapters">Programmatic tuning with
  adapters</a></h3>
<p>Declarative tuning through annotation rapidly hits limitations when
  tuning the generation for specific purposes (typically when supporting
  additional Java libraries). Hence, JSweet provides an API so that
  programmers can extend the way JSweet generates the intermediate
  TypeScript code. Writing such an adaptation program is similar to
  writing a regular Java program, except that it will apply to your
  programs to transform them. As such, it falls into the category of
  so-called meta-programs (i.e. programs use other programs as data).
  Since programmers may write extensions that leads to invalid code, that
  is where it becomes really handy to have an intermediate compilation
  layer. If the generated code is invalid, the TypeScript to JavaScript
  compilation will raise errors, thus allowing the programmer to fix the
  extension code.</p>
<h4><a href="#introducing-the-extension-api" id="introducing-the-extension-api">Introducing the extension API</a></h4>
<p>The extension API is available in the <code>org.jsweet.transpiler.extension</code>
  package. It is based on a factory pattern
  (<code>org.jsweet.transpiler.JSweetFactory</code>) that allows the programmer to
  adapt all the main components of the transpiler by subclassing them. In
  practice, most adaptations can be done by creating new printer adapters,
  as subclasses of <code>org.jsweet.transpiler.extension.PrinterAdapter</code>.
  Adapters are the core extension mechanism because they are chainable and
  can be composed (it is a sort of decorator pattern). JSweet uses default
  adapters in a default adaptation chain and tuning JSweet will then
  consist in adding new adapters to the chain.</p>
<p>An adapter will typically perform three kinds of operations to tune the
  generated code:</p>
<ol>
  <li>
    <p>Map Java types to TypeScript ones.</p>
  </li>
  <li>
    <p>Add annotations to the program either in a declarative way (with
      global filters) or in a programmatic way (with annotation managers).</p>
  </li>
  <li>
    <p>Override printing methods defined in <code>PrinterAdapter</code> in order to
      override the TypeScript core that is generated by default. Printing
      methods take program elements, which are based on the standard
      <code>javax.lang.model.element</code> API. It provides an extension of that API
      for program elements that are expressions and statements
      (<code>org.jsweet.transpiler.extension.model</code>).</p>
  </li>
</ol>
<p>The following template shows the typical sections when programming an
  adapter. First, an adapter must extend <code>PrinterAdapter</code> or any other
  adapter. It must define a constructor taking the parent adapter, which
  will be set by JSweet when inserting the adapter in the chain.</p>
<pre><code class="java">public class MyAdapter extends PrinterAdapter {

    public MyAdapter(PrinterAdapter parent) {
        super(parent);
        ...
</code></pre>
<p>In the constructor, an adapter typically maps Java types to TypeScript
  types.</p>
<pre><code class="java">        // will change the type in variable/parameters declarations
        addTypeMapping("AJavaType", "ATypeScriptType");
        // you may want to erase type checking by mapping to 'any'
        addTypeMapping("AJavaType2", "any");
        [...]
</code></pre>
<p>In the constructor, an adapter can also add annotations in a more
  flexible way than when using the <code>jsweetconfig.json</code> syntax.</p>
<pre><code class="java">        // add annotations dynamically to the AST, with global filters
        addAnnotation("jsweet.lang.Erased", //
                "**.readObject(..)", //
                "**.writeObject(..)", //
                "**.hashCode(..)");
        // or with annotation managers (see the Javadoc and the example below)
        addAnnotationManager(new AnnotationManager() { ... });
    }
</code></pre>
<p>Most importantly, an adapter can override substitution methods for most
  important AST elements. By overriding these methods, an adapter will
  change the way JSweet generates the intermediate TypeScript code. To
  print out code, you can use the <code>print</code> method, which is defined in the
  root <code>PrinterAdapter</code> class. For example, the following code will
  replace all <code>new AJavaType(...)</code> with <code>new ATypeScriptType(...)</code>.</p>
<pre><code class="java">    @Override
    public boolean substituteNewClass(NewClassElement newClass) {
        // check if the 'new' applies to the right class
        if ("AJavaType".equals(newClass.getTypeAsElement().toString())) {
            // the 'print' method will generate intermediate TypeScript code
            print("new ATypeScriptType(")
                    .printArgList(newClass.getArguments()).print(")");
            // once some code has been printed, you should return true to break
            // the adapter chain, so your code will replace the default one
            return true;
        }
        // if not substituted, delegate to the adapter chain
        return super.substituteNewClass(newClass);
    }
</code></pre>
<p>Most useful substitution method remains invocation substitution, which
  is typically used to map a Java API to a similar JavaScript API.</p>
<pre><code class="java">    @Override
    public boolean substituteMethodInvocation(MethodInvocationElement invocation) {
        // substitute potential method invocation here
        [...]
        // delegate to the adapter chain
        return super.substituteMethodInvocation(invocation);
    }
}
</code></pre>
<p>Note also a special method to insert code after a Java type has been
  printed out:</p>
<pre><code class="java">    @Override
    public void afterType(TypeElement type) {
        super.afterType(type);
        // insert whatever TypeScript you need here
        [...]
    }
</code></pre>
<p>There are many applications to adapters (see the examples below).
  Besides tuning the code generation and supporting Java APIs at
  compile-time, adapters can also be used to raise errors when the
  compiled code does not conform to expected standards depending on the
  target context. Another very useful use case it to allow the generation
  of proxies. For instance one can write an adapter that will generate
  JavaScript stubs to invoke Java services deployed with JAX-RS.</p>
<h4><a href="#installing-and-activating-adapters" id="installing-and-activating-adapters">Installing and activating
  adapters</a></h4>
<p>Once you have written an adapter, you need to compile it and add it to
  the adapter chain. The simplest way to do it with JSweet is to put it in
  the <code>jsweet_extension</code> directory that you need to create at the root of
  your project JSweet. In that directory, you can directly add Java source
  files for adapters, that will be compiled by JSweet on the fly. For
  instance, you may add two custom adapters <code>CustomAdapter1.java</code> and
  <code>CustomAdapter2.java</code> in <code>jsweet_extension/com/mycompany/</code>.</p>
<p>Then, in order to activate that adapter, you just need to add the
  <code>jsweetconfig.json</code> file at the root of the project and define the
  <code>adapters</code> configuration option, like this:</p>
<pre><code class="java">{
  // JSweet will add the declared adapters at the beginning of the default
  // chain... you can add as many adapters as you need
  adapters: [ "com.mycompany.CustomAdapter1", "com.mycompany.CustomAdapter2" ]
}
</code></pre>
<h4><a href="#hello-world-adapter" id="hello-world-adapter">Hello world adapter</a></h4>
<p>Here, we will step through how to tune the JSweet generation to generate
  strings in place of dates when finding <code>java.util.Date</code> types in the
  Java program.</p>
<p>First, create the <code>HelloWorldAdapter.java</code> file in the
  <code>jsweet_extension</code> directory at the root of your project. Copy and paste
  the following code in that file:</p>
<pre><code class="java">import org.jsweet.transpiler.extension.PrinterAdapter;
public class HelloWorldAdapter extends PrinterAdapter {
    public HelloWorldAdapter(PrinterAdapter parent) {
        super(parent);
        addTypeMapping(java.util.Date.class.getName(), "string");
    }
}
</code></pre>
<p>Second, in the project’s root directory, create the <code>jsweetconfig.json</code>
  file with the following configuration:</p>
<pre><code class="java">{
  adapters: [ "HelloWorldAdapter" ]
}
</code></pre>
<p>Done. Now you can just try this extension on the following simple Java
  DTO:</p>
<pre><code class="java">package source.extension;
import java.util.Date;
/**
 * A Hello World DTO.
 *
 * @author Renaud Pawlak
 */
public class HelloWorldDto {
    private Date date;
    /**
     * Gets the date.
     */
    public Date getDate() {
        return date;
    }
    /**
     * Sets the date.
     */
    public void setDate(Date date) {
        this.date = date;
    }
}
</code></pre>
<p>The generated code should look like:</p>
<pre><code class="java">/* Generated from Java with JSweet 2.XXX - http://www.jsweet.org */
namespace source.extension {
    /**
     * A Hello World DTO.
     *
     * @author Renaud Pawlak
     * @class
     */
    export class HelloWorldDto {
        /*private*/ date : string;
        public constructor() {
            this.date = null;
        }
        /**
         * Gets the date.
         * @return {string}
         */
        public getDate() : string {
            return this.date;
        }
        /**
         * Sets the date.
         * @param {string} date
         */
        public setDate(date : string) {
            this.date = date;
        }
    }
    HelloWorldDto["__class"] = "source.extension.HelloWorldDto";
}
</code></pre>
<p>Note that all the date types have been translated to strings as
  expected. By the way, note also the JSDoc support, which makes JSweet a
  powerful tool to create well-documented JavaScript APIs from Java (doc
  comments are also tunable in adapters!).</p>
<h3><a href="#extension-examples" id="extension-examples">Extension examples</a></h3>
<p>The following sections illustrate the use of JSweet adapters with 5
  real-life examples. Most of these adapters are built-in with JSweet (in
  the <code>org.jsweet.transpiler.extension</code> package) and can just be activated
  by adding them to the adapter chain as explained above. If you want to
  modify the adapters, just copy-paste the code in the <code>jsweet_extension</code>
  directory and change the names.</p>
<h4><a href="#example-1-an-adapter-to-rename-private-fields" id="example-1-an-adapter-to-rename-private-fields">Example
  1: an adapter to rename private fields</a></h4>
<p>This simple adapter renames non-public members by adding two underscores
  as a prefix. Note that this could be dangerous to use for protected
  fields if wanting to access them from subclasses declared in other
  JSweet projects. So you may want to use carefully or to modify the code
  for your own needs.</p>
<p>This adapter is a good example for demonstrating how to use annotation
  managers. Annotation managers are used to add (soft) annotations to
  program elements driven by some Java code (programmatically). Annotation
  managers are added to the context and will be chained to other existing
  annotation managers (potentially added by other adapters). An annotation
  manager must implement the <code>manageAnnotation</code> method, that will tell if
  a given annotation should be added, removed, or left unchanged on a
  given element. If the annotation has parameters, an annotation manager
  shall implement the <code>getAnnotationValue</code> in order to specify the values.</p>
<p>In this example, the annotation manager adds the <code>@jsweet.lang.Name</code>
  annotation to all non-public elements in order to rename them and add
  the underscores to the initial name.</p>
<pre><code class="java">package org.jsweet.transpiler.extension;

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 org.jsweet.transpiler.util.Util;

public class AddPrefixToNonPublicMembersAdapter extends PrinterAdapter {

    public AddPrefixToNonPublicMembersAdapter(PrinterAdapter parentAdapter) {
        super(parentAdapter);
        // add a custom annotation manager to the chain
        addAnnotationManager(new AnnotationManager() {

            @Override
            public Action manageAnnotation(Element element, String annotationType) {
                // add the @Name annotation to non-public elements
                return "jsweet.lang.Name".equals(annotationType)
                        && isNonPublicMember(element) ? Action.ADD : Action.VOID;
            }

            @Override
            public <T> T getAnnotationValue(Element element,
                    String annotationType, String propertyName,
                    Class<T> propertyClass, T defaultValue) {
                // set the name of the added @Name annotation (value)
                if ("jsweet.lang.Name".equals(annotationType) && isNonPublicMember(element)) {
                    return propertyClass.cast("__" + element.getSimpleName());
                } else {
                    return null;
                }
            }

            private boolean isNonPublicMember(Element element) {
                return (element instanceof VariableElement || element instanceof ExecutableElement)
                        && element.getEnclosingElement() instanceof TypeElement
                        && !element.getModifiers().contains(Modifier.PUBLIC)
                        && Util.isSourceElement(element);
            }
        });
    }
}
</code></pre>
<h4><a href="#example-2-an-adapter-to-use-es6-maps" id="example-2-an-adapter-to-use-es6-maps">Example 2: an adapter to
  use ES6 Maps</a></h4>
<p>JSweet default implementation of maps behaves as follows:</p>
<ul>
  <li>
    <p>If the key type is a string, the map is transpiled to a regular
      JavaScript object, where property names will be the keys.</p>
  </li>
  <li>
    <p>If the key type is an object (any other than a string), the map is
      transpiled as a list of entries. The implementation is quite
      inefficient because finding a key requires iterating over the
      entries to find the right entry key.</p>
  </li>
</ul>
<p>In some contexts, you may want to get more efficient map
  implementations. If you target modern browsers (or expect to have the
  appropriate polyfill available), you can simply use the <code>Map</code> object,
  which was standardized with ES6. Doing so requires an adapter that
  performs the following actions:</p>
<ul>
  <li>
    <p>Erase the Java <code>Map</code> type and replace it with the JavaScript <code>Map</code>
      type, or actually the <code>any</code> type, since you may want to keep the
      <code>Object</code> implementation when keys are strings.</p>
  </li>
  <li>
    <p>Substitute the construction of a map with the corresponding
      JavaScript construction.</p>
  </li>
  <li>
    <p>Substitute the invocations on Java maps with the corresponding
      JavaScript invocations.</p>
  </li>
</ul>
<p>Note that the following adapter is a partial implementation that shall
  be extended to support more cases and adapted to your own requirements.
  Additionally, this implementation generates untyped JavaScript in order
  to avoid having to have the ES6 API in the compilation path.</p>
<pre><code class="java">package org.jsweet.transpiler.extension;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.TreeMap;
import javax.lang.model.element.Element;
import org.jsweet.transpiler.model.MethodInvocationElement;
import org.jsweet.transpiler.model.NewClassElement;

public class MapAdapter extends PrinterAdapter {

    // all the Java types that will be translated to ES6 maps
    static String[] mapTypes = {
        Map.class.getName(), HashMap.class.getName(),
        TreeMap.class.getName(), Hashtable.class.getName() };

    public MapAdapter(PrinterAdapter parent) {
        super(parent);
        // rewrite all Java map and compatible map implementations types
        // note that we rewrite to 'any' because we don't want to require the
        // ES6 API to compile (all subsequent accesses will be untyped)
        for (String mapType : mapTypes) {
            addTypeMapping(mapType, "any");
        }
    }

    @Override
    public boolean substituteNewClass(NewClassElement newClass) {
        String className = newClass.getTypeAsElement().toString();
        // map the map constructor to the global 'Map' variable (untyped access)
        if (Arrays.binarySearch(mapTypes, className) >= 0) {
            // this access is browser/node-compatible
            print("new (typeof window == 'undefined'?global:window)['Map'](")
                    .printArgList(newClass.getArguments()).print(")");
            return true;
        }
        // delegate to the adapter chain
        return super.substituteNewClass(newClass);
    }

    @Override
    public boolean substituteMethodInvocation(MethodInvocationElement invocation) {
        if (invocation.getTargetExpression() != null) {
            Element targetType = invocation.getTargetExpression().getTypeAsElement();
            if (Arrays.binarySearch(mapTypes, targetType.toString()) >= 0) {
                // Java Map methods are mapped to their JavaScript equivalent
                switch (invocation.getMethodName()) {
                case "put":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression()).print(".set(")
                            .printArgList(invocation.getArguments())
                            .print(")");
                    return true;
                // although 'get' has the same name, we still rewrite it in case
                // another extension would provide it's own implementation
                case "get":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression()).print(".get(")
                            .printArgList(invocation.getArguments())
                            .print(")");
                    return true;
                case "containsKey":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression()).print(".has(")
                            .printArgList(invocation.getArguments())
                            .print(")");
                    return true;
                // we use the ES6 'Array.from' method in an untyped way to
                // transform the iterator in an array
                case "keySet":
                    printMacroName(invocation.getMethodName());
                    print("(<any>Array).from(")
                            .print(invocation.getTargetExpression()).print(".keys())");
                    return true;
                case "values":
                    printMacroName(invocation.getMethodName());
                    print("(<any>Array).from(")
                            .print(invocation.getTargetExpression()).print(".values())");
                    return true;
                // in ES6 maps, 'size' is a property, not a method
                case "size":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression()).print(".size");
                    return true;
                }
            }

        }
        // delegate to the adapter chain
        return super.substituteMethodInvocation(invocation);
    }
}
</code></pre>
<h4><a href="#example-3-an-adapter-to-support-java-bigdecimal" id="example-3-an-adapter-to-support-java-bigdecimal">Example
  3: an adapter to support Java BigDecimal</a></h4>
<p>Java’s BigDecimal API is a really good API to avoid typical floating
  point precision issues, especially when working on currencies. This API
  is not available by default in JavaScript and would be quite difficult
  to emulate. GWT provides an emulation of the BigDecimal API, which is
  implemented with Java, but JSweet proposes another way to do it, which
  consists of mapping the BigDecimal API to an existing JavaScript API
  called Big.js. Mapping to an existing JS library has several advantages
  compared to emulating an API:</p>
<ol>
  <li>
    <p>The implementation is already available in JavaScript, so there is
      less work emulating the Java library.</p>
  </li>
  <li>
    <p>The implementation is pure JavaScript and is made specifically for
      JavaScript. So we can assume that will be more efficient that an
      emulation, and even more portable.</p>
  </li>
  <li>
    <p>The generated code is free from any Java APIs, which makes it more
      JavaScript friendly and more inter-operable with existing JavaScript
      programs (legacy JavaScript clearly uses Big.js objects, and if not,
      we can decide to tune the adapter).</p>
  </li>
</ol>
<p>The following code shows the adapter that tunes the JavaScript
  generation to map the Java’s BigDecimal API to the Big JavaScript
  library. This extension requires the big.js candy to be available in the
  JSweet classpath: https://github.com/jsweet-candies/candy-bigjs.</p>
<pre><code class="java">package org.jsweet.transpiler.extension;

import java.math.BigDecimal;
import javax.lang.model.element.Element;
import org.jsweet.transpiler.extension.PrinterAdapter;
import org.jsweet.transpiler.model.MethodInvocationElement;
import org.jsweet.transpiler.model.NewClassElement;

public class BigDecimalAdapter extends PrinterAdapter {

    public BigDecimalAdapter(PrinterAdapter parent) {
        super(parent);
        // all BigDecimal types are mapped to Big
        addTypeMapping(BigDecimal.class.getName(), "Big");
    }

    @Override
    public boolean substituteNewClass(NewClassElement newClass) {
        String className = newClass.getTypeAsElement().toString();
        // map the BigDecimal constructors
        if (BigDecimal.class.getName().equals(className)) {
            print("new Big(").printArgList(newClass.getArguments()).print(")");
            return true;
        }
        // delegate to the adapter chain
        return super.substituteNewClass(newClass);
    }

    @Override
    public boolean substituteMethodInvocation(MethodInvocationElement invocation) {
        if (invocation.getTargetExpression() != null) {
            Element targetType = invocation.getTargetExpression().getTypeAsElement();
            if (BigDecimal.class.getName().equals(targetType.toString())) {
                // BigDecimal methods are mapped to their Big.js equivalent
                switch (invocation.getMethodName()) {
                case "multiply":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression())
                            .print(".times(").printArgList(invocation.getArguments())
                            .print(")");
                    return true;
                case "add":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression())
                            .print(".plus(").printArgList(invocation.getArguments())
                            .print(")");
                    return true;
                case "scale":
                    printMacroName(invocation.getMethodName());
                    // we assume that we always have a scale of 2, which is a
                    // good default if we deal with currencies...
                    // to be changed/implemented further
                    print("2");
                    return true;
                case "setScale":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression())
                            .print(".round(").print(invocation.getArguments().get(0))
                            .print(")");
                    return true;
                case "compareTo":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression()).print(".cmp(")
                            .print(invocation.getArguments().get(0))
                            .print(")");
                    return true;
                case "equals":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression()).print(".eq(")
                            .print(invocation.getArguments().get(0))
                            .print(")");
                    return true;
                }
            }

        }
        // delegate to the adapter chain
        return super.substituteMethodInvocation(invocation);
    }
}
</code></pre>
<h4><a href="#example-4-an-adapter-to-map-enums-to-strings" id="example-4-an-adapter-to-map-enums-to-strings">Example 4:
  an adapter to map enums to strings</a></h4>
<p>This example tunes the JavaScript generation to remove enums and replace
  them with strings. It only applies to enums that are annotated with
  @<code>jsweet.lang.StringType</code>.</p>
<p>For instance: <code>@StringType enum MyEnum { A, B, C }</code> will be erased and
  all subsequent accesses to the enum constants will be mapped to simple
  strings (<code>MyEnum.A => "A", MyEnum.B => "B", MyEnum.C => "C"</code>).
  Typically, a method declaration such as <code>void m(MyEnum e) {...}</code> will be
  printed as <code>void m(e : string) {...}</code>. And of course, the invocation
  <code>xxx.m(MyEnum.A)</code> will be printed as <code>xxx.m("A")</code>.</p>
<pre><code class="java">package org.jsweet.transpiler.extension;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import org.jsweet.JSweetConfig;
import org.jsweet.transpiler.model.CaseElement;
import org.jsweet.transpiler.model.ExtendedElement;
import org.jsweet.transpiler.model.MethodInvocationElement;
import org.jsweet.transpiler.model.VariableAccessElement;

public class StringEnumAdapter extends PrinterAdapter {

    private boolean isStringEnum(Element element) {
        // note: this function could be improved to exclude enums that have
        // fields or methods other than the enum constants
        return element.getKind() == ElementKind.ENUM
                && hasAnnotationType(element, JSweetConfig.ANNOTATION_STRING_TYPE);
    }

    public StringEnumAdapter(PrinterAdapter parent) {
        super(parent);
        // eligible enums will be translated to string in JS
        addTypeMapping((typeTree, name) ->
                isStringEnum(typeTree.getTypeAsElement()) ? "string" : null);

        // ignore enum declarations with a programmatic annotation manager
        addAnnotationManager(new AnnotationManager() {
            @Override
            public Action manageAnnotation(Element element, String annotationType) {
                // add the @Erased annotation to string enums
                return JSweetConfig.ANNOTATION_ERASED.equals(annotationType)
                        && isStringEnum(element) ? Action.ADD : Action.VOID;
            }
        });

    }

    @Override
    public boolean substituteMethodInvocation(MethodInvocationElement invocation) {
        if (invocation.getTargetExpression() != null) {
            Element targetType = invocation.getTargetExpression().getTypeAsElement();
            // enum API must be erased and use plain strings instead
            if (isStringEnum(targetType)) {
                switch (invocation.getMethodName()) {
                case "name":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getTargetExpression());
                    return true;
                case "valueOf":
                    printMacroName(invocation.getMethodName());
                    print(invocation.getArgument(0));
                    return true;
                case "equals":
                    printMacroName(invocation.getMethodName());
                    print("(").print(invocation.getTargetExpression()).print(" == ")
                            .print(invocation.getArguments().get(0)).print(")");
                    return true;
                }
            }
        }
        return super.substituteMethodInvocation(invocation);
    }

    @Override
    public boolean substituteVariableAccess(VariableAccessElement variableAccess) {
        // accessing an enum field is replaced by a simple string value
        // (MyEnum.A => "A")
        if (isStringEnum(variableAccess.getTargetElement())) {
            print("/"" + variableAccess.getVariableName() + "/"");
            return true;
        }
        return super.substituteVariableAccess(variableAccess);
    }

    @Override
    public boolean substituteCaseStatementPattern(CaseElement caseStatement,
            ExtendedElement pattern) {
        // map enums to strings in case statements
        if (isStringEnum(pattern.getTypeAsElement())) {
            print("/"" + pattern + "/"");
            return true;
        }
        return super.substituteCaseStatementPattern(caseStatement, pattern);
    }
}
</code></pre>
<h4><a href="#example-5-an-adapter-to-generate-javascript-jax-rs-proxiesstubs"
       id="example-5-an-adapter-to-generate-javascript-jax-rs-proxiesstubs">Example 5: an adapter to generate JavaScript
  JAX-RS proxies/stubs</a></h4>
<p>It is a common use case to implement a WEB or mobile application with
  Java on the server and JavaScript on the client. Typically, a
  JEE/Jackson server will expose a REST API through the JAX-RS
  specifications, and the HTML5 client will have to invoke this API using
  <code>XMLHttpRequest</code> or higher-level libraries such as jQuery. However,
  manually coding the HTTP invocations comes with many drawbacks:</p>
<ul>
  <li>
    <p>It requires the use of specific APIs (XHR, jQuery), which is not
      easy for all programmers and may imply different programming styles
      that would make the code more difficult to read and maintain.</p>
  </li>
  <li>
    <p>It requires the programmers to handle manually the
      serialization/deserialization, while it can be done automatically
      trough the use of annotation-driven generative programming.</p>
  </li>
  <li>
    <p>It leads to unchecked invocations, which means that it is easy for
      the programmer to make an error in the name of the
      service/path/parameters, and in the expected DTOs. No refactoring
      and content-assist is available.</p>
  </li>
</ul>
<p>With a JSweet adapter, using the <code>afterType</code> method it is easy to
  automatically generate a TypeScript stub that is well-typed and performs
  the required operations for invoking the REST service, simply by using
  the service API and the JAX-RS annotations. This type of tooling falls
  in the category of so-called Generative Programming.</p>
<p>The following code is only a partial implementation of an adapter that
  would introspect the program’s model and generate the appropriate stubs
  in TypeScript. It is not meant to be operational, so you need to modify
  to fit your own use case.</p>
<pre><code class="java">import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;

class JaxRSStubAdapter extends PrinterAdapter {

    public JaxRSStubAdapter(PrinterAdapter parent) {
        super(parent);
        // erase service classes (server-side only)
        addAnnotationManager(new AnnotationManager() {
            @Override
            public Action manageAnnotation(Element element, String annotationType) {
                return JSweetConfig.ANNOTATION_ERASED.equals(annotationType)
                        && hasAnnotationType(element, Path.class.getName()) ?
                        Action.ADD : Action.VOID;
            }
        });
    }

    @Override
    public void afterType(TypeElement type) {
        super.afterType(type);
        if (hasAnnotationType(type, Path.class.getName())) {
            // actually generates the JAX-RS stub
            println().printIndent();
            print("class ").print(type.getSimpleName()).print(" {");
            startIndent();
            String typePathAnnotationValue = getAnnotationValue(type,
                    Path.class.getName(), String.class, null);
            String typePath = typePathAnnotationValue != null ? typePathAnnotationValue : "";
            for (Element e : type.getEnclosedElements()) {
                if (e instanceof ExecutableElement
                        && hasAnnotationType(e, GET.class.getName(),
                                PUT.class.getName(), Path.class.getName())) {
                    ExecutableElement method = (ExecutableElement) e;
                    println().printIndent().print(method.getSimpleName().toString())
                            .print("(");
                    for (VariableElement parameter : method.getParameters()) {
                        print(parameter.getSimpleName())
                                .print(" : ").print(getMappedType(parameter.asType()))
                                .print(", ");
                    }
                    print("successHandler : (");
                    if (method.getReturnType().getKind() != TypeKind.VOID) {
                        print("result : ").print(getMappedType(method.getReturnType()));
                    }
                    print(") => void, errorHandler?: () => void").print(") : void");
                    print(" {").println().startIndent().printIndent();
                    String pathAnnotationValue = getAnnotationValue(e, Path.class.getName(),
                            String.class, null);
                    String path = pathAnnotationValue != null ? pathAnnotationValue : "";
                    String httpMethod = "POST";
                    if(hasAnnotationType(e, GET.class.getName())) {
                        httpMethod = "GET";
                    }
                    if(hasAnnotationType(e, POST.class.getName())) {
                        httpMethod = "POST";
                    }
                    String[] consumes = getAnnotationValue(e, "javax.ws.rs.Consumes",
                            String[].class, null);
                    if (consumes == null) {
                        consumes = new String[] { "application/json" };
                    }
                    // actual code to be done
                    print("// modify JaxRSStubAdapter to generate an HTTP invocation here")
                            .println().printIndent();
                    print("//   - httpMethod: " + httpMethod).println().printIndent();
                    print("//   - path: " + typePath + path).println().printIndent();
                    print("//   - consumes: " + consumes[0]);
                    println().endIndent().printIndent().print("}");
                }
            }
            println().endIndent().printIndent().print("}");
        }
    }
}
</code></pre>
<p>NOTE: for compilation, you need the JAX-RS API in your classpath.</p>
<pre><code><dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.1-m07</version>
</dependency>
</code></pre>
<p>As an example, let us consider the following JAX-RS service.</p>
<pre><code class="java">@Path("/hello")
public class HelloWorldService {
    @GET
    @Path("/{param}")
    @Produces(MediaType.APPLICATION_JSON)
    public HelloWorldDto getMsg(@PathParam("param") String msg) {
        String output = "service says : " + msg;
        return new HelloWorldDto(output);
    }
}
</code></pre>
<p>Using the following DTO:</p>
<pre><code class="java">public class HelloWorldDto {
    private String msg;
    public HelloWorldDto(String msg) {
        super();
        this.msg = msg;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}
</code></pre>
<p>If you apply JSweet enhanced with <code>JaxRSStubAdapter</code>, you will get the
  following TypeScript code (and corresponding JavaScript):</p>
<pre><code class="java">export class HelloWorldDto {
    /*private*/ msg : string;
    public constructor(msg : string) {
        this.msg = msg;
    }
    public getMsg() : string {
        return this.msg;
    }
    public setMsg(msg : string) {
        this.msg = msg;
    }
}
HelloWorldDto["__class"] = "HelloWorldDto";

class HelloWorldService {
    getMsg(msg : string,
            successHandler : (result : HelloWorldDto) => void,
            errorHandler?: () => void) : void {
        // modify JaxRSStubAdapter to generate an HTTP invocation here
        //   - httpMethod: GET
        //   - path: /hello/{param}
        //   - consumes: application/json
    }
}
</code></pre>
<p>So, all you need to do is to modify the code of the adapter to generate
  the actual invocation code in place of the comment. Once it is done, you
  can use the generated JavaScript code as a bundle to access your service
  in a well-typed way. Moreover, you can use JSweet to generate the
  TypeScript definitions of your services and DTOs, so that your
  TypeScript client are well-typed (see the JSweet’s <code>declaration</code>
  option).</p>
<h4><a href="#example-6-an-adapter-to-disallow-global-variables" id="example-6-an-adapter-to-disallow-global-variables">Example
  6: an adapter to disallow global variables</a></h4>
<p>This is a quite special adapter since it does not really generate any
  code, but it reports errors when the source code does not conform to
  certain coding standards. Here, we implement a simple constraint that
  reports errors when the user tries to declare global variables (i.e. in
  JSweet non-final static field declared in a <code>Globals</code> class).</p>
<pre><code class="java">package org.jsweet.transpiler.extension;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;

import org.jsweet.transpiler.JSweetProblem;

public class DisallowGlobalVariablesAdapter extends PrinterAdapter {

    public DisallowGlobalVariablesAdapter(PrinterAdapter parentAdapter) {
        super(parentAdapter);
    }

    @Override
    public void afterType(TypeElement type) {
        // we only check for static variables that are in a Globals class but
        // this could be generalized to any static variable
        if (!type.getQualifiedName().toString().startsWith("def.")
                && type.getSimpleName().toString().equals("Globals")) {
            for (Element member : type.getEnclosedElements()) {
                if (member.getKind() == ElementKind.FIELD) {
                    VariableElement field = (VariableElement) member;
                    // only non-final static variable have side effect
                    if (field.getModifiers().contains(Modifier.STATIC)
                            && !field.getModifiers().contains(Modifier.FINAL)) {
                        report(field, JSweetProblem.USER_ERROR, "global variables are not allowed");
                    }
                }
            }
        }
        super.afterType(type);
    }

}
</code></pre>
<p>This adapter falls into the category of static analysis, which can be
  useful (along with code generation) to check that the input complies to
  the expected coding guidelines. It would be easy to enhance this adapter
  to add check on any static fields. A nice feature would be to disable
  the check when the field is annotated with a specific annotation (for
  instance <code>@AllowSideEffect</code>).</p>
<h2><a href="#appendix-1-jsweet-transpiler-options" id="appendix-1-jsweet-transpiler-options">Appendix 1: JSweet
  transpiler options</a></h2>
<pre><code>  [-h|--help]

  [-w|--watch]
        Start a process that watches the input directories for changes and
        re-run transpilation on-the-fly.

  [-v|--verbose]
        Turn on all levels of logging.

  [--encoding <encoding>]
        Force the Java compiler to use a specific encoding (UTF-8, UTF-16, ...).
        (default: UTF-8)

  [--jdkHome <jdkHome>]
        Set the JDK home directory to be used to find the Java compiler. If not
        set, the transpiler will try to use the JAVA_HOME environment variable.
        Note that the expected JDK version is greater or equals to version 8.

  (-i|--input) input1:input2:...:inputN
        An input directory (or column-separated input directories) containing
        Java files to be transpiled. Java files will be recursively looked up in
        sub-directories. Inclusion and exclusion patterns can be defined with
        the 'includes' and 'excludes' options.

  [--includes includes1:includes2:...:includesN ]
        A column-separated list of expressions matching files to be included
        (relatively to the input directory).

  [--excludes excludes1:excludes2:...:excludesN ]
        A column-separated list of expressions matching files to be excluded
        (relatively to the input directory).

  [(-d|--defInput) defInput1:defInput2:...:defInputN ]
        An input directory (or column-separated input directories) containing
        TypeScript definition files (*.d.ts) to be used for transpilation.
        Definition files will be recursively looked up in sub-diredctories.

  [--noRootDirectories]
        Skip the root directories (i.e. packages annotated with
        @jsweet.lang.Root) so that the generated file hierarchy starts at the
        root directories rather than including the entire directory structure.

  [--tsout <tsout>]
        Specify where to place generated TypeScript files. (default: .ts)

  [(-o|--jsout) <jsout>]
        Specify where to place generated JavaScript files (ignored if jsFile is
        specified). (default: js)

  [--disableSinglePrecisionFloats]
        By default, for a target version >=ES5, JSweet will force Java floats to
        be mapped to JavaScript numbers that will be constrained with ES5
        Math.fround function. If this option is true, then the calls to
        Math.fround are erased and the generated program will use the JavaScript
        default precision (double precision).

  [--tsOnly]
        Do not compile the TypeScript output (let an external TypeScript
        compiler do so).

  [--ignoreDefinitions]
        Ignore definitions from def.* packages, so that they are not generated
        in d.ts definition files. If this option is not set, the transpiler
        generates d.ts definition files in the directory given by the tsout
        option.

  [--declaration]
        Generate the d.ts files along with the js files, so that other programs
        can use them to compile.

  [--dtsout <dtsout>]
        Specify where to place generated d.ts files when the declaration option
        is set (by default, d.ts files are generated in the JavaScript output
        directory - next to the corresponding js files).

  [--candiesJsOut <candiesJsOut>]
        Specify where to place extracted JavaScript files from candies.
        (default: js/candies)

  [--sourceRoot <sourceRoot>]
        Specify the location where debugger should locate Java files instead of
        source locations. Use this flag if the sources will be located at
        run-time in a different location than that at design-time. The location
        specified will be embedded in the sourceMap to direct the debugger where
        the source files will be located.

  [--classpath <classpath>]
        The JSweet transpilation classpath (candy jars). This classpath should
        at least contain the core candy.

  [(-m|--module) <module>]
        The module kind (none, commonjs, amd, system or umd). (default: none)

  [-b|--bundle]
        Bundle up all the generated code in a single file, which can be used in
        the browser. The bundle files are called 'bundle.ts', 'bundle.d.ts', or
        'bundle.js' depending on the kind of generated code. NOTE: bundles are
        not compatible with any module kind other than 'none'.

  [(-f|--factoryClassName) <factoryClassName>]
        Use the given factory to tune the default transpiler behavior.

  [--sourceMap]
        Generate source map files for the Java files, so that it is possible to
        debug Java files directly with a debugger that supports source maps
        (most JavaScript debuggers).

  [--enableAssertions]
        Java 'assert' statements are transpiled as runtime JavaScript checks.

  [--header <header>]
        A file that contains a header to be written at the beginning of each
        generated file. If left unspecified, JSweet will generate a default
        header.

  [--workingDir <workingDir>]
        The directory JSweet uses to store temporary files such as extracted
        candies. JSweet uses '.jsweet' if left unspecified.

  [--targetVersion <targetVersion>]
        The EcmaScript target (JavaScript) version. Possible values: [ES3, ES5,
        ES6] (default: ES3)
</code></pre>
<h2><a href="#appendix-2-packaging-and-static-behavior" id="appendix-2-packaging-and-static-behavior">Appendix 2:
  packaging and static behavior</a></h2>
<p>This appendix explains some static behavior with regards to packaging.</p>
<h3><a href="#when-main-methods-are-invoked" id="when-main-methods-are-invoked">When main methods are invoked</a></h3>
<p>When main methods are invoked depends on the way the program is
  packaged.</p>
<ul>
  <li>
    <p><code>module</code>: off, <code>bundle</code>: off. With default packaging, one Java
      source file corresponds to one generated JavaScript file. In that
      case, when loading a file in the browser, all the main methods will
      be invoked right at the end of the file.</p>
  </li>
  <li>
    <p><code>module</code>: off, <code>bundle</code>: on. When the <code>bundle</code> option is on and the
      <code>module</code> option is off, main methods are called at the end of the
      bundle.</p>
  </li>
  <li>
    <p><code>module</code>: on, <code>bundle</code>: off. With module packaging (<code>module</code>
      option), one Java package corresponds to one module. With modules,
      it is mandatory to have only one main method in the program, which
      will be the global entry point from which the module dependency
      graph will be calculated. The main module (the one with the main
      method) will use directly or transitively all the other modules. The
      main method will be invoked at the end of the main module
      evaluation.</p>
  </li>
</ul>
<p>Because of modules, it is good practice to have only one main method in
  an application.</p>
<h3><a href="#static-and-inheritance-dependencies" id="static-and-inheritance-dependencies">Static and inheritance
  dependencies</a></h3>
<p>In TypeScript, programmers need to take care of the ordering of classes
  with regards to static fields and initializers. Typically, a static
  member cannot be initialized with a static member of a class that has
  not yet been defined. Also, a class cannot extend a class that has not
  been defined yet. This forward-dependency issue triggers runtime errors
  when evaluating the generated JavaScript code, which can be quite
  annoying for the programmers and may requires the use of external
  JavaScript bundling tools, such as Browserify.</p>
<p>JSweet’s statics lazy initialization allows static forward references
  within a given file, and within an entire bundle when the <code>bundle</code>
  option is set. Also, when bundling a set of files, JSweet analyses the
  inheritance tree and performs a partial order permutation to eliminate
  forward references in the inheritance tree. Note that TypeScript bundle
  provide a similar feature, but the references need to be manually
  declared, which is not convenient for programmers.</p>
<p>To wrap it up, here are the guidelines to be followed by the programmers
  depending on the packaging method:</p>
<ul>
  <li>
    <p><code>module</code>: off, <code>bundle</code>: off. One JavaScript file is generated per
      Java file. The programmer must take care of including the files in
      the right order in the HTML page, so that there are no forward
      references with regard to inheritance and statics. Within a given
      file, static forward references are allowed, but inheritance forward
      reference are not supported yet (this will be supported in coming
      releases).</p>
  </li>
  <li>
    <p><code>module</code>: off, <code>bundle</code>: on. This configuration produces a unique
      browser-compatible bundle file that can be included in an HTML page.
      Here, the programmer does not have to take care at all of the
      forward references across files. Exactly like in Java, the order
      does not matter. Within a single file, the programmer still have to
      take care of the inheritance forward references (in other words, a
      subclass must be declared after its parent class) (this will be
      supported in coming releases).</p>
  </li>
  <li>
    <p><code>module</code>: commonjs, amd or umd, <code>bundle</code>: off. This configuration
      produces one module file per Java package so that they can be used
      within a module system. For instance, using the <code>commonjs</code> module
      kind will allow the program to run on Node.js. In that
      configuration, the program should contain one main method and only
      the module file containing the main method should be loaded (because
      it will take care loading all the other modules). This configuration
      imposes the same constraint within a single file (no
      forward-references in inheritance).</p>
  </li>
</ul>


</body>
</html>
原文  https://segmentfault.com/a/1190000019731732
正文到此结束
Loading...