mirror of
https://github.com/cincheo/jsweet.git
synced 2025-12-14 23:09:22 +00:00
added a main section on accessing external JavaScript (more info on
ambient declarations and mixins).
This commit is contained in:
parent
2201ef96e3
commit
04afb5e563
@ -20,7 +20,10 @@ Content
|
||||
- [Indexed objects](#indexed-objects)
|
||||
- [Globals](#globals)
|
||||
- [Optional parameters](#optional-parameters)
|
||||
- [Bridging to external JavaScript elements](#bridging-to-external-javascript-elements)
|
||||
- [Ambient declarations](#ambient-declarations)
|
||||
- [Definitions (`def.*` packages)](#definitions-def.-packages)
|
||||
- [Mixins](#mixins)
|
||||
- [Auxiliary types](#auxiliary-types)
|
||||
- [Functional types](#functional-types)
|
||||
- [Object types](#object-types)
|
||||
@ -519,9 +522,14 @@ String m(String s) { return m(s, 0); }
|
||||
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 (<http://www.jsweet.org/candies-releases/> and <http://www.jsweet.org/candies-snapshots/>), which are automatically generated from TypeScript’s DefinitelyTyped. When the candy does not exist, or does not entirely cover what is needed, one can use the `@jsweet.lang.Ambient` annotation, which will make available to the programmers a class definition or an interface.
|
||||
|
||||
### Ambient declarations
|
||||
|
||||
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 (<http://www.jsweet.org/candies-releases/> and <http://www.jsweet.org/candies-snapshots/>), which are automatically generated from TypeScript’s DefinitelyTyped. When the candy does not exist, or does not entirely cover what is needed, one can use the `@jsweet.lang.Ambient` annotation, which will make available to the programmers a class definition or an interface. The following example shows the backbone store class made accessible to the JSweet programmer with a simple ambient declaration. This class is only for typing and will be erased during the JavaScript generation.
|
||||
The following example shows the backbone store class made accessible to the JSweet programmer with a simple ambient declaration. This class is only for typing and will be erased during the JavaScript generation.
|
||||
|
||||
``` java
|
||||
@Ambient
|
||||
@ -530,6 +538,99 @@ class Store {
|
||||
}
|
||||
```
|
||||
|
||||
Note that ambient classes constructors cannot have a body. Also, ambient classes methods must be `abstract` or `native`. For instance:
|
||||
|
||||
``` java
|
||||
@Ambient
|
||||
class MyExternalJavaScriptClass {
|
||||
public native myExternalJavaScriptMethod();
|
||||
}
|
||||
```
|
||||
|
||||
### Definitions (`def.*` packages)
|
||||
|
||||
By convention, putting the classes in a `def.libname` package defines a set of definitions for the `libname` external JavaScript library called `libname`. Definitions are by default all ambient declarations and do not need to be annotated with `@jsweet.lang.Ambient` annotations since they are implicit in `def.*` packages and sub-packages. Note that this mechanism is similar to the TypeScript `d.ts` definition files.
|
||||
|
||||
Candies (bridges to external JavaScript libraries) use definitions. For instance, the jQuery candy defines all the jQuery API in the `def.jquery` package.
|
||||
|
||||
### Mixins
|
||||
|
||||
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 `JQuery` class. For example the jQuery timer plugin adds a `timer` field to the `JQuery` class. As a consequence, the `JQuery` class does not have the same prototype if you are using jQuery alone, or jQuery enhanced with its timer plugin.
|
||||
|
||||
In Java, this extension mechanism is problematic because Java cannot dynamically enhance a given class.
|
||||
|
||||
#### Untyped accesses
|
||||
|
||||
Programmers can access the added element with `$get` accessors and/or with brute-force casting.
|
||||
|
||||
Here is an example using `$get` for the timer plugin case:
|
||||
|
||||
``` java
|
||||
((Timer)$("#myId").$get("timer")).pause();
|
||||
```
|
||||
|
||||
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 `def.jqueryui.JQuery` instead of `def.jquery.JQuery` in order to access the `menu()` function, added by the UI plugin):
|
||||
|
||||
``` java
|
||||
import def.jqueryui.JQuery;
|
||||
[...]
|
||||
Object obj = $("#myMenu");
|
||||
JQuery jq = (JQuery) obj;
|
||||
jq.menu();
|
||||
```
|
||||
|
||||
However, these solutions are not satisfying because clearly unsafe in terms of typing.
|
||||
|
||||
#### Typed accesses with mixins
|
||||
|
||||
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 `@Mixin` annotation. Here is the excerpt of the `def.jqueryui.JQuery` mixin:
|
||||
|
||||
``` java
|
||||
package def.jqueryui;
|
||||
import jsweet.dom.MouseEvent;
|
||||
import jsweet.lang.Function;
|
||||
import jsweet.lang.Date;
|
||||
import jsweet.lang.Array;
|
||||
import jsweet.lang.RegExp;
|
||||
import jsweet.dom.Element;
|
||||
import def.jquery.JQueryEventObject;
|
||||
@jsweet.lang.Interface
|
||||
@jsweet.lang.Mixin(target=def.jquery.JQuery.class)
|
||||
public abstract class JQuery extends jsweet.lang.Object {
|
||||
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();
|
||||
...
|
||||
```
|
||||
|
||||
One can notice the `@jsweet.lang.Mixin(target=def.jquery.JQuery.class)` that states that this mixin will be merged to the `def.jquery.JQuery` so that users will be able to use all the UI plugin members directly and in a well-typed way.
|
||||
|
||||
#### Implementation and how to use
|
||||
|
||||
JSweet merges mixins using a bytecode manipulation tool called Javassist. It takes the mixin classes bytecode, copies all the members to the target classes, and writes the resulting merged classes bytecode to the `.jsweet/candies/processed` directory. As a consequence, in order to benefit the JSweet mixin mechanism, one must add the `.jsweet/candies/processed` directory to the compilation classpath. This directory should be placed before all the other classpath elements so that the mixined results override the original classes (for example the `def.jquery.JQuery` should be overridden and, as a consequence, `.jsweet/candies/processed/def/jquery/JQuery.class` must be found first in the classpath).
|
||||
|
||||
The JSweet transpiler automatically adds the `.jsweet/candies/processed` directory to the compilation classpath so that you do not have to do anything special when using JSweet with Maven. However, when using mixins within an IDE, you must force your project classpath to include this directory in order to ensure compilation of mixin-ed elements. When using the JSweet Eclipse plugin for instance, this is done automatically and transparently for the user. But when not using any plugins, this configuration must be done manually.
|
||||
|
||||
For example, with Eclipse (similar configuration can be made with other IDEs):
|
||||
|
||||
1. Right-click on the project \(>\) Build path \(>\) Configure build path... \(>\) Libraries (tab) \(>\) Add class folder (button). Then choose the `.jsweet/candies/processed` directory.
|
||||
|
||||
2. In the “order and export” tab of the build path dialog, make sure that the `.jsweet/candies/processed` directory appears at the top of the list (or at least before the Maven dependencies).
|
||||
|
||||
NOTE: you do not have to configure anything if you are not using mixins or if you are using the Eclipse plugin.
|
||||
|
||||
Once this configuration is done, you can safely use mixins. For instance, if using the jQuery candy along with jQuery UI, you will be able to write statements such as:
|
||||
|
||||
``` java
|
||||
$("#myMenu").menu();
|
||||
```
|
||||
|
||||
This is neat compared to the untyped access solution because it is checked by the Java compiler (and you will also have completion on mixin-ed elements).
|
||||
|
||||
Auxiliary types
|
||||
---------------
|
||||
|
||||
|
||||
@ -541,9 +541,13 @@ String m(String s) { return m(s, 0); }
|
||||
String m(String s) { return s; }
|
||||
\end{lstlisting}
|
||||
|
||||
\chapter{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 (\url{http://www.jsweet.org/candies-releases/} and \url{http://www.jsweet.org/candies-snapshots/}), which are automatically generated from TypeScript's DefinitelyTyped. When the candy does not exist, or does not entirely cover what is needed, one can use the \texttt{@jsweet.lang.Ambient} annotation, which will make available to the programmers a class definition or an interface.
|
||||
|
||||
\section{Ambient declarations}
|
||||
|
||||
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 (\url{http://www.jsweet.org/candies-releases/} and \url{http://www.jsweet.org/candies-snapshots/}), which are automatically generated from TypeScript's DefinitelyTyped. When the candy does not exist, or does not entirely cover what is needed, one can use the \texttt{@jsweet.lang.Ambient} annotation, which will make available to the programmers a class definition or an interface. The following example shows the backbone store class made accessible to the JSweet programmer with a simple ambient declaration. This class is only for typing and will be erased during the JavaScript generation.
|
||||
The following example shows the backbone store class made accessible to the JSweet programmer with a simple ambient declaration. This class is only for typing and will be erased during the JavaScript generation.
|
||||
|
||||
\begin{lstlisting}[language=Java]
|
||||
@Ambient
|
||||
@ -552,6 +556,100 @@ class Store {
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
Note that ambient classes constructors cannot have a body. Also, ambient classes methods must be \texttt{abstract} or \texttt{native}. For instance:
|
||||
|
||||
\begin{lstlisting}[language=Java]
|
||||
@Ambient
|
||||
class MyExternalJavaScriptClass {
|
||||
public native myExternalJavaScriptMethod();
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\section{Definitions (\texttt{def.*} packages)}
|
||||
|
||||
By convention, putting the classes in a \texttt{def.libname} package defines a set of definitions for the \texttt{libname} external JavaScript library called \texttt{libname}. Definitions are by default all ambient declarations and do not need to be annotated with \texttt{@jsweet.lang.Ambient} annotations since they are implicit in \texttt{def.*} packages and sub-packages. Note that this mechanism is similar to the TypeScript \texttt{d.ts} definition files.
|
||||
|
||||
Candies (bridges to external JavaScript libraries) use definitions. For instance, the jQuery candy defines all the jQuery API in the \texttt{def.jquery} package.
|
||||
|
||||
\section{Mixins}
|
||||
|
||||
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 \texttt{JQuery} class. For example the jQuery timer plugin adds a \texttt{timer} field to the \texttt{JQuery} class. As a consequence, the \texttt{JQuery} class does not have the same prototype if you are using jQuery alone, or jQuery enhanced with its timer plugin.
|
||||
|
||||
In Java, this extension mechanism is problematic because Java cannot dynamically enhance a given class.
|
||||
|
||||
\subsection{Untyped accesses}
|
||||
|
||||
Programmers can access the added element with \texttt{\$get} accessors and/or with brute-force casting.
|
||||
|
||||
Here is an example using \texttt{\$get} for the timer plugin case:
|
||||
|
||||
\begin{lstlisting}[language=Java]
|
||||
((Timer)$("#myId").$get("timer")).pause();
|
||||
\end{lstlisting}
|
||||
|
||||
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 \texttt{def.jqueryui.JQuery} instead of \texttt{def.jquery.JQuery} in order to access the \texttt{menu()} function, added by the UI plugin):
|
||||
|
||||
\begin{lstlisting}[language=Java]
|
||||
import def.jqueryui.JQuery;
|
||||
[...]
|
||||
Object obj = $("#myMenu");
|
||||
JQuery jq = (JQuery) obj;
|
||||
jq.menu();
|
||||
\end{lstlisting}
|
||||
|
||||
However, these solutions are not satisfying because clearly unsafe in terms of typing.
|
||||
|
||||
\subsection{Typed accesses with mixins}
|
||||
|
||||
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 \texttt{@Mixin} annotation. Here is the excerpt of the \texttt{def.jqueryui.JQuery} mixin:
|
||||
|
||||
\begin{lstlisting}[language=Java]
|
||||
package def.jqueryui;
|
||||
import jsweet.dom.MouseEvent;
|
||||
import jsweet.lang.Function;
|
||||
import jsweet.lang.Date;
|
||||
import jsweet.lang.Array;
|
||||
import jsweet.lang.RegExp;
|
||||
import jsweet.dom.Element;
|
||||
import def.jquery.JQueryEventObject;
|
||||
@jsweet.lang.Interface
|
||||
@jsweet.lang.Mixin(target=def.jquery.JQuery.class)
|
||||
public abstract class JQuery extends jsweet.lang.Object {
|
||||
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();
|
||||
...
|
||||
\end{lstlisting}
|
||||
|
||||
One can notice the \texttt{@jsweet.lang.Mixin(target=def.jquery.JQuery.class)} that states that this mixin will be merged to the \texttt{def.jquery.JQuery} so that users will be able to use all the UI plugin members directly and in a well-typed way.
|
||||
|
||||
\subsection{Implementation and how to use}
|
||||
|
||||
JSweet merges mixins using a bytecode manipulation tool called Javassist. It takes the mixin classes bytecode, copies all the members to the target classes, and writes the resulting merged classes bytecode to the \texttt{.jsweet/candies/processed} directory. As a consequence, in order to benefit the JSweet mixin mechanism, one must add the \texttt{.jsweet/candies/processed} directory to the compilation classpath. This directory should be placed before all the other classpath elements so that the mixined results override the original classes (for example the \texttt{def.jquery.JQuery} should be overridden and, as a consequence, \texttt{.jsweet/candies/processed/def/jquery/JQuery.class} must be found first in the classpath).
|
||||
|
||||
The JSweet transpiler automatically adds the \texttt{.jsweet/candies/processed} directory to the compilation classpath so that you do not have to do anything special when using JSweet with Maven. However, when using mixins within an IDE, you must force your project classpath to include this directory in order to ensure compilation of mixin-ed elements. When using the JSweet Eclipse plugin for instance, this is done automatically and transparently for the user. But when not using any plugins, this configuration must be done manually.
|
||||
|
||||
For example, with Eclipse (similar configuration can be made with other IDEs):
|
||||
|
||||
\begin{enumerate}
|
||||
\item Right-click on the project $>$ Build path $>$ Configure build path... $>$ Libraries (tab) $>$ Add class folder (button). Then choose the \texttt{.jsweet/candies/processed} directory.
|
||||
\item In the "order and export" tab of the build path dialog, make sure that the \texttt{.jsweet/candies/processed} directory appears at the top of the list (or at least before the Maven dependencies).
|
||||
\end{enumerate}
|
||||
|
||||
NOTE: you do not have to configure anything if you are not using mixins or if you are using the Eclipse plugin.
|
||||
|
||||
Once this configuration is done, you can safely use mixins. For instance, if using the jQuery candy along with jQuery UI, you will be able to write statements such as:
|
||||
|
||||
\begin{lstlisting}[language=Java]
|
||||
$("#myMenu").menu();
|
||||
\end{lstlisting}
|
||||
|
||||
This is neat compared to the untyped access solution because it is checked by the Java compiler (and you will also have completion on mixin-ed elements).
|
||||
|
||||
\chapter{Auxiliary types}
|
||||
|
||||
JSweet uses most Java typing features (including functional types) but also extends the Java type system with so-called \emph{auxiliary types}. 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 \emph{generics}), 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).
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user