# GWT

## compatible 規範

符合下列條件的 class 才能在 GWT 內使用：

* primitive data type
* array（陣列元素也必須 compatible）
* enum
* JRE emulation class（包含可用 method）
* 繼承 compatible class
* 所有 field 都符合 compatible 原則

另外，`synchronized` 與 `finalize()` 加了不會出問題，但是 GWT 會直接忽略。

*（沒有考慮撰寫 emulate class 的招數）*

reference：

* JRE compatibility：<http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsCompatibility.html>
* JRE emulation class： <http://www.gwtproject.org/doc/latest/RefJreEmulation.html>

### RPC（serializable）規範

* 滿足 compatible 規範
* implement `IsSerializable` 或是 `Serializable`
* 如果是某個 class 的 inner class，則必須得是 static class（可以不是 public）
* 有 default（沒有參數）constructor，任何 access modifier 均可（private 也無所謂）。

  &#x20; 或是根本沒有任何 constructor。
* 所有 field 都符合 RPC 原則。
  * 例外：final field（不會在 GWT RPC 中傳遞）
  * 例外：transient field（接收端會得到 null）

*（沒有考慮 custom serialization 的招數）*

備註：enum 只傳遞名稱。例如 `FooEnum.FOO`，就只會傳 `FOO`，裡頭的 field 值一概忽略。

reference：

* RPC： <http://www.gwtproject.org/doc/latest/DevGuideServerCommunication.html#DevGuideSerializableTypes>

#### client code 常見炸點 ##\#

假設 `Foo` 是符合 RPC 規範的 class，`fooList` 是 `ArrayList<Foo>`，

* Collections.unmodifiableList(fooList)
* fooList.subList()

上述是因為回傳值的 instance 的 class 不符合 RPC 規範，所以無法過 GWT RPC。

### 雜項

* 預設設定下，在 production mode 的 assert 是不會被觸發的。

  &#x20; （[參考資料](http://www.gwtproject.org/doc/latest/FAQ_Client.html#How_do_I_enable_assertions?)）

## Generator

