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

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