You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ApplicationConnection.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. package com.itmill.toolkit.terminal.gwt.client;
  2. import java.util.Date;
  3. import java.util.HashMap;
  4. import java.util.Iterator;
  5. import java.util.Vector;
  6. import com.google.gwt.http.client.Request;
  7. import com.google.gwt.http.client.RequestBuilder;
  8. import com.google.gwt.http.client.RequestCallback;
  9. import com.google.gwt.http.client.RequestException;
  10. import com.google.gwt.http.client.Response;
  11. import com.google.gwt.json.client.JSONArray;
  12. import com.google.gwt.json.client.JSONObject;
  13. import com.google.gwt.json.client.JSONParser;
  14. import com.google.gwt.json.client.JSONString;
  15. import com.google.gwt.json.client.JSONValue;
  16. import com.google.gwt.user.client.ui.FocusListener;
  17. import com.google.gwt.user.client.ui.FocusWidget;
  18. import com.google.gwt.user.client.ui.HasFocus;
  19. import com.google.gwt.user.client.ui.HasWidgets;
  20. import com.google.gwt.user.client.ui.Widget;
  21. import com.itmill.toolkit.terminal.gwt.client.ui.ContextMenu;
  22. import com.itmill.toolkit.terminal.gwt.client.ui.IView;
  23. /**
  24. * Entry point classes define <code>onModuleLoad()</code>.
  25. */
  26. public class ApplicationConnection implements FocusListener {
  27. private String appUri;
  28. private HashMap resourcesMap = new HashMap();
  29. private static Console console;
  30. private Vector pendingVariables = new Vector();
  31. private HashMap idToPaintable = new HashMap();
  32. private HashMap paintableToId = new HashMap();
  33. private final WidgetSet widgetSet;
  34. private ContextMenu contextMenu = null;
  35. private IView view;
  36. public ApplicationConnection(WidgetSet widgetSet) {
  37. this.widgetSet = widgetSet;
  38. appUri = getAppUri();
  39. if (isDebugMode()) {
  40. console = new DebugConsole();
  41. } else {
  42. console = new NullConsole();
  43. }
  44. makeUidlRequest("repaintAll=1");
  45. // TODO remove hardcoded id name
  46. view = new IView("itmtk-ajax-window");
  47. }
  48. public static Console getConsole() {
  49. return console;
  50. }
  51. private native static boolean isDebugMode() /*-{
  52. var uri = $wnd.location;
  53. var re = /debug[^\/]*$/;
  54. return re.test(uri);
  55. }-*/;
  56. public native String getAppUri()/*-{
  57. var u = $wnd.itmtk.appUri;
  58. if (u.indexOf("/") != 0 && u.indexOf("http") != 0) u = "../../../" +u;
  59. return u;
  60. }-*/;
  61. private native String getPathInfo()/*-{
  62. return $wnd.itmtk.pathInfo;
  63. }-*/;
  64. private void makeUidlRequest(String requestData) {
  65. console.log("Making UIDL Request with params: " + requestData);
  66. RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, appUri
  67. + "/UIDL" + getPathInfo());
  68. rb.setHeader("Content-Type",
  69. "application/x-www-form-urlencoded; charset=utf-8");
  70. try {
  71. rb.sendRequest(requestData, new RequestCallback() {
  72. public void onError(Request request, Throwable exception) {
  73. // TODO Better reporting to user
  74. console.error("Got error");
  75. }
  76. public void onResponseReceived(Request request,
  77. Response response) {
  78. handleReceivedJSONMessage(response);
  79. }
  80. });
  81. } catch (RequestException e) {
  82. // TODO Better reporting to user
  83. console.error(e.getMessage());
  84. }
  85. }
  86. private void handleReceivedJSONMessage(Response response) {
  87. Date start = new Date();
  88. String jsonText = response.getText().substring(3) + "}";
  89. JSONValue json;
  90. try {
  91. json = JSONParser.parse(jsonText);
  92. } catch (com.google.gwt.json.client.JSONException e) {
  93. console.log(e.getMessage() + " - Original JSON-text:");
  94. console.log(jsonText);
  95. return;
  96. }
  97. // Handle redirect
  98. JSONObject redirect = (JSONObject) ((JSONObject) json).get("redirect");
  99. if (redirect != null) {
  100. JSONString url = (JSONString) redirect.get("url");
  101. if (url != null) {
  102. console.log("redirecting to " + url.stringValue());
  103. redirect(url.stringValue());
  104. return;
  105. }
  106. }
  107. // Store resources
  108. JSONObject resources = (JSONObject) ((JSONObject) json)
  109. .get("resources");
  110. for (Iterator i = resources.keySet().iterator(); i.hasNext();) {
  111. String key = (String) i.next();
  112. resourcesMap.put(key, ((JSONString) resources.get(key))
  113. .stringValue());
  114. }
  115. // Store locale data
  116. if (((JSONObject) json).containsKey("locales")) {
  117. JSONArray l = (JSONArray) ((JSONObject) json).get("locales");
  118. for (int i = 0; i < l.size(); i++)
  119. LocaleService.addLocale((JSONObject) l.get(i));
  120. }
  121. // Process changes
  122. JSONArray changes = (JSONArray) ((JSONObject) json).get("changes");
  123. for (int i = 0; i < changes.size(); i++) {
  124. try {
  125. UIDL change = new UIDL((JSONArray) changes.get(i));
  126. try {
  127. console.dirUIDL(change);
  128. } catch (Exception e) {
  129. console.log(e.getMessage());
  130. // TODO: dir doesn't work in any browser although it should
  131. // work (works in hosted mode)
  132. // it partially did at some part but now broken.
  133. }
  134. UIDL uidl = change.getChildUIDL(0);
  135. Paintable paintable = getPaintable(uidl.getId());
  136. if (paintable != null)
  137. paintable.updateFromUIDL(uidl, this);
  138. else {
  139. if (!uidl.getTag().equals("window"))
  140. throw new IllegalStateException("Received update for "
  141. + uidl.getTag()
  142. + ", but there is no such paintable ("
  143. + uidl.getId() + ") registered yet.");
  144. view.updateFromUIDL(uidl, this);
  145. }
  146. } catch (Throwable e) {
  147. e.printStackTrace();
  148. }
  149. }
  150. if (((JSONObject) json).containsKey("meta")) {
  151. JSONObject meta = ((JSONObject) json).get("meta").isObject();
  152. if (meta.containsKey("focus")) {
  153. String focusPid = meta.get("focus").isString().stringValue();
  154. Paintable toBeFocused = this.getPaintable(focusPid);
  155. if (toBeFocused instanceof HasFocus) {
  156. HasFocus toBeFocusedWidget = (HasFocus) toBeFocused;
  157. toBeFocusedWidget.setFocus(true);
  158. }
  159. }
  160. }
  161. long prosessingTime = (new Date().getTime()) - start.getTime();
  162. console.log(" Processing time was " + String.valueOf(prosessingTime)
  163. + "ms for " + jsonText.length() + " characters of JSON");
  164. console.log("Referenced paintables: " + idToPaintable.size());
  165. }
  166. // Redirect browser
  167. private static native void redirect(String url)/*-{
  168. $wnd.location = url;
  169. }-*/;
  170. public void registerPaintable(String id, Paintable paintable) {
  171. idToPaintable.put(id, paintable);
  172. paintableToId.put(paintable, id);
  173. }
  174. public void unregisterPaintable(Paintable p) {
  175. idToPaintable.remove(paintableToId.get(p));
  176. paintableToId.remove(p);
  177. if (p instanceof HasWidgets) {
  178. unregisterChildPaintables((HasWidgets) p);
  179. }
  180. }
  181. public void unregisterChildPaintables(HasWidgets container) {
  182. Iterator it = container.iterator();
  183. while (it.hasNext()) {
  184. Widget w = (Widget) it.next();
  185. if (w instanceof Paintable) {
  186. this.unregisterPaintable((Paintable) w);
  187. }
  188. if (w instanceof HasWidgets) {
  189. unregisterChildPaintables((HasWidgets) w);
  190. }
  191. }
  192. }
  193. /**
  194. * Returns Paintable element by its id
  195. *
  196. * @param id
  197. * Paintable ID
  198. */
  199. public Paintable getPaintable(String id) {
  200. return (Paintable) idToPaintable.get(id);
  201. }
  202. private void addVariableToQueue(String paintableId, String variableName,
  203. String encodedValue, boolean immediate, char type) {
  204. String id = paintableId + "_" + variableName + "_" + type;
  205. for (int i = 0; i < pendingVariables.size(); i += 2)
  206. if ((pendingVariables.get(i)).equals(id)) {
  207. pendingVariables.remove(i);
  208. pendingVariables.remove(i);
  209. break;
  210. }
  211. pendingVariables.add(id);
  212. pendingVariables.add(encodedValue);
  213. if (immediate)
  214. sendPendingVariableChanges();
  215. }
  216. public void sendPendingVariableChanges() {
  217. StringBuffer req = new StringBuffer();
  218. req.append("changes=");
  219. for (int i = 0; i < pendingVariables.size(); i++) {
  220. if (i > 0)
  221. req.append("\u0001");
  222. req.append(pendingVariables.get(i));
  223. }
  224. pendingVariables.clear();
  225. makeUidlRequest(req.toString());
  226. }
  227. private static native String escapeString(String value) /*-{
  228. return encodeURIComponent(value);
  229. }-*/;
  230. public void updateVariable(String paintableId, String variableName,
  231. String newValue, boolean immediate) {
  232. addVariableToQueue(paintableId, variableName, escapeString(newValue),
  233. immediate, 's');
  234. }
  235. public void updateVariable(String paintableId, String variableName,
  236. int newValue, boolean immediate) {
  237. addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
  238. 'i');
  239. }
  240. public void updateVariable(String paintableId, String variableName,
  241. long newValue, boolean immediate) {
  242. addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
  243. 'l');
  244. }
  245. public void updateVariable(String paintableId, String variableName,
  246. float newValue, boolean immediate) {
  247. addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
  248. 'f');
  249. }
  250. public void updateVariable(String paintableId, String variableName,
  251. double newValue, boolean immediate) {
  252. addVariableToQueue(paintableId, variableName, "" + newValue, immediate,
  253. 'd');
  254. }
  255. public void updateVariable(String paintableId, String variableName,
  256. boolean newValue, boolean immediate) {
  257. addVariableToQueue(paintableId, variableName, newValue ? "true"
  258. : "false", immediate, 'b');
  259. }
  260. public void updateVariable(String paintableId, String variableName,
  261. Object[] values, boolean immediate) {
  262. StringBuffer buf = new StringBuffer();
  263. for (int i = 0; i < values.length; i++) {
  264. if (i > 0)
  265. buf.append(",");
  266. buf.append(escapeString(values[i].toString()));
  267. }
  268. addVariableToQueue(paintableId, variableName, buf.toString(),
  269. immediate, 'a');
  270. }
  271. public static Container getParentLayout(Widget component) {
  272. Widget parent = component.getParent();
  273. while (parent != null && !(parent instanceof Container))
  274. parent = parent.getParent();
  275. if (parent != null && ((Container) parent).hasChildComponent(component))
  276. return (Container) parent;
  277. return null;
  278. }
  279. /**
  280. * Update generic component features.
  281. *
  282. * <h2>Selecting correct implementation</h2>
  283. *
  284. * <p>
  285. * The implementation of a component depends on many properties, including
  286. * styles, component features, etc. Sometimes the user changes those
  287. * properties after the component has been created. Calling this method in
  288. * the beginning of your updateFromUIDL -method automatically replaces your
  289. * component with more appropriate if the requested implementation changes.
  290. * </p>
  291. *
  292. * <h2>Caption, icon, error messages and description</h2>
  293. *
  294. * <p>
  295. * Component can delegate management of caption, icon, error messages and
  296. * description to parent layout. This is optional an should be decided by
  297. * component author
  298. * </p>
  299. *
  300. * <h2>Component visibility and disabling</h2>
  301. *
  302. * This method will manage component visibility automatically and if
  303. * component is an instanceof FocusWidget, also handle component disabling
  304. * when needed.
  305. *
  306. * @param currentWidget
  307. * Current widget that might need replacement
  308. * @param uidl
  309. * UIDL to be painted
  310. * @param manageCaption
  311. * True if you want to delegate caption, icon, description and
  312. * error message management to parent.
  313. *
  314. * @return Returns true iff no further painting is needed by caller
  315. */
  316. public boolean updateComponent(Widget component, UIDL uidl,
  317. boolean manageCaption) {
  318. // If the server request that a cached instance should be used, do
  319. // nothing
  320. if (uidl.getBooleanAttribute("cached"))
  321. return true;
  322. // Switch to correct implementation if needed
  323. if (!widgetSet.isCorrectImplementation(component, uidl)) {
  324. Container parent = getParentLayout(component);
  325. if (parent != null) {
  326. Widget w = widgetSet.createWidget(uidl);
  327. parent.replaceChildComponent(component, w);
  328. registerPaintable(uidl.getId(), (Paintable) w);
  329. ((Paintable) w).updateFromUIDL(uidl, this);
  330. return true;
  331. }
  332. }
  333. // Set captions
  334. // TODO Manage Error messages
  335. if (manageCaption) {
  336. Container parent = getParentLayout(component);
  337. if (parent != null)
  338. parent.updateCaption((Paintable) component, uidl);
  339. }
  340. // Visibility, Disabling and read-only status
  341. if (component instanceof FocusWidget) {
  342. boolean enabled = true;
  343. if (uidl.hasAttribute("disabled"))
  344. enabled = !uidl.getBooleanAttribute("disabled");
  345. else if (uidl.hasAttribute("readonly"))
  346. enabled = !uidl.getBooleanAttribute("readonly");
  347. ((FocusWidget) component).setEnabled(enabled);
  348. } else {
  349. boolean enabled = true;
  350. if (uidl.hasAttribute("disabled"))
  351. enabled = !uidl.getBooleanAttribute("disabled");
  352. if (!enabled)
  353. component.addStyleName("i-disabled");
  354. else
  355. component.removeStyleName("i-disabled");
  356. }
  357. boolean visible = !uidl.getBooleanAttribute("invisible");
  358. component.setVisible(visible);
  359. if (!visible)
  360. return true;
  361. // add additional styles as css classes
  362. if (uidl.hasAttribute("style"))
  363. component.addStyleName(uidl.getStringAttribute("style"));
  364. return false;
  365. }
  366. /**
  367. * Get either existing or new widget for given UIDL.
  368. *
  369. * If corresponding paintable has been previously painted, return it.
  370. * Otherwise create and register a new widget from UIDL. Caller must update
  371. * the returned widget from UIDL after it has been connected to parent.
  372. *
  373. * @param uidl
  374. * UIDL to create widget from.
  375. * @return Either existing or new widget corresponding to UIDL.
  376. */
  377. public Widget getWidget(UIDL uidl) {
  378. String id = uidl.getId();
  379. Widget w = (Widget) getPaintable(id);
  380. if (w != null)
  381. return w;
  382. w = widgetSet.createWidget(uidl);
  383. registerPaintable(id, (Paintable) w);
  384. return w;
  385. }
  386. public String getResource(String name) {
  387. return (String) resourcesMap.get(name);
  388. }
  389. /**
  390. * Singleton method to get instance of app's context menu.
  391. *
  392. * @return IContextMenu object
  393. */
  394. public ContextMenu getContextMenu() {
  395. if (contextMenu == null) {
  396. contextMenu = new ContextMenu();
  397. }
  398. return contextMenu;
  399. }
  400. public void onFocus(Widget sender) {
  401. // TODO Auto-generated method stub
  402. }
  403. public void onLostFocus(Widget sender) {
  404. // TODO Auto-generated method stub
  405. }
  406. }