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 13KB

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