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.

JsonPaintTarget.java 29KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.server;
  17. import java.io.PrintWriter;
  18. import java.io.Serializable;
  19. import java.io.Writer;
  20. import java.util.ArrayDeque;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.Deque;
  24. import java.util.HashSet;
  25. import java.util.Iterator;
  26. import java.util.List;
  27. import java.util.Locale;
  28. import java.util.Map;
  29. import java.util.Set;
  30. import java.util.logging.Level;
  31. import java.util.logging.Logger;
  32. import com.vaadin.ui.Alignment;
  33. import com.vaadin.ui.Component;
  34. import com.vaadin.ui.CustomLayout;
  35. /**
  36. * User Interface Description Language Target.
  37. *
  38. * TODO document better: role of this class, UIDL format, attributes, variables,
  39. * etc.
  40. *
  41. * @author Vaadin Ltd.
  42. * @since 5.0
  43. */
  44. @SuppressWarnings("serial")
  45. public class JsonPaintTarget implements PaintTarget {
  46. /* Document type declarations */
  47. private static final String UIDL_ARG_NAME = "name";
  48. private final Deque<String> mOpenTags;
  49. private final Deque<JsonTag> openJsonTags;
  50. // these match each other element-wise
  51. private final Deque<ClientConnector> openPaintables;
  52. private final Deque<String> openPaintableTags;
  53. private final PrintWriter uidlBuffer;
  54. private boolean closed = false;
  55. private final LegacyCommunicationManager manager;
  56. private int changes = 0;
  57. private final Set<Object> usedResources = new HashSet<>();
  58. private boolean customLayoutArgumentsOpen = false;
  59. private JsonTag tag;
  60. private boolean cacheEnabled = false;
  61. private final Set<Class<? extends ClientConnector>> usedClientConnectors = new HashSet<>();
  62. /**
  63. * Creates a new JsonPaintTarget.
  64. *
  65. * @param manager
  66. * @param outWriter
  67. * A character-output stream.
  68. * @param cachingRequired
  69. * true if this is not a full repaint, i.e. caches are to be
  70. * used.
  71. * @throws PaintException
  72. * if the paint operation failed.
  73. */
  74. public JsonPaintTarget(LegacyCommunicationManager manager, Writer outWriter,
  75. boolean cachingRequired) throws PaintException {
  76. this.manager = manager;
  77. // Sets the target for UIDL writing
  78. uidlBuffer = new PrintWriter(outWriter);
  79. // Initialize tag-writing
  80. mOpenTags = new ArrayDeque<>();
  81. openJsonTags = new ArrayDeque<>();
  82. openPaintables = new ArrayDeque<>();
  83. openPaintableTags = new ArrayDeque<>();
  84. cacheEnabled = cachingRequired;
  85. }
  86. @Override
  87. public void startTag(String tagName) throws PaintException {
  88. startTag(tagName, false);
  89. }
  90. /**
  91. * Prints the element start tag.
  92. *
  93. * <pre>
  94. * Todo:
  95. * Checking of input values
  96. *
  97. * </pre>
  98. *
  99. * @param tagName
  100. * the name of the start tag.
  101. * @throws PaintException
  102. * if the paint operation failed.
  103. *
  104. */
  105. public void startTag(String tagName, boolean isChildNode)
  106. throws PaintException {
  107. // In case of null data output nothing:
  108. if (tagName == null) {
  109. throw new NullPointerException();
  110. }
  111. // Ensures that the target is open
  112. if (closed) {
  113. throw new PaintException(
  114. "Attempted to write to a closed PaintTarget.");
  115. }
  116. if (tag != null) {
  117. openJsonTags.push(tag);
  118. }
  119. // Checks tagName and attributes here
  120. mOpenTags.push(tagName);
  121. tag = new JsonTag(tagName);
  122. customLayoutArgumentsOpen = false;
  123. }
  124. /**
  125. * Prints the element end tag.
  126. *
  127. * If the parent tag is closed before every child tag is closed an
  128. * PaintException is raised.
  129. *
  130. * @param tagName
  131. * the name of the end tag.
  132. * @throws PaintException
  133. * if the paint operation failed.
  134. */
  135. @Override
  136. public void endTag(String tagName) throws PaintException {
  137. // In case of null data output nothing:
  138. if (tagName == null) {
  139. throw new NullPointerException();
  140. }
  141. // Ensure that the target is open
  142. if (closed) {
  143. throw new PaintException(
  144. "Attempted to write to a closed PaintTarget.");
  145. }
  146. if (!openJsonTags.isEmpty()) {
  147. final JsonTag parent = openJsonTags.pop();
  148. String lastTag = "";
  149. lastTag = mOpenTags.pop();
  150. if (!tagName.equalsIgnoreCase(lastTag)) {
  151. throw new PaintException("Invalid UIDL: wrong ending tag: '"
  152. + tagName + "' expected: '" + lastTag + "'.");
  153. }
  154. parent.addData(tag.getJSON());
  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. public static String escapeXML(String xml) {
  171. if (xml == null || xml.length() <= 0) {
  172. return "";
  173. }
  174. return escapeXML(new StringBuilder(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 StringBuilder instance where all occurrences of XML
  182. * sensitive characters are substituted with entities.
  183. *
  184. */
  185. static StringBuilder escapeXML(StringBuilder xml) {
  186. if (xml == null || xml.length() <= 0) {
  187. return new StringBuilder();
  188. }
  189. final StringBuilder result = new StringBuilder(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. /**
  202. * Escapes the given string so it can safely be used as a JSON string.
  203. *
  204. * @param s
  205. * The string to escape
  206. * @return Escaped version of the string
  207. */
  208. public static String escapeJSON(String s) {
  209. // FIXME: Move this method to another class as other classes use it
  210. // also.
  211. if (s == null) {
  212. return "";
  213. }
  214. final StringBuilder sb = new StringBuilder();
  215. for (int i = 0; i < s.length(); i++) {
  216. final char ch = s.charAt(i);
  217. switch (ch) {
  218. case '"':
  219. sb.append("\\\"");
  220. break;
  221. case '\\':
  222. sb.append("\\\\");
  223. break;
  224. case '\b':
  225. sb.append("\\b");
  226. break;
  227. case '\f':
  228. sb.append("\\f");
  229. break;
  230. case '\n':
  231. sb.append("\\n");
  232. break;
  233. case '\r':
  234. sb.append("\\r");
  235. break;
  236. case '\t':
  237. sb.append("\\t");
  238. break;
  239. case '/':
  240. sb.append("\\/");
  241. break;
  242. default:
  243. if (ch >= '\u0000' && ch <= '\u001F') {
  244. final String ss = Integer.toHexString(ch);
  245. sb.append("\\u");
  246. for (int k = 0; k < 4 - ss.length(); k++) {
  247. sb.append('0');
  248. }
  249. sb.append(ss.toUpperCase(Locale.ROOT));
  250. } else {
  251. sb.append(ch);
  252. }
  253. }
  254. }
  255. return sb.toString();
  256. }
  257. /**
  258. * Substitutes a XML sensitive character with predefined XML entity.
  259. *
  260. * @param c
  261. * the Character to be replaced with an entity.
  262. * @return String of the entity or null if character is not to be replaced
  263. * with an entity.
  264. */
  265. private static String toXmlChar(char c) {
  266. switch (c) {
  267. case '&':
  268. return "&amp;"; // & => &amp;
  269. case '>':
  270. return "&gt;"; // > => &gt;
  271. case '<':
  272. return "&lt;"; // < => &lt;
  273. case '"':
  274. return "&quot;"; // " => &quot;
  275. case '\'':
  276. return "&apos;"; // ' => &apos;
  277. default:
  278. return null;
  279. }
  280. }
  281. /**
  282. * Prints XML-escaped text.
  283. *
  284. * @param str
  285. * @throws PaintException
  286. * if the paint operation failed.
  287. *
  288. */
  289. @Override
  290. public void addText(String str) throws PaintException {
  291. tag.addData("\"" + escapeJSON(str) + "\"");
  292. }
  293. @Override
  294. public void addAttribute(String name, boolean value) throws PaintException {
  295. tag.addAttribute("\"" + name + "\":" + (value ? "true" : "false"));
  296. }
  297. @Override
  298. public void addAttribute(String name, Resource value)
  299. throws PaintException {
  300. if (value == null) {
  301. throw new NullPointerException();
  302. }
  303. ClientConnector ownerConnector = openPaintables.peek();
  304. ownerConnector.getUI().getSession().getGlobalResourceHandler(true)
  305. .register(value, ownerConnector);
  306. ResourceReference reference = ResourceReference.create(value,
  307. ownerConnector, name);
  308. addAttribute(name, reference.getURL());
  309. }
  310. @Override
  311. public void addAttribute(String name, int value) throws PaintException {
  312. tag.addAttribute("\"" + name + "\":" + String.valueOf(value));
  313. }
  314. @Override
  315. public void addAttribute(String name, long value) throws PaintException {
  316. tag.addAttribute("\"" + name + "\":" + String.valueOf(value));
  317. }
  318. @Override
  319. public void addAttribute(String name, float value) throws PaintException {
  320. tag.addAttribute("\"" + name + "\":" + String.valueOf(value));
  321. }
  322. @Override
  323. public void addAttribute(String name, double value) throws PaintException {
  324. tag.addAttribute("\"" + name + "\":" + String.valueOf(value));
  325. }
  326. @Override
  327. public void addAttribute(String name, String value) throws PaintException {
  328. // In case of null data output nothing:
  329. if ((value == null) || (name == null)) {
  330. throw new NullPointerException(
  331. "Parameters must be non-null strings");
  332. }
  333. tag.addAttribute("\"" + name + "\":\"" + escapeJSON(value) + "\"");
  334. if (customLayoutArgumentsOpen && "template".equals(name)) {
  335. getUsedResources().add("layouts/" + value + ".html");
  336. }
  337. }
  338. @Override
  339. public void addAttribute(String name, Component value)
  340. throws PaintException {
  341. final String id = value.getConnectorId();
  342. addAttribute(name, id);
  343. }
  344. @Override
  345. public void addAttribute(String name, Map<?, ?> value)
  346. throws PaintException {
  347. StringBuilder sb = new StringBuilder();
  348. sb.append("\"");
  349. sb.append(name);
  350. sb.append("\":");
  351. sb.append('{');
  352. for (Iterator<?> it = value.keySet().iterator(); it.hasNext();) {
  353. Object key = it.next();
  354. Object mapValue = value.get(key);
  355. sb.append("\"");
  356. if (key instanceof ClientConnector) {
  357. sb.append(((ClientConnector) key).getConnectorId());
  358. } else {
  359. sb.append(escapeJSON(key.toString()));
  360. }
  361. sb.append("\":");
  362. if (mapValue instanceof Float || mapValue instanceof Integer
  363. || mapValue instanceof Double || mapValue instanceof Boolean
  364. || mapValue instanceof Alignment) {
  365. sb.append(mapValue);
  366. } else {
  367. sb.append("\"");
  368. sb.append(escapeJSON(mapValue.toString()));
  369. sb.append("\"");
  370. }
  371. if (it.hasNext()) {
  372. sb.append(',');
  373. }
  374. }
  375. sb.append('}');
  376. tag.addAttribute(sb.toString());
  377. }
  378. @Override
  379. public void addAttribute(String name, Object[] values) {
  380. // In case of null data output nothing:
  381. if ((values == null) || (name == null)) {
  382. throw new NullPointerException(
  383. "Parameters must be non-null strings");
  384. }
  385. final StringBuilder buf = new StringBuilder();
  386. buf.append("\"").append(name).append("\":[");
  387. for (int i = 0; i < values.length; i++) {
  388. if (i > 0) {
  389. buf.append(',');
  390. }
  391. buf.append("\"");
  392. buf.append(escapeJSON(values[i].toString()));
  393. buf.append("\"");
  394. }
  395. buf.append(']');
  396. tag.addAttribute(buf.toString());
  397. }
  398. @Override
  399. public void addVariable(VariableOwner owner, String name, String value)
  400. throws PaintException {
  401. tag.addVariable(new StringVariable(owner, name, escapeJSON(value)));
  402. }
  403. @Override
  404. public void addVariable(VariableOwner owner, String name, Component value)
  405. throws PaintException {
  406. tag.addVariable(
  407. new StringVariable(owner, name, value.getConnectorId()));
  408. }
  409. @Override
  410. public void addVariable(VariableOwner owner, String name, int value)
  411. throws PaintException {
  412. tag.addVariable(new IntVariable(owner, name, value));
  413. }
  414. @Override
  415. public void addVariable(VariableOwner owner, String name, long value)
  416. throws PaintException {
  417. tag.addVariable(new LongVariable(owner, name, value));
  418. }
  419. @Override
  420. public void addVariable(VariableOwner owner, String name, float value)
  421. throws PaintException {
  422. tag.addVariable(new FloatVariable(owner, name, value));
  423. }
  424. @Override
  425. public void addVariable(VariableOwner owner, String name, double value)
  426. throws PaintException {
  427. tag.addVariable(new DoubleVariable(owner, name, value));
  428. }
  429. @Override
  430. public void addVariable(VariableOwner owner, String name, boolean value)
  431. throws PaintException {
  432. tag.addVariable(new BooleanVariable(owner, name, value));
  433. }
  434. @Override
  435. public void addVariable(VariableOwner owner, String name, String[] value)
  436. throws PaintException {
  437. tag.addVariable(new ArrayVariable(owner, name, value));
  438. }
  439. /**
  440. * Adds a upload stream type variable.
  441. *
  442. * TODO not converted for JSON
  443. *
  444. * @param owner
  445. * the Listener for variable changes.
  446. * @param name
  447. * the Variable name.
  448. *
  449. * @throws PaintException
  450. * if the paint operation failed.
  451. */
  452. @Override
  453. public void addUploadStreamVariable(VariableOwner owner, String name)
  454. throws PaintException {
  455. startTag("uploadstream");
  456. addAttribute(UIDL_ARG_NAME, name);
  457. endTag("uploadstream");
  458. }
  459. /**
  460. * Prints the single text section.
  461. *
  462. * Prints full text section. The section data is escaped
  463. *
  464. * @param sectionTagName
  465. * the name of the tag.
  466. * @param sectionData
  467. * the section data to be printed.
  468. * @throws PaintException
  469. * if the paint operation failed.
  470. */
  471. @Override
  472. public void addSection(String sectionTagName, String sectionData)
  473. throws PaintException {
  474. tag.addData("{\"" + sectionTagName + "\":\"" + escapeJSON(sectionData)
  475. + "\"}");
  476. }
  477. /**
  478. * Adds XML directly to UIDL.
  479. *
  480. * @param xml
  481. * the Xml to be added.
  482. * @throws PaintException
  483. * if the paint operation failed.
  484. */
  485. @Override
  486. public void addUIDL(String xml) throws PaintException {
  487. // Ensure that the target is open
  488. if (closed) {
  489. throw new PaintException(
  490. "Attempted to write to a closed PaintTarget.");
  491. }
  492. // Make sure that the open start tag is closed before
  493. // anything is written.
  494. // Escape and write what was given
  495. if (xml != null) {
  496. tag.addData("\"" + escapeJSON(xml) + "\"");
  497. }
  498. }
  499. /**
  500. * Adds XML section with namespace.
  501. *
  502. * @param sectionTagName
  503. * the name of the tag.
  504. * @param sectionData
  505. * the section data.
  506. * @param namespace
  507. * the namespace to be added.
  508. * @throws PaintException
  509. * if the paint operation failed.
  510. *
  511. * @see com.vaadin.server.PaintTarget#addXMLSection(String, String, String)
  512. */
  513. @Override
  514. public void addXMLSection(String sectionTagName, String sectionData,
  515. String namespace) throws PaintException {
  516. // Ensure that the target is open
  517. if (closed) {
  518. throw new PaintException(
  519. "Attempted to write to a closed PaintTarget.");
  520. }
  521. startTag(sectionTagName);
  522. if (namespace != null) {
  523. addAttribute("xmlns", namespace);
  524. }
  525. if (sectionData != null) {
  526. tag.addData("\"" + escapeJSON(sectionData) + "\"");
  527. }
  528. endTag(sectionTagName);
  529. }
  530. /**
  531. * Gets the UIDL already printed to stream. Paint target must be closed
  532. * before the <code>getUIDL</code> can be called.
  533. *
  534. * @return the UIDL.
  535. */
  536. public String getUIDL() {
  537. if (closed) {
  538. return uidlBuffer.toString();
  539. }
  540. throw new IllegalStateException(
  541. "Tried to read UIDL from open PaintTarget");
  542. }
  543. /**
  544. * Closes the paint target. Paint target must be closed before the
  545. * <code>getUIDL</code> can be called. Subsequent attempts to write to paint
  546. * target. If the target was already closed, call to this function is
  547. * ignored. will generate an exception.
  548. *
  549. * @throws PaintException
  550. * if the paint operation failed.
  551. */
  552. public void close() throws PaintException {
  553. if (tag != null) {
  554. uidlBuffer.write(tag.getJSON());
  555. }
  556. flush();
  557. closed = true;
  558. }
  559. /**
  560. * Method flush.
  561. */
  562. private void flush() {
  563. uidlBuffer.flush();
  564. }
  565. /*
  566. * (non-Javadoc)
  567. *
  568. * @see com.vaadin.terminal.PaintTarget#startPaintable(com.vaadin.terminal
  569. * .Paintable, java.lang.String)
  570. */
  571. @Override
  572. public PaintStatus startPaintable(Component connector, String tagName)
  573. throws PaintException {
  574. boolean topLevelPaintable = openPaintables.isEmpty();
  575. if (getLogger().isLoggable(Level.FINE)) {
  576. getLogger().log(Level.FINE, "startPaintable for {0}@{1}",
  577. new Object[] { connector.getClass().getName(),
  578. Integer.toHexString(connector.hashCode()) });
  579. }
  580. startTag(tagName, true);
  581. openPaintables.push(connector);
  582. openPaintableTags.push(tagName);
  583. addAttribute("id", connector.getConnectorId());
  584. // Only paint top level paintables. All sub paintables are marked as
  585. // queued and painted separately later.
  586. if (!topLevelPaintable) {
  587. return PaintStatus.CACHED;
  588. }
  589. if (connector instanceof CustomLayout) {
  590. customLayoutArgumentsOpen = true;
  591. }
  592. return PaintStatus.PAINTING;
  593. }
  594. @Override
  595. public void endPaintable(Component paintable) throws PaintException {
  596. if (getLogger().isLoggable(Level.FINE)) {
  597. getLogger().log(Level.FINE, "endPaintable for {0}@{1}",
  598. new Object[] { paintable.getClass().getName(),
  599. Integer.toHexString(paintable.hashCode()) });
  600. }
  601. ClientConnector openPaintable = openPaintables.peek();
  602. if (paintable != openPaintable) {
  603. throw new PaintException("Invalid UIDL: closing wrong paintable: '"
  604. + paintable.getConnectorId() + "' expected: '"
  605. + openPaintable.getConnectorId() + "'.");
  606. }
  607. // remove paintable from the stack
  608. openPaintables.pop();
  609. String openTag = openPaintableTags.pop();
  610. endTag(openTag);
  611. }
  612. /*
  613. * (non-Javadoc)
  614. *
  615. * @see com.vaadin.terminal.PaintTarget#addCharacterData(java.lang.String )
  616. */
  617. @Override
  618. public void addCharacterData(String text) throws PaintException {
  619. if (text != null) {
  620. tag.addData(text);
  621. }
  622. }
  623. /**
  624. * This is basically a container for UI components variables, that will be
  625. * added at the end of JSON object.
  626. *
  627. * @author mattitahvonen
  628. *
  629. */
  630. class JsonTag implements Serializable {
  631. boolean firstField = false;
  632. List<Object> variables = new ArrayList<>();
  633. List<Object> children = new ArrayList<>();
  634. List<Object> attr = new ArrayList<>();
  635. StringBuilder data = new StringBuilder();
  636. public boolean childrenArrayOpen = false;
  637. private boolean childNode = false;
  638. private boolean tagClosed = false;
  639. public JsonTag(String tagName) {
  640. data.append("[\"").append(tagName).append("\"");
  641. }
  642. private void closeTag() {
  643. if (!tagClosed) {
  644. data.append(attributesAsJsonObject());
  645. data.append(getData());
  646. // Writes the end (closing) tag
  647. data.append(']');
  648. tagClosed = true;
  649. }
  650. }
  651. public String getJSON() {
  652. if (!tagClosed) {
  653. closeTag();
  654. }
  655. return data.toString();
  656. }
  657. public void openChildrenArray() {
  658. if (!childrenArrayOpen) {
  659. // append("c : [");
  660. childrenArrayOpen = true;
  661. // firstField = true;
  662. }
  663. }
  664. public void closeChildrenArray() {
  665. // append(']');
  666. // firstField = false;
  667. }
  668. public void setChildNode(boolean b) {
  669. childNode = b;
  670. }
  671. public boolean isChildNode() {
  672. return childNode;
  673. }
  674. public String startField() {
  675. if (firstField) {
  676. firstField = false;
  677. return "";
  678. } else {
  679. return ",";
  680. }
  681. }
  682. /**
  683. *
  684. * @param s
  685. * json string, object or array
  686. */
  687. public void addData(String s) {
  688. children.add(s);
  689. }
  690. public String getData() {
  691. final StringBuilder buf = new StringBuilder();
  692. final Iterator<Object> it = children.iterator();
  693. while (it.hasNext()) {
  694. buf.append(startField());
  695. buf.append(it.next());
  696. }
  697. return buf.toString();
  698. }
  699. public void addAttribute(String jsonNode) {
  700. attr.add(jsonNode);
  701. }
  702. private String attributesAsJsonObject() {
  703. final StringBuilder buf = new StringBuilder();
  704. buf.append(startField());
  705. buf.append('{');
  706. for (final Iterator<Object> iter = attr.iterator(); iter
  707. .hasNext();) {
  708. final String element = (String) iter.next();
  709. buf.append(element);
  710. if (iter.hasNext()) {
  711. buf.append(',');
  712. }
  713. }
  714. buf.append(tag.variablesAsJsonObject());
  715. buf.append('}');
  716. return buf.toString();
  717. }
  718. public void addVariable(Variable v) {
  719. variables.add(v);
  720. }
  721. private String variablesAsJsonObject() {
  722. if (variables.isEmpty()) {
  723. return "";
  724. }
  725. final StringBuilder buf = new StringBuilder();
  726. buf.append(startField());
  727. buf.append("\"v\":{");
  728. final Iterator<Object> iter = variables.iterator();
  729. while (iter.hasNext()) {
  730. final Variable element = (Variable) iter.next();
  731. buf.append(element.getJsonPresentation());
  732. if (iter.hasNext()) {
  733. buf.append(',');
  734. }
  735. }
  736. buf.append('}');
  737. return buf.toString();
  738. }
  739. }
  740. abstract class Variable implements Serializable {
  741. String name;
  742. public abstract String getJsonPresentation();
  743. }
  744. class BooleanVariable extends Variable {
  745. boolean value;
  746. public BooleanVariable(VariableOwner owner, String name, boolean v) {
  747. value = v;
  748. this.name = name;
  749. }
  750. @Override
  751. public String getJsonPresentation() {
  752. return "\"" + name + "\":" + (value ? "true" : "false");
  753. }
  754. }
  755. class StringVariable extends Variable {
  756. String value;
  757. public StringVariable(VariableOwner owner, String name, String v) {
  758. value = v;
  759. this.name = name;
  760. }
  761. @Override
  762. public String getJsonPresentation() {
  763. return "\"" + name + "\":\"" + value + "\"";
  764. }
  765. }
  766. class IntVariable extends Variable {
  767. int value;
  768. public IntVariable(VariableOwner owner, String name, int v) {
  769. value = v;
  770. this.name = name;
  771. }
  772. @Override
  773. public String getJsonPresentation() {
  774. return "\"" + name + "\":" + value;
  775. }
  776. }
  777. class LongVariable extends Variable {
  778. long value;
  779. public LongVariable(VariableOwner owner, String name, long v) {
  780. value = v;
  781. this.name = name;
  782. }
  783. @Override
  784. public String getJsonPresentation() {
  785. return "\"" + name + "\":" + value;
  786. }
  787. }
  788. class FloatVariable extends Variable {
  789. float value;
  790. public FloatVariable(VariableOwner owner, String name, float v) {
  791. value = v;
  792. this.name = name;
  793. }
  794. @Override
  795. public String getJsonPresentation() {
  796. return "\"" + name + "\":" + value;
  797. }
  798. }
  799. class DoubleVariable extends Variable {
  800. double value;
  801. public DoubleVariable(VariableOwner owner, String name, double v) {
  802. value = v;
  803. this.name = name;
  804. }
  805. @Override
  806. public String getJsonPresentation() {
  807. return "\"" + name + "\":" + value;
  808. }
  809. }
  810. class ArrayVariable extends Variable {
  811. String[] value;
  812. public ArrayVariable(VariableOwner owner, String name, String[] v) {
  813. value = v;
  814. this.name = name;
  815. }
  816. @Override
  817. public String getJsonPresentation() {
  818. StringBuilder sb = new StringBuilder();
  819. sb.append("\"");
  820. sb.append(name);
  821. sb.append("\":[");
  822. for (int i = 0; i < value.length;) {
  823. sb.append("\"");
  824. sb.append(escapeJSON(value[i]));
  825. sb.append("\"");
  826. i++;
  827. if (i < value.length) {
  828. sb.append(',');
  829. }
  830. }
  831. sb.append(']');
  832. return sb.toString();
  833. }
  834. }
  835. public Set<Object> getUsedResources() {
  836. return usedResources;
  837. }
  838. @Override
  839. @SuppressWarnings("unchecked")
  840. public String getTag(ClientConnector clientConnector) {
  841. Class<? extends ClientConnector> clientConnectorClass = clientConnector
  842. .getClass();
  843. while (clientConnectorClass.isAnonymousClass()) {
  844. clientConnectorClass = (Class<? extends ClientConnector>) clientConnectorClass
  845. .getSuperclass();
  846. }
  847. Class<?> clazz = clientConnectorClass;
  848. while (!usedClientConnectors.contains(clazz)
  849. && clazz.getSuperclass() != null
  850. && ClientConnector.class.isAssignableFrom(clazz)) {
  851. usedClientConnectors.add((Class<? extends ClientConnector>) clazz);
  852. clazz = clazz.getSuperclass();
  853. }
  854. return manager.getTagForType(clientConnectorClass);
  855. }
  856. public Collection<Class<? extends ClientConnector>> getUsedClientConnectors() {
  857. return usedClientConnectors;
  858. }
  859. @Override
  860. public void addVariable(VariableOwner owner, String name,
  861. StreamVariable value) throws PaintException {
  862. String url = manager.getStreamVariableTargetUrl((ClientConnector) owner,
  863. name, value);
  864. if (url != null) {
  865. addVariable(owner, name, url);
  866. } // else { //NOP this was just a cleanup by component }
  867. }
  868. /*
  869. * (non-Javadoc)
  870. *
  871. * @see com.vaadin.terminal.PaintTarget#isFullRepaint()
  872. */
  873. @Override
  874. public boolean isFullRepaint() {
  875. return !cacheEnabled;
  876. }
  877. private static final Logger getLogger() {
  878. return Logger.getLogger(JsonPaintTarget.class.getName());
  879. }
  880. }