Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

JsonPaintTarget.java 34KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.server;
  5. import java.io.PrintWriter;
  6. import java.io.Serializable;
  7. import java.util.Collection;
  8. import java.util.HashSet;
  9. import java.util.Iterator;
  10. import java.util.LinkedList;
  11. import java.util.Map;
  12. import java.util.Set;
  13. import java.util.Stack;
  14. import java.util.Vector;
  15. import com.vaadin.Application;
  16. import com.vaadin.terminal.ApplicationResource;
  17. import com.vaadin.terminal.ExternalResource;
  18. import com.vaadin.terminal.PaintException;
  19. import com.vaadin.terminal.PaintTarget;
  20. import com.vaadin.terminal.Paintable;
  21. import com.vaadin.terminal.Resource;
  22. import com.vaadin.terminal.ThemeResource;
  23. import com.vaadin.terminal.VariableOwner;
  24. import com.vaadin.ui.Alignment;
  25. import com.vaadin.ui.ClientWidget;
  26. import com.vaadin.ui.Component;
  27. /**
  28. * User Interface Description Language Target.
  29. *
  30. * @author IT Mill Ltd.
  31. * @version
  32. * @VERSION@
  33. * @since 5.0
  34. */
  35. @SuppressWarnings("serial")
  36. public class JsonPaintTarget implements PaintTarget {
  37. /* Document type declarations */
  38. private final static String UIDL_ARG_NAME = "name";
  39. private final Stack<String> mOpenTags;
  40. private final Stack<JsonTag> openJsonTags;
  41. private final PrintWriter uidlBuffer;
  42. private boolean closed = false;
  43. private final CommunicationManager manager;
  44. private int changes = 0;
  45. private Set<Object> usedResources = new HashSet<Object>();
  46. private boolean customLayoutArgumentsOpen = false;
  47. private JsonTag tag;
  48. private int errorsOpen;
  49. private boolean cacheEnabled = false;
  50. private Collection<Paintable> paintedComponents = new HashSet<Paintable>();
  51. private Collection<Paintable> identifiersCreatedDueRefPaint;
  52. private Collection<Class<? extends Paintable>> usedPaintableTypes = new LinkedList<Class<? extends Paintable>>();
  53. /**
  54. * Creates a new XMLPrintWriter, without automatic line flushing.
  55. *
  56. * @param variableMap
  57. * @param manager
  58. * @param outWriter
  59. * A character-output stream.
  60. * @throws PaintException
  61. * if the paint operation failed.
  62. */
  63. public JsonPaintTarget(CommunicationManager manager, PrintWriter outWriter,
  64. boolean cachingRequired) throws PaintException {
  65. this.manager = manager;
  66. // Sets the target for UIDL writing
  67. uidlBuffer = outWriter;
  68. // Initialize tag-writing
  69. mOpenTags = new Stack<String>();
  70. openJsonTags = new Stack<JsonTag>();
  71. cacheEnabled = cachingRequired;
  72. }
  73. public void startTag(String tagName) throws PaintException {
  74. startTag(tagName, false);
  75. }
  76. /**
  77. * Prints the element start tag.
  78. *
  79. * <pre>
  80. * Todo:
  81. * Checking of input values
  82. *
  83. * </pre>
  84. *
  85. * @param tagName
  86. * the name of the start tag.
  87. * @throws PaintException
  88. * if the paint operation failed.
  89. *
  90. */
  91. public void startTag(String tagName, boolean isChildNode)
  92. throws PaintException {
  93. // In case of null data output nothing:
  94. if (tagName == null) {
  95. throw new NullPointerException();
  96. }
  97. // Ensures that the target is open
  98. if (closed) {
  99. throw new PaintException(
  100. "Attempted to write to a closed PaintTarget.");
  101. }
  102. if (tag != null) {
  103. openJsonTags.push(tag);
  104. }
  105. // Checks tagName and attributes here
  106. mOpenTags.push(tagName);
  107. tag = new JsonTag(tagName);
  108. customLayoutArgumentsOpen = "customlayout".equals(tagName);
  109. if ("error".equals(tagName)) {
  110. errorsOpen++;
  111. }
  112. }
  113. /**
  114. * Prints the element end tag.
  115. *
  116. * If the parent tag is closed before every child tag is closed an
  117. * PaintException is raised.
  118. *
  119. * @param tag
  120. * the name of the end tag.
  121. * @throws Paintexception
  122. * if the paint operation failed.
  123. */
  124. public void endTag(String tagName) throws PaintException {
  125. // In case of null data output nothing:
  126. if (tagName == null) {
  127. throw new NullPointerException();
  128. }
  129. // Ensure that the target is open
  130. if (closed) {
  131. throw new PaintException(
  132. "Attempted to write to a closed PaintTarget.");
  133. }
  134. if (openJsonTags.size() > 0) {
  135. final JsonTag parent = openJsonTags.pop();
  136. String lastTag = "";
  137. lastTag = mOpenTags.pop();
  138. if (!tagName.equalsIgnoreCase(lastTag)) {
  139. throw new PaintException("Invalid UIDL: wrong ending tag: '"
  140. + tagName + "' expected: '" + lastTag + "'.");
  141. }
  142. // simple hack which writes error uidl structure into attribute
  143. if ("error".equals(lastTag)) {
  144. if (errorsOpen == 1) {
  145. parent.addAttribute("\"error\":[\"error\",{}"
  146. + tag.getData() + "]");
  147. } else {
  148. // sub error
  149. parent.addData(tag.getJSON());
  150. }
  151. errorsOpen--;
  152. } else {
  153. parent.addData(tag.getJSON());
  154. }
  155. tag = parent;
  156. } else {
  157. changes++;
  158. uidlBuffer.print(((changes > 1) ? "," : "") + tag.getJSON());
  159. tag = null;
  160. }
  161. }
  162. /**
  163. * Substitutes the XML sensitive characters with predefined XML entities.
  164. *
  165. * @param xml
  166. * the String to be substituted.
  167. * @return A new string instance where all occurrences of XML sensitive
  168. * characters are substituted with entities.
  169. */
  170. static public String escapeXML(String xml) {
  171. if (xml == null || xml.length() <= 0) {
  172. return "";
  173. }
  174. return escapeXML(new StringBuffer(xml)).toString();
  175. }
  176. /**
  177. * Substitutes the XML sensitive characters with predefined XML entities.
  178. *
  179. * @param xml
  180. * the String to be substituted.
  181. * @return A new StringBuffer instance where all occurrences of XML
  182. * sensitive characters are substituted with entities.
  183. *
  184. */
  185. static public StringBuffer escapeXML(StringBuffer xml) {
  186. if (xml == null || xml.length() <= 0) {
  187. return new StringBuffer("");
  188. }
  189. final StringBuffer result = new StringBuffer(xml.length() * 2);
  190. for (int i = 0; i < xml.length(); i++) {
  191. final char c = xml.charAt(i);
  192. final String s = toXmlChar(c);
  193. if (s != null) {
  194. result.append(s);
  195. } else {
  196. result.append(c);
  197. }
  198. }
  199. return result;
  200. }
  201. static public String escapeJSON(String s) {
  202. if (s == null) {
  203. return "";
  204. }
  205. final StringBuffer sb = new StringBuffer();
  206. for (int i = 0; i < s.length(); i++) {
  207. final char ch = s.charAt(i);
  208. switch (ch) {
  209. case '"':
  210. sb.append("\\\"");
  211. break;
  212. case '\\':
  213. sb.append("\\\\");
  214. break;
  215. case '\b':
  216. sb.append("\\b");
  217. break;
  218. case '\f':
  219. sb.append("\\f");
  220. break;
  221. case '\n':
  222. sb.append("\\n");
  223. break;
  224. case '\r':
  225. sb.append("\\r");
  226. break;
  227. case '\t':
  228. sb.append("\\t");
  229. break;
  230. case '/':
  231. sb.append("\\/");
  232. break;
  233. default:
  234. if (ch >= '\u0000' && ch <= '\u001F') {
  235. final String ss = Integer.toHexString(ch);
  236. sb.append("\\u");
  237. for (int k = 0; k < 4 - ss.length(); k++) {
  238. sb.append('0');
  239. }
  240. sb.append(ss.toUpperCase());
  241. } else {
  242. sb.append(ch);
  243. }
  244. }
  245. }
  246. return sb.toString();
  247. }
  248. /**
  249. * Substitutes a XML sensitive character with predefined XML entity.
  250. *
  251. * @param c
  252. * the Character to be replaced with an entity.
  253. * @return String of the entity or null if character is not to be replaced
  254. * with an entity.
  255. */
  256. private static String toXmlChar(char c) {
  257. switch (c) {
  258. case '&':
  259. return "&amp;"; // & => &amp;
  260. case '>':
  261. return "&gt;"; // > => &gt;
  262. case '<':
  263. return "&lt;"; // < => &lt;
  264. case '"':
  265. return "&quot;"; // " => &quot;
  266. case '\'':
  267. return "&apos;"; // ' => &apos;
  268. default:
  269. return null;
  270. }
  271. }
  272. /**
  273. * Prints XML-escaped text.
  274. *
  275. * @param str
  276. * @throws PaintException
  277. * if the paint operation failed.
  278. *
  279. */
  280. public void addText(String str) throws PaintException {
  281. tag.addData("\"" + escapeJSON(str) + "\"");
  282. }
  283. /**
  284. * Adds a boolean attribute to component. Atributes must be added before any
  285. * content is written.
  286. *
  287. * @param name
  288. * the Attribute name.
  289. * @param value
  290. * the Attribute value.
  291. * @throws PaintException
  292. * if the paint operation failed.
  293. */
  294. public void addAttribute(String name, boolean value) throws PaintException {
  295. tag.addAttribute("\"" + name + "\":" + (value ? "true" : "false"));
  296. }
  297. /**
  298. * Adds a resource attribute to component. Attributes must be added before
  299. * any content is written.
  300. *
  301. * @param name
  302. * the Attribute name.
  303. * @param value
  304. * the Attribute value.
  305. *
  306. * @throws PaintException
  307. * if the paint operation failed.
  308. */
  309. public void addAttribute(String name, Resource value) throws PaintException {
  310. if (value instanceof ExternalResource) {
  311. addAttribute(name, ((ExternalResource) value).getURL());
  312. } else if (value instanceof ApplicationResource) {
  313. final ApplicationResource r = (ApplicationResource) value;
  314. final Application a = r.getApplication();
  315. if (a == null) {
  316. throw new PaintException(
  317. "Application not specified for resorce "
  318. + value.getClass().getName());
  319. }
  320. String uri;
  321. if (a.getURL() != null) {
  322. uri = a.getURL().getPath();
  323. } else {
  324. uri = "";
  325. }
  326. if (uri.length() > 0 && uri.charAt(uri.length() - 1) != '/') {
  327. uri += "/";
  328. }
  329. uri += a.getRelativeLocation(r);
  330. addAttribute(name, uri);
  331. } else if (value instanceof ThemeResource) {
  332. final String uri = "theme://"
  333. + ((ThemeResource) value).getResourceId();
  334. addAttribute(name, uri);
  335. } else {
  336. throw new PaintException("Ajax adapter does not "
  337. + "support resources of type: "
  338. + value.getClass().getName());
  339. }
  340. }
  341. /**
  342. * Adds a integer attribute to component. Atributes must be added before any
  343. * content is written.
  344. *
  345. * @param name
  346. * the Attribute name.
  347. * @param value
  348. * the Attribute value.
  349. *
  350. * @throws PaintException
  351. * if the paint operation failed.
  352. */
  353. public void addAttribute(String name, int value) throws PaintException {
  354. tag.addAttribute("\"" + name + "\":" + String.valueOf(value));
  355. }
  356. /**
  357. * Adds a long attribute to component. Atributes must be added before any
  358. * content is written.
  359. *
  360. * @param name
  361. * the Attribute name.
  362. * @param value
  363. * the Attribute value.
  364. *
  365. * @throws PaintException
  366. * if the paint operation failed.
  367. */
  368. public void addAttribute(String name, long value) throws PaintException {
  369. tag.addAttribute("\"" + name + "\":" + String.valueOf(value));
  370. }
  371. /**
  372. * Adds a float attribute to component. Atributes must be added before any
  373. * content is written.
  374. *
  375. * @param name
  376. * the Attribute name.
  377. * @param value
  378. * the Attribute value.
  379. *
  380. * @throws PaintException
  381. * if the paint operation failed.
  382. */
  383. public void addAttribute(String name, float value) throws PaintException {
  384. tag.addAttribute("\"" + name + "\":" + String.valueOf(value));
  385. }
  386. /**
  387. * Adds a double attribute to component. Atributes must be added before any
  388. * content is written.
  389. *
  390. * @param name
  391. * the Attribute name.
  392. * @param value
  393. * the Attribute value.
  394. *
  395. * @throws PaintException
  396. * if the paint operation failed.
  397. */
  398. public void addAttribute(String name, double value) throws PaintException {
  399. tag.addAttribute("\"" + name + "\":" + String.valueOf(value));
  400. }
  401. /**
  402. * Adds a string attribute to component. Atributes must be added before any
  403. * content is written.
  404. *
  405. * @param name
  406. * the String attribute name.
  407. * @param value
  408. * the String attribute value.
  409. *
  410. * @throws PaintException
  411. * if the paint operation failed.
  412. */
  413. public void addAttribute(String name, String value) throws PaintException {
  414. // In case of null data output nothing:
  415. if ((value == null) || (name == null)) {
  416. throw new NullPointerException(
  417. "Parameters must be non-null strings");
  418. }
  419. tag.addAttribute("\"" + name + "\": \"" + escapeJSON(value) + "\"");
  420. if (customLayoutArgumentsOpen && "template".equals(name)) {
  421. getUsedResources().add("layouts/" + value + ".html");
  422. }
  423. if (name.equals("locale")) {
  424. manager.requireLocale(value);
  425. }
  426. }
  427. public void addAttribute(String name, Map<?, ?> value)
  428. throws PaintException {
  429. StringBuilder sb = new StringBuilder();
  430. sb.append("\"");
  431. sb.append(name);
  432. sb.append("\": ");
  433. sb.append("{");
  434. for (Iterator<?> it = value.keySet().iterator(); it.hasNext();) {
  435. Object key = it.next();
  436. Object mapValue = value.get(key);
  437. sb.append("\"");
  438. if (key instanceof Paintable) {
  439. Paintable paintable = (Paintable) key;
  440. sb.append(getPaintIdentifier(paintable));
  441. } else {
  442. sb.append(escapeJSON(key.toString()));
  443. }
  444. sb.append("\":");
  445. if (mapValue instanceof Float || mapValue instanceof Integer
  446. || mapValue instanceof Double
  447. || mapValue instanceof Boolean
  448. || mapValue instanceof Alignment) {
  449. sb.append(mapValue);
  450. } else {
  451. sb.append("\"");
  452. sb.append(escapeJSON(mapValue.toString()));
  453. sb.append("\"");
  454. }
  455. if (it.hasNext()) {
  456. sb.append(",");
  457. }
  458. }
  459. sb.append("}");
  460. tag.addAttribute(sb.toString());
  461. }
  462. public void addAttribute(String name, Object[] values) {
  463. // In case of null data output nothing:
  464. if ((values == null) || (name == null)) {
  465. throw new NullPointerException(
  466. "Parameters must be non-null strings");
  467. }
  468. final StringBuffer buf = new StringBuffer();
  469. buf.append("\"" + name + "\":[");
  470. for (int i = 0; i < values.length; i++) {
  471. if (i > 0) {
  472. buf.append(",");
  473. }
  474. buf.append("\"");
  475. buf.append(escapeJSON(values[i].toString()));
  476. buf.append("\"");
  477. }
  478. buf.append("]");
  479. tag.addAttribute(buf.toString());
  480. }
  481. /**
  482. * Adds a string type variable.
  483. *
  484. * @param owner
  485. * the Listener for variable changes.
  486. * @param name
  487. * the Variable name.
  488. * @param value
  489. * the Variable initial value.
  490. *
  491. * @throws PaintException
  492. * if the paint operation failed.
  493. */
  494. public void addVariable(VariableOwner owner, String name, String value)
  495. throws PaintException {
  496. tag.addVariable(new StringVariable(owner, name, escapeJSON(value)));
  497. }
  498. /**
  499. * Adds a int type variable.
  500. *
  501. * @param owner
  502. * the Listener for variable changes.
  503. * @param name
  504. * the Variable name.
  505. * @param value
  506. * the Variable initial value.
  507. *
  508. * @throws PaintException
  509. * if the paint operation failed.
  510. */
  511. public void addVariable(VariableOwner owner, String name, int value)
  512. throws PaintException {
  513. tag.addVariable(new IntVariable(owner, name, value));
  514. }
  515. /**
  516. * Adds a long type variable.
  517. *
  518. * @param owner
  519. * the Listener for variable changes.
  520. * @param name
  521. * the Variable name.
  522. * @param value
  523. * the Variable initial value.
  524. *
  525. * @throws PaintException
  526. * if the paint operation failed.
  527. */
  528. public void addVariable(VariableOwner owner, String name, long value)
  529. throws PaintException {
  530. tag.addVariable(new LongVariable(owner, name, value));
  531. }
  532. /**
  533. * Adds a float type variable.
  534. *
  535. * @param owner
  536. * the Listener for variable changes.
  537. * @param name
  538. * the Variable name.
  539. * @param value
  540. * the Variable initial value.
  541. *
  542. * @throws PaintException
  543. * if the paint operation failed.
  544. */
  545. public void addVariable(VariableOwner owner, String name, float value)
  546. throws PaintException {
  547. tag.addVariable(new FloatVariable(owner, name, value));
  548. }
  549. /**
  550. * Adds a double type variable.
  551. *
  552. * @param owner
  553. * the Listener for variable changes.
  554. * @param name
  555. * the Variable name.
  556. * @param value
  557. * the Variable initial value.
  558. *
  559. * @throws PaintException
  560. * if the paint operation failed.
  561. */
  562. public void addVariable(VariableOwner owner, String name, double value)
  563. throws PaintException {
  564. tag.addVariable(new DoubleVariable(owner, name, value));
  565. }
  566. /**
  567. * Adds a boolean type variable.
  568. *
  569. * @param owner
  570. * the Listener for variable changes.
  571. * @param name
  572. * the Variable name.
  573. * @param value
  574. * the Variable initial value.
  575. *
  576. * @throws PaintException
  577. * if the paint operation failed.
  578. */
  579. public void addVariable(VariableOwner owner, String name, boolean value)
  580. throws PaintException {
  581. tag.addVariable(new BooleanVariable(owner, name, value));
  582. }
  583. /**
  584. * Adds a string array type variable.
  585. *
  586. * @param owner
  587. * the Listener for variable changes.
  588. * @param name
  589. * the Variable name.
  590. * @param value
  591. * the Variable initial value.
  592. *
  593. * @throws PaintException
  594. * if the paint operation failed.
  595. */
  596. public void addVariable(VariableOwner owner, String name, String[] value)
  597. throws PaintException {
  598. tag.addVariable(new ArrayVariable(owner, name, value));
  599. }
  600. /**
  601. * Adds a upload stream type variable.
  602. *
  603. * TODO not converted for JSON
  604. *
  605. * @param owner
  606. * the Listener for variable changes.
  607. * @param name
  608. * the Variable name.
  609. *
  610. * @throws PaintException
  611. * if the paint operation failed.
  612. */
  613. public void addUploadStreamVariable(VariableOwner owner, String name)
  614. throws PaintException {
  615. startTag("uploadstream");
  616. addAttribute(UIDL_ARG_NAME, name);
  617. endTag("uploadstream");
  618. }
  619. /**
  620. * Prints the single text section.
  621. *
  622. * Prints full text section. The section data is escaped
  623. *
  624. * @param sectionTagName
  625. * the name of the tag.
  626. * @param sectionData
  627. * the section data to be printed.
  628. * @throws PaintException
  629. * if the paint operation failed.
  630. */
  631. public void addSection(String sectionTagName, String sectionData)
  632. throws PaintException {
  633. tag.addData("{\"" + sectionTagName + "\":\"" + escapeJSON(sectionData)
  634. + "\"}");
  635. }
  636. /**
  637. * Adds XML directly to UIDL.
  638. *
  639. * @param xml
  640. * the Xml to be added.
  641. * @throws PaintException
  642. * if the paint operation failed.
  643. */
  644. public void addUIDL(String xml) throws PaintException {
  645. // Ensure that the target is open
  646. if (closed) {
  647. throw new PaintException(
  648. "Attempted to write to a closed PaintTarget.");
  649. }
  650. // Make sure that the open start tag is closed before
  651. // anything is written.
  652. // Escape and write what was given
  653. if (xml != null) {
  654. tag.addData("\"" + escapeJSON(xml) + "\"");
  655. }
  656. }
  657. /**
  658. * Adds XML section with namespace.
  659. *
  660. * @param sectionTagName
  661. * the name of the tag.
  662. * @param sectionData
  663. * the section data.
  664. * @param namespace
  665. * the namespace to be added.
  666. * @throws PaintException
  667. * if the paint operation failed.
  668. *
  669. * @see com.vaadin.terminal.PaintTarget#addXMLSection(String, String,
  670. * String)
  671. */
  672. public void addXMLSection(String sectionTagName, String sectionData,
  673. String namespace) throws PaintException {
  674. // Ensure that the target is open
  675. if (closed) {
  676. throw new PaintException(
  677. "Attempted to write to a closed PaintTarget.");
  678. }
  679. startTag(sectionTagName);
  680. if (namespace != null) {
  681. addAttribute("xmlns", namespace);
  682. }
  683. customLayoutArgumentsOpen = false;
  684. if (sectionData != null) {
  685. tag.addData("\"" + escapeJSON(sectionData) + "\"");
  686. }
  687. endTag(sectionTagName);
  688. }
  689. /**
  690. * Gets the UIDL already printed to stream. Paint target must be closed
  691. * before the <code>getUIDL</code> can be called.
  692. *
  693. * @return the UIDL.
  694. */
  695. public String getUIDL() {
  696. if (closed) {
  697. return uidlBuffer.toString();
  698. }
  699. throw new IllegalStateException(
  700. "Tried to read UIDL from open PaintTarget");
  701. }
  702. /**
  703. * Closes the paint target. Paint target must be closed before the
  704. * <code>getUIDL</code> can be called. Subsequent attempts to write to paint
  705. * target. If the target was already closed, call to this function is
  706. * ignored. will generate an exception.
  707. *
  708. * @throws PaintException
  709. * if the paint operation failed.
  710. */
  711. public void close() throws PaintException {
  712. if (tag != null) {
  713. uidlBuffer.write(tag.getJSON());
  714. }
  715. flush();
  716. closed = true;
  717. }
  718. /**
  719. * Method flush.
  720. */
  721. private void flush() {
  722. uidlBuffer.flush();
  723. }
  724. /*
  725. * (non-Javadoc)
  726. *
  727. * @see com.vaadin.terminal.PaintTarget#startTag(com.vaadin.terminal
  728. * .Paintable, java.lang.String)
  729. */
  730. public boolean startTag(Paintable paintable, String tagName)
  731. throws PaintException {
  732. startTag(tagName, true);
  733. final boolean isPreviouslyPainted = manager.hasPaintableId(paintable)
  734. && (identifiersCreatedDueRefPaint == null || !identifiersCreatedDueRefPaint
  735. .contains(paintable));
  736. final String id = manager.getPaintableId(paintable);
  737. paintable.addListener(manager);
  738. addAttribute("id", id);
  739. paintedComponents.add(paintable);
  740. return cacheEnabled && isPreviouslyPainted;
  741. }
  742. public void paintReference(Paintable paintable, String referenceName)
  743. throws PaintException {
  744. final String id = getPaintIdentifier(paintable);
  745. addAttribute(referenceName, id);
  746. }
  747. public String getPaintIdentifier(Paintable paintable) throws PaintException {
  748. if (!manager.hasPaintableId(paintable)) {
  749. if (identifiersCreatedDueRefPaint == null) {
  750. identifiersCreatedDueRefPaint = new HashSet<Paintable>();
  751. }
  752. identifiersCreatedDueRefPaint.add(paintable);
  753. }
  754. return manager.getPaintableId(paintable);
  755. }
  756. /*
  757. * (non-Javadoc)
  758. *
  759. * @see com.vaadin.terminal.PaintTarget#addCharacterData(java.lang.String )
  760. */
  761. public void addCharacterData(String text) throws PaintException {
  762. if (text != null) {
  763. tag.addData(text);
  764. }
  765. }
  766. /**
  767. * This is basically a container for UI components variables, that will be
  768. * added at the end of JSON object.
  769. *
  770. * @author mattitahvonen
  771. *
  772. */
  773. class JsonTag implements Serializable {
  774. boolean firstField = false;
  775. Vector variables = new Vector();
  776. Vector children = new Vector();
  777. Vector attr = new Vector();
  778. StringBuffer data = new StringBuffer();
  779. public boolean childrenArrayOpen = false;
  780. private boolean childNode = false;
  781. private boolean tagClosed = false;
  782. public JsonTag(String tagName) {
  783. data.append("[\"" + tagName + "\"");
  784. }
  785. private void closeTag() {
  786. if (!tagClosed) {
  787. data.append(attributesAsJsonObject());
  788. data.append(getData());
  789. // Writes the end (closing) tag
  790. data.append("]");
  791. tagClosed = true;
  792. }
  793. }
  794. public String getJSON() {
  795. if (!tagClosed) {
  796. closeTag();
  797. }
  798. return data.toString();
  799. }
  800. public void openChildrenArray() {
  801. if (!childrenArrayOpen) {
  802. // append("c : [");
  803. childrenArrayOpen = true;
  804. // firstField = true;
  805. }
  806. }
  807. public void closeChildrenArray() {
  808. // append("]");
  809. // firstField = false;
  810. }
  811. public void setChildNode(boolean b) {
  812. childNode = b;
  813. }
  814. public boolean isChildNode() {
  815. return childNode;
  816. }
  817. public String startField() {
  818. if (firstField) {
  819. firstField = false;
  820. return "";
  821. } else {
  822. return ",";
  823. }
  824. }
  825. /**
  826. *
  827. * @param s
  828. * json string, object or array
  829. */
  830. public void addData(String s) {
  831. children.add(s);
  832. }
  833. public String getData() {
  834. final StringBuffer buf = new StringBuffer();
  835. final Iterator it = children.iterator();
  836. while (it.hasNext()) {
  837. buf.append(startField());
  838. buf.append(it.next());
  839. }
  840. return buf.toString();
  841. }
  842. public void addAttribute(String jsonNode) {
  843. attr.add(jsonNode);
  844. }
  845. private String attributesAsJsonObject() {
  846. final StringBuffer buf = new StringBuffer();
  847. buf.append(startField());
  848. buf.append("{");
  849. for (final Iterator iter = attr.iterator(); iter.hasNext();) {
  850. final String element = (String) iter.next();
  851. buf.append(element);
  852. if (iter.hasNext()) {
  853. buf.append(",");
  854. }
  855. }
  856. buf.append(tag.variablesAsJsonObject());
  857. buf.append("}");
  858. return buf.toString();
  859. }
  860. public void addVariable(Variable v) {
  861. variables.add(v);
  862. }
  863. private String variablesAsJsonObject() {
  864. if (variables.size() == 0) {
  865. return "";
  866. }
  867. final StringBuffer buf = new StringBuffer();
  868. buf.append(startField());
  869. buf.append("\"v\":{");
  870. final Iterator iter = variables.iterator();
  871. while (iter.hasNext()) {
  872. final Variable element = (Variable) iter.next();
  873. buf.append(element.getJsonPresentation());
  874. if (iter.hasNext()) {
  875. buf.append(",");
  876. }
  877. }
  878. buf.append("}");
  879. return buf.toString();
  880. }
  881. class TagCounter {
  882. int count;
  883. public TagCounter() {
  884. count = 0;
  885. }
  886. public void increment() {
  887. count++;
  888. }
  889. public String postfix(String s) {
  890. if (count > 0) {
  891. return s + count;
  892. }
  893. return s;
  894. }
  895. }
  896. }
  897. abstract class Variable implements Serializable {
  898. String name;
  899. public abstract String getJsonPresentation();
  900. }
  901. class BooleanVariable extends Variable implements Serializable {
  902. boolean value;
  903. public BooleanVariable(VariableOwner owner, String name, boolean v) {
  904. value = v;
  905. this.name = name;
  906. }
  907. @Override
  908. public String getJsonPresentation() {
  909. return "\"" + name + "\":" + (value == true ? "true" : "false");
  910. }
  911. }
  912. class StringVariable extends Variable implements Serializable {
  913. String value;
  914. public StringVariable(VariableOwner owner, String name, String v) {
  915. value = v;
  916. this.name = name;
  917. }
  918. @Override
  919. public String getJsonPresentation() {
  920. return "\"" + name + "\":\"" + value + "\"";
  921. }
  922. }
  923. class IntVariable extends Variable implements Serializable {
  924. int value;
  925. public IntVariable(VariableOwner owner, String name, int v) {
  926. value = v;
  927. this.name = name;
  928. }
  929. @Override
  930. public String getJsonPresentation() {
  931. return "\"" + name + "\":" + value;
  932. }
  933. }
  934. class LongVariable extends Variable implements Serializable {
  935. long value;
  936. public LongVariable(VariableOwner owner, String name, long v) {
  937. value = v;
  938. this.name = name;
  939. }
  940. @Override
  941. public String getJsonPresentation() {
  942. return "\"" + name + "\":" + value;
  943. }
  944. }
  945. class FloatVariable extends Variable implements Serializable {
  946. float value;
  947. public FloatVariable(VariableOwner owner, String name, float v) {
  948. value = v;
  949. this.name = name;
  950. }
  951. @Override
  952. public String getJsonPresentation() {
  953. return "\"" + name + "\":" + value;
  954. }
  955. }
  956. class DoubleVariable extends Variable implements Serializable {
  957. double value;
  958. public DoubleVariable(VariableOwner owner, String name, double v) {
  959. value = v;
  960. this.name = name;
  961. }
  962. @Override
  963. public String getJsonPresentation() {
  964. return "\"" + name + "\":" + value;
  965. }
  966. }
  967. class ArrayVariable extends Variable implements Serializable {
  968. String[] value;
  969. public ArrayVariable(VariableOwner owner, String name, String[] v) {
  970. value = v;
  971. this.name = name;
  972. }
  973. @Override
  974. public String getJsonPresentation() {
  975. String pres = "\"" + name + "\":[";
  976. for (int i = 0; i < value.length;) {
  977. pres += "\"" + value[i] + "\"";
  978. i++;
  979. if (i < value.length) {
  980. pres += ",";
  981. }
  982. }
  983. pres += "]";
  984. return pres;
  985. }
  986. }
  987. public Set<Object> getUsedResources() {
  988. return usedResources;
  989. }
  990. /**
  991. * Method to check if paintable is already painted into this target.
  992. *
  993. * @param p
  994. * @return true if is not yet painted into this target and is connected to
  995. * app
  996. */
  997. public boolean needsToBePainted(Paintable p) {
  998. if (paintedComponents.contains(p)) {
  999. return false;
  1000. } else if (((Component) p).getApplication() == null) {
  1001. return false;
  1002. } else {
  1003. return true;
  1004. }
  1005. }
  1006. @SuppressWarnings("unchecked")
  1007. public String getTag(Paintable paintable) {
  1008. /*
  1009. * Client widget annotation is searched from component hierarchy to
  1010. * detect the component that presumably has client side implementation.
  1011. * The server side name is used in the transportation, but encoded into
  1012. * integer strings to optimized transferred data.
  1013. */
  1014. Class<? extends Paintable> class1 = paintable.getClass();
  1015. ClientWidget annotation = class1.getAnnotation(ClientWidget.class);
  1016. while (annotation == null) {
  1017. Class<?> superclass = class1.getSuperclass();
  1018. if (superclass != null
  1019. && Paintable.class.isAssignableFrom(superclass)) {
  1020. class1 = (Class<? extends Paintable>) superclass;
  1021. annotation = class1.getAnnotation(ClientWidget.class);
  1022. } else {
  1023. System.out
  1024. .append("Warning: no superclass of givent has ClientWidget"
  1025. + " annotation. Component will not be mapped correctly on client side.");
  1026. break;
  1027. }
  1028. }
  1029. usedPaintableTypes.add(class1);
  1030. return CommunicationManager.getTagForType(class1);
  1031. }
  1032. Collection<Class<? extends Paintable>> getUsedPaintableTypes() {
  1033. return usedPaintableTypes;
  1034. }
  1035. }