因為哏太多而且不是正常的開發流程，所以另外開 [GwtGenerator](https://github.com/psmonkey/gwt-wiki/tree/e52cbcce19c5974f24402fcfbfa6574318d72779/GWT/GwtGenerator.md)。

## Deferred Binding

`when-type-is` 的 class 沒有限定一定要 interface（官方文件好像也沒特別註明）， 目前測試用 abstract class 也沒有問題。

## JSNI

`$wnd` 是對應到 JS 的 `window`，也就是說 JSNI 的 `$wnd.alert()` 就等於 JS 的 `window.alert()`。 如果需要 JS 的全域變數，基本上就只能讓 `$wnd` 多加一個變數來處理， 實際上這等同在 host page 的 `<script>` 宣告一個變數：

```
//host page script block
var value = "foo";
const wtf = "wtf";    //用 const 宣告會取不到值，原因不明...... Orz

//in JSNI
$wnd.newGlobalArray = []; //JS 可以亂加 field 不要意外，延伸出來的 JS 哏這裡略過不提
$wnd.alert($wnd.value);    //alert 視窗的訊息會是「foo」
$wnd.alert($wnd.wtf);    //alert 視窗的訊息會是「undefine」
```

`$doc` 是對應到 JS 的 `document`。 注意，如果在 JSNI 裡頭直接操作 `document`，並不會是 host page 的 `document` instance， 而是 host page 裡頭某個 iframe 裡頭的 `document`。 這背後機制暫時不明... \[死]

在 JSNI 裡頭用 `[]` 產生一個 class array 作為回傳值，基本上是不可行， 因為（以 Java 的觀點）缺乏 class 的資訊，而且到了執行期才會真正炸 cast 錯誤。 目前找到最簡單的方法就是自己用 Java 重新包一次... ＝＝"，例如：

```java
//目的：不是要取得整個 entry，而是只要 entry 裡頭的 resource

public native FooJSO[] errorResult() /*-{
    var result = [];

    for (var i = 0; i < this.entry.legnth; i++) {
        result.push(this.entry[i].resource);
    }

    return result;
}-*/;

// ======== //

private final native Foo result(int index) /*-{ return this.entry[index].resource; }-*/;

public List<FooJSO> getResult() {
    ArrayList<FooJSO> result = new ArrayList<>();

    for (int i = 0; i < getSize(); i++) {
        result.add(result(i));
    }

    return result;
}
```

### JSON 相關

client side 的 GWT 將 JSON 字串轉回（偽）Java instance 的議題。

> **注意 ######**
>
> 請不要忘記這是 JS、這是 JS、這是 JS（很重要所以要講三次）， 所以隨便 casting 是很合理的（謎之聲：教練我想寫 Java \[淚目]）。

#### Overlay Type ##\#

Overlay Type 的基礎是 `JavaScriptObject`。 繼承 `JavaScriptObject` 的 class 必須：

* `package` 等級的 class modifier
* 有 `protected` 等級的 default constructor
* 不能有 instance field
* 要嘛 class 宣告成 final，要嘛每個 method（即使不是 native）宣告成 final

主要是用 JSNI 的 getter：

```java
public final native String getId() /*-{ return this.id; }-*/;
```

對一個 JSON 字串作 `JsonUtils.safeEval()` 就會得到 `JavaScriptObject` 或其子孫，端看泛型怎麼指定。 後續呼叫 `JavaScriptObject.cast()` 也可以轉成想要的（繼承 JavaScriptObject）的 class。

**轉換 tip ####**

* 務必在 production mode 作實際測試，會有 SDM 與 production mode 結果不同的情況。
* 無法作 autoboxing
  * 所以即使 `T` 給 primitive type 的 wrapper，

    &#x20; 仍然無法用泛型 `public native <T> getField(String fieldName) /*-{ return this[fieldName]; }-*/;`。
* primitive type、enum（名稱一樣應該就 OK）可以直接轉換。
  * 無法轉換 `long`，compiler 會報錯，用 `Long` 就沒問題
  * 但是這很容易導致一個炸點，在實際使用時還是有可能被當成字串，例如：

    ```java
      public final native int fakeInt() /*-{
          return this.foo == "" ? 0 : this.foo;
      }-*/;

      public final native Integer realInt() /*-{
          return @java.lang.Integer::valueOf(Ljava/lang/String;)(this.foo == "" ? "0" : this.foo);
      }-*/;

      public final void test() {
          Console.log((fakeInt() + 1) + " != " + (realInt() + 1));
      }
    ```

    呼叫 `test()` 會得到如「11 != 2」這種結果。 所以保險起見還是統統 ~~拿去作雞精~~ 轉成標準 class instance。 **注意：** 只有 production mode 才會炸，SDM 結果正常。
* `Date` 轉換：

  ```java
    private final native String fooDate() /*-{ return this.fooDate; }-*/;

    public final Date getFooDate() {
        //JSON 的 datetime 標準據說是用 ISO-8601
        //但是真的用這個 formate 卻無法處理如 "2012-12-21" 這種只有前半段符合的字串 ＝＝"
        //return DateTimeFormat.getFormat(PredefinedFormat.ISO_8601).parse(fooDate());

        //所以... 直接用 Date constructor，底層實作是直接用 JS 解... Orz
        return new Date(fooDate());
        //當然也可以直接寫在 JSNI 裡頭，不過寫起來好囉唆... [蓋牌]
    }
  ```

**炸點 ####**

假設收到這樣一個 JSON 字串

```
["ID", "DATA"]
```

然後這段程式碼就會炸奇怪的問題：

```
HashMap<String, JavaScriptObject> map = new HashMap<>();
JsArrayMixed array = JsonUtils.safeEval(json);
map.put(array.getString(0), array.getObject(1));    //這邊不會有事
JavaScriptObject foo = map.get(array.getString(0));    //這行會炸 casting 問題

WtfJSO wtf = new WtfJSO(array.getObject(1));    //constructor 參數的型態也是 JavaScriptObject
//上面這樣就不會出問題... WTF?
```

#### 相關工具 ##\#

* Online Prettify：<http://json.parser.online.fr/>
* Online Prettify：<http://jsonviewer.stack.hu/>

## UI 相關

### UiBinder

用 UiBinder 弄畫面，執行時一直炸找不到 fooWidget class 的錯誤（但是 Eclipse 沒有 compile error）， 先單純用程式 new FooWidget() 出來，通常就會知道 FooWidget 錯在哪裡了。 （2.5.1 時常見不小心用了 generic 的 `<>` 就炸了，但是 Eclipse 不會炸錯誤 Orz）

#### ui:import ##\#

似乎是 ui.xml 中可以使用 enum 的唯一方法 （[ui:import ref](http://stackoverflow.com/questions/9492658/can-i-use-enum-values-as-field-values-inside-uibinder-template)）。

### I18N

#### gwt.xml ##\#

在 `gwt.xml` 當中要加上有要實作的語系

```markup
    <extend-property name="locale" values="zh_CN"/>
```

至於設定 default locale 沒啥用

```markup
    <set-property-fallback name="locale" value="en"/>
```

個人覺得沒啥用，倒是指定了語系就得要有 `extend-property` 然後也找得到對應的 properties 檔。 而且 default 的 properties 檔案還是不能少...... ＝＝"

掛 `<collapse-all-properties />` 好像不受影響（還是我誤會了這個設定值的意義...... Orz）

#### 實際操作 ##\#

內建原生機制無法徵測 browser 的語系，必須在 host page 給 `<meta>`

或是在 URL 掛上 query string

```
http://127.0.0.1/hostPage.html?locale=zh_CN
```

會優先使用 query string 的設定值。

在 SDM 下，加上 query string 之後、甚至重新 compile 好像都不會順利切換語系。 另外開一個新的 tab 測試會比較實在。

### Image

用 `ImageResource` 的 `Image` 如果要調整大小，

```java
    new Image(imgResource);    //size 太大會留白
    new Image(imgResource.getSafeUri());    //OK
```

沒有 attach 就不會觸發 `LoadEvent`，不過即使 `setVisible(false)` 也沒關係。

### GWT 活跳跳的證據

* 2011 年
  * <https://plus.google.com/+RayCromwell/posts/ivVepvxCu3g> &#x20;

    &#x20; Ray Cromwell 列出使用 GWT 的 Google Product。
* 2013 年：
  * <https://plus.google.com/+ThomasBroyer/posts/iCq9E2Wk3Jz> &#x20;

    &#x20; 新版的 Google Drive 的 Sheet 使用 GWT 撰寫
  * <http://www.slideshare.net/cromwellian1/gwtcreate-keynote-san-francisco> &#x20;

    &#x20; Ray Cromwell 在 Gwt.Create 報告 Google Drive 使用 GWT 撰寫的投影片（p.6）
* 2014 年：
  * <https://news.ycombinator.com/item?id=8552296#up_8554339> &#x20;

    &#x20; Google Inbox、Google Calendar 是 GWT（70%）+ Closure（30%）
* ？：<https://bar.foo/docs.html> &#x20;

  ```
    詭異的 Google 官方網站，證實 Google Document 使用 GWT
  ```

註：Ray Cromwell 是 Googler、現任 GWT 委員長。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gwt.dontcareabout.us/gwt.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
