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.

WebPaintTarget.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. /* *************************************************************************
  2. IT Mill Toolkit
  3. Development of Browser User Intarfaces Made Easy
  4. Copyright (C) 2000-2006 IT Mill Ltd
  5. *************************************************************************
  6. This product is distributed under commercial license that can be found
  7. from the product package on license/license.txt. Use of this product might
  8. require purchasing a commercial license from IT Mill Ltd. For guidelines
  9. on usage, see license/licensing-guidelines.html
  10. *************************************************************************
  11. For more information, contact:
  12. IT Mill Ltd phone: +358 2 4802 7180
  13. Ruukinkatu 2-4 fax: +358 2 4802 7181
  14. 20540, Turku email: info@itmill.com
  15. Finland company www: www.itmill.com
  16. Primary source for information and releases: www.itmill.com
  17. ********************************************************************** */
  18. package com.itmill.toolkit.terminal.web;
  19. import com.itmill.toolkit.Application;
  20. import com.itmill.toolkit.terminal.ApplicationResource;
  21. import com.itmill.toolkit.terminal.ExternalResource;
  22. import com.itmill.toolkit.terminal.PaintException;
  23. import com.itmill.toolkit.terminal.PaintTarget;
  24. import com.itmill.toolkit.terminal.Paintable;
  25. import com.itmill.toolkit.terminal.Resource;
  26. import com.itmill.toolkit.terminal.ThemeResource;
  27. import com.itmill.toolkit.terminal.UploadStream;
  28. import com.itmill.toolkit.terminal.VariableOwner;
  29. import java.util.Stack;
  30. /** User Interface Description Language Target.
  31. * @author IT Mill Ltd.
  32. * @version @VERSION@
  33. * @since 3.0
  34. */
  35. public class WebPaintTarget implements PaintTarget {
  36. /* Document type declarations */
  37. private final static String UIDL_XML_DECL =
  38. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
  39. /* commonly used tags and argument names */
  40. private final static String UIDL_ARG_NAME = "name";
  41. private final static String UIDL_ARG_VALUE = "value";
  42. private final static String UIDL_ARG_ID = "id";
  43. private Stack mOpenTags;
  44. private boolean mTagArgumentListOpen;
  45. private StringBuffer uidlBuffer;
  46. private StringBuffer tagBuffer;
  47. private HttpVariableMap variableMap;
  48. private boolean closed = false;
  49. private ApplicationServlet webAdapterServlet;
  50. private Theme theme;
  51. private static final int TAG_BUFFER_DEFAULT_SIZE = 20;
  52. private boolean mSuppressOutput = false;
  53. /** Create a new XMLPrintWriter, without automatic line flushing.
  54. *
  55. *
  56. * @param out A character-output stream.
  57. */
  58. public WebPaintTarget(
  59. HttpVariableMap variableMap,
  60. UIDLTransformerType type,
  61. ApplicationServlet webAdapterServlet,
  62. Theme theme)
  63. throws PaintException {
  64. // Host servlet
  65. this.webAdapterServlet = webAdapterServlet;
  66. // Target theme
  67. this.theme = theme;
  68. // Set the variable map
  69. this.variableMap = variableMap;
  70. // Set the target for UIDL writing
  71. this.uidlBuffer = new StringBuffer();
  72. // Set the target for TAG data
  73. this.tagBuffer = new StringBuffer();
  74. // Initialize tag-writing
  75. mOpenTags = new Stack();
  76. mTagArgumentListOpen = false;
  77. //Add document declaration
  78. this.print(UIDL_XML_DECL + "\n\n");
  79. // Add UIDL start tag and its attributes
  80. this.startTag("uidl");
  81. // Name of the active theme
  82. this.addAttribute("theme", type.getTheme().getName());
  83. }
  84. /** Ensures that the currently open element tag is closed.
  85. */
  86. private void ensureClosedTag() {
  87. if (mTagArgumentListOpen) {
  88. tagBuffer.append(">");
  89. mTagArgumentListOpen = false;
  90. append(tagBuffer);
  91. }
  92. }
  93. /** Print element start tag.
  94. *
  95. * <pre>Todo:
  96. * Checking of input values
  97. * </pre>
  98. *
  99. * @param tagName The name of the start tag
  100. *
  101. */
  102. public void startTag(String tagName) throws PaintException {
  103. // In case of null data output nothing:
  104. if (tagName == null)
  105. throw new NullPointerException();
  106. //Ensure that the target is open
  107. if (this.closed)
  108. throw new PaintException("Attempted to write to a closed PaintTarget.");
  109. // Make sure that the open start tag is closed before
  110. // anything is written.
  111. ensureClosedTag();
  112. // Check tagName and attributes here
  113. mOpenTags.push(tagName);
  114. tagBuffer = new StringBuffer(TAG_BUFFER_DEFAULT_SIZE);
  115. // Print the tag with attributes
  116. tagBuffer.append("<" + tagName);
  117. mTagArgumentListOpen = true;
  118. }
  119. /** Print element end tag.
  120. *
  121. * If the parent tag is closed before
  122. * every child tag is closed a PaintException is raised.
  123. *
  124. * @param tag The name of the end tag
  125. */
  126. public void endTag(String tagName) throws PaintException {
  127. // In case of null data output nothing:
  128. if (tagName == null)
  129. throw new NullPointerException();
  130. //Ensure that the target is open
  131. if (this.closed)
  132. throw new PaintException("Attempted to write to a closed PaintTarget.");
  133. String lastTag = "";
  134. lastTag = (String) mOpenTags.pop();
  135. if (!tagName.equalsIgnoreCase(lastTag))
  136. throw new PaintException(
  137. "Invalid UIDL: wrong ending tag: '"
  138. + tagName
  139. + "' expected: '"
  140. + lastTag
  141. + "'.");
  142. // Make sure that the open start tag is closed before
  143. // anything is written.
  144. ensureClosedTag();
  145. //Write the end (closing) tag
  146. append("</" + lastTag + "\n>");
  147. // NOTE: We re-enable the output (if it has been disabled)
  148. // for subsequent tags. The output is suppressed if tag
  149. // contains attribute "invisible" with value true.
  150. mSuppressOutput = false;
  151. }
  152. /** Append data into UIDL output buffer.
  153. *
  154. * @param data String to be appended.
  155. */
  156. private void append(String data) {
  157. if (!mSuppressOutput) {
  158. uidlBuffer.append(data);
  159. }
  160. }
  161. /** Append data into UIDL output buffer.
  162. *
  163. * @param data StringBuffer to be appended.
  164. */
  165. private void append(StringBuffer data) {
  166. if (!mSuppressOutput) {
  167. uidlBuffer.append(data);
  168. }
  169. }
  170. /** Substitute the XML sensitive characters with predefined XML entities.
  171. *
  172. * @return A new string instance where all occurrences of XML sensitive
  173. * characters are substituted with entities.
  174. */
  175. static public String escapeXML(String xml) {
  176. if (xml == null || xml.length() <= 0)
  177. return "";
  178. return escapeXML(new StringBuffer(xml)).toString();
  179. }
  180. /** Substitute the XML sensitive characters with predefined XML entities.
  181. * @param xml the String to be substituted
  182. * @return A new StringBuffer instance where all occurrences of XML
  183. * sensitive characters are substituted with entities.
  184. *
  185. */
  186. static public StringBuffer escapeXML(StringBuffer xml) {
  187. if (xml == null || xml.length() <= 0)
  188. return new StringBuffer("");
  189. StringBuffer result = new StringBuffer(xml.length()*2);
  190. for (int i = 0; i < xml.length(); i++) {
  191. char c = xml.charAt(i);
  192. 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. /** Substitute a XML sensitive character with predefined XML entity.
  202. * @param c Character to be replaced with an entity.
  203. * @return String of the entity or null if character is not to be replaced
  204. * with an entity.
  205. */
  206. private static String toXmlChar(char c) {
  207. switch (c) {
  208. case '&' :
  209. return "&amp;"; // & => &amp;
  210. case '>' :
  211. return "&gt;"; // > => &gt;
  212. case '<' :
  213. return "&lt;"; // < => &lt;
  214. case '"' :
  215. return "&quot;"; // " => &quot;
  216. case '\'' :
  217. return "&apos;"; // ' => &apos;
  218. default :
  219. return null;
  220. }
  221. }
  222. /** Print XML.
  223. *
  224. * Writes pre-formatted XML to stream. Well-formness of XML is checked.
  225. * <pre>
  226. * TODO: XML checking should be made
  227. * </pre>
  228. */
  229. private void print(String str) {
  230. // In case of null data output nothing:
  231. if (str == null)
  232. return;
  233. // Make sure that the open start tag is closed before
  234. // anything is written.
  235. ensureClosedTag();
  236. // Write what was given
  237. append(str);
  238. }
  239. /** Print XML-escaped text.
  240. *
  241. */
  242. public void addText(String str) throws PaintException {
  243. addUIDL(escapeXML(str));
  244. }
  245. /** Adds a boolean attribute to component.
  246. * Atributes must be added before any content is written.
  247. *
  248. * @param name Attribute name
  249. * @param value Attribute value
  250. */
  251. public void addAttribute(String name, boolean value)
  252. throws PaintException {
  253. if ("invisible".equals(name) && value) {
  254. // NOTE: If we receive the "invisible attribute
  255. // we filter these tags (and ceontent) from
  256. // them out from the output.
  257. this.mSuppressOutput = true;
  258. } else {
  259. addAttribute(name, String.valueOf(value));
  260. }
  261. }
  262. /** Adds a resource attribute to component.
  263. * Atributes must be added before any content is written.
  264. *
  265. * @param name Attribute name
  266. * @param value Attribute value
  267. */
  268. public void addAttribute(String name, Resource value)
  269. throws PaintException {
  270. if (value instanceof ExternalResource) {
  271. addAttribute(name, ((ExternalResource) value).getURL());
  272. } else if (value instanceof ApplicationResource) {
  273. ApplicationResource r = (ApplicationResource) value;
  274. Application a = r.getApplication();
  275. if (a == null)
  276. throw new PaintException(
  277. "Application not specified for resorce "
  278. + value.getClass().getName());
  279. String uri = a.getURL().getPath();
  280. if (uri.charAt(uri.length() - 1) != '/')
  281. uri += "/";
  282. uri += a.getRelativeLocation(r);
  283. addAttribute(name, uri);
  284. } else if (value instanceof ThemeResource) {
  285. addAttribute(
  286. name,
  287. webAdapterServlet.getResourceLocation(
  288. theme.getName(),
  289. (ThemeResource) value));
  290. } else
  291. throw new PaintException(
  292. "Web adapter does not "
  293. + "support resources of type: "
  294. + value.getClass().getName());
  295. }
  296. /** Adds a integer attribute to component.
  297. * Atributes must be added before any content is written.
  298. *
  299. * @param name Attribute name
  300. * @param value Attribute value
  301. * @return this object
  302. */
  303. public void addAttribute(String name, int value) throws PaintException {
  304. addAttribute(name, String.valueOf(value));
  305. }
  306. /** Adds a long attribute to component.
  307. * Atributes must be added before any content is written.
  308. *
  309. * @param name Attribute name
  310. * @param value Attribute value
  311. * @return this object
  312. */
  313. public void addAttribute(String name, long value) throws PaintException {
  314. addAttribute(name, String.valueOf(value));
  315. }
  316. /** Adds a string attribute to component.
  317. * Atributes must be added before any content is written.
  318. *
  319. * @param name Boolean attribute name
  320. * @param value Boolean attribute value
  321. * @return this object
  322. */
  323. public void addAttribute(String name, String value) throws PaintException {
  324. // In case of null data output nothing:
  325. if ((value == null) || (name == null))
  326. throw new NullPointerException("Parameters must be non-null strings ("+name+"="+value+")");
  327. //Ensure that the target is open
  328. if (this.closed)
  329. throw new PaintException("Attempted to write to a closed PaintTarget.");
  330. // Check that argument list is writable.
  331. if (!mTagArgumentListOpen)
  332. throw new PaintException("XML argument list not open.");
  333. tagBuffer.append(" " + name + "=\"" + escapeXML(value) + "\"");
  334. }
  335. /** Add a string type variable.
  336. * @param owner Listener for variable changes
  337. * @param name Variable name
  338. * @param value Variable initial value
  339. * @return Reference to this.
  340. */
  341. public void addVariable(VariableOwner owner, String name, String value)
  342. throws PaintException {
  343. String code = variableMap.registerVariable(name, String.class, value, owner);
  344. startTag("string");
  345. addAttribute(UIDL_ARG_ID, code);
  346. addAttribute(UIDL_ARG_NAME, name);
  347. addText(value);
  348. endTag("string");
  349. }
  350. /** Add a int type variable.
  351. * @param owner Listener for variable changes
  352. * @param name Variable name
  353. * @param value Variable initial value
  354. * @return Reference to this.
  355. */
  356. public void addVariable(VariableOwner owner, String name, int value)
  357. throws PaintException {
  358. String code = variableMap.registerVariable(name, Integer.class, new Integer(value), owner);
  359. startTag("integer");
  360. addAttribute(UIDL_ARG_ID, code);
  361. addAttribute(UIDL_ARG_NAME, name);
  362. addAttribute(UIDL_ARG_VALUE, String.valueOf(value));
  363. endTag("integer");
  364. }
  365. /** Add a boolean type variable.
  366. * @param owner Listener for variable changes
  367. * @param name Variable name
  368. * @param value Variable initial value
  369. * @return Reference to this.
  370. */
  371. public void addVariable(VariableOwner owner, String name, boolean value)
  372. throws PaintException {
  373. String code = variableMap.registerVariable(name, Boolean.class, new Boolean(value), owner);
  374. startTag("boolean");
  375. addAttribute(UIDL_ARG_ID, code);
  376. addAttribute(UIDL_ARG_NAME, name);
  377. addAttribute(UIDL_ARG_VALUE, String.valueOf(value));
  378. endTag("boolean");
  379. }
  380. /** Add a string array type variable.
  381. * @param owner Listener for variable changes
  382. * @param name Variable name
  383. * @param value Variable initial value
  384. * @return Reference to this.
  385. */
  386. public void addVariable(VariableOwner owner, String name, String[] value)
  387. throws PaintException {
  388. String code = variableMap.registerVariable(name, String[].class, value, owner);
  389. startTag("array");
  390. addAttribute(UIDL_ARG_ID, code);
  391. addAttribute(UIDL_ARG_NAME, name);
  392. for (int i = 0; i < value.length; i++)
  393. addSection("ai", value[i]);
  394. endTag("array");
  395. }
  396. /** Add a upload stream type variable.
  397. * @param owner Listener for variable changes
  398. * @param name Variable name
  399. * @param value Variable initial value
  400. * @return Reference to this.
  401. */
  402. public void addUploadStreamVariable(VariableOwner owner, String name)
  403. throws PaintException {
  404. String code =
  405. variableMap.registerVariable(name, UploadStream.class, null, owner);
  406. startTag("uploadstream");
  407. addAttribute(UIDL_ARG_ID, code);
  408. addAttribute(UIDL_ARG_NAME, name);
  409. endTag("uploadstream");
  410. }
  411. /** Print single text section.
  412. *
  413. * Prints full text section. The section data is escaped from XML tags and
  414. * surrounded by XML start and end-tags.
  415. */
  416. public void addSection(String sectionTagName, String sectionData)
  417. throws PaintException {
  418. startTag(sectionTagName);
  419. addText(sectionData);
  420. endTag(sectionTagName);
  421. }
  422. /** Add XML dirctly to UIDL */
  423. public void addUIDL(String xml) throws PaintException {
  424. //Ensure that the target is open
  425. if (this.closed)
  426. throw new PaintException("Attempted to write to a closed PaintTarget.");
  427. // Make sure that the open start tag is closed before
  428. // anything is written.
  429. ensureClosedTag();
  430. // Escape and write what was given
  431. if (xml != null)
  432. append(xml);
  433. }
  434. /** Add XML section with namespace
  435. * @see com.itmill.toolkit.terminal.PaintTarget#addXMLSection(String, String, String)
  436. */
  437. public void addXMLSection(
  438. String sectionTagName,
  439. String sectionData,
  440. String namespace)
  441. throws PaintException {
  442. //Ensure that the target is open
  443. if (this.closed)
  444. throw new PaintException("Attempted to write to a closed PaintTarget.");
  445. startTag(sectionTagName);
  446. if (namespace != null)
  447. addAttribute("xmlns", namespace);
  448. // Close that starting tag
  449. ensureClosedTag();
  450. if (sectionData != null)
  451. append(sectionData);
  452. endTag(sectionTagName);
  453. }
  454. /** Get the UIDL already printed to stream.
  455. * Paint target must be closed before the getUIDL()
  456. * cn be called.
  457. */
  458. public String getUIDL() {
  459. if (this.closed) {
  460. return uidlBuffer.toString();
  461. }
  462. throw new IllegalStateException("Tried to read UIDL from open PaintTarget");
  463. }
  464. /** Close the paint target.
  465. * Paint target must be closed before the getUIDL()
  466. * cn be called.
  467. * Subsequent attempts to write to paint target.
  468. * If the target was already closed, call to this
  469. * function is ignored.
  470. * will generate an exception.
  471. */
  472. public void close() throws PaintException {
  473. if (!this.closed) {
  474. this.endTag("uidl");
  475. this.closed = true;
  476. }
  477. }
  478. /** Print element start tag of a paintable section.
  479. * Starts a paintable section using the given tag. The PaintTarget may
  480. * implement a caching scheme, that checks the paintable has actually
  481. * changed or can a cached version be used instead. This method should call
  482. * the startTag method. <p> If the Paintable is found in cache and this
  483. * function returns true it may omit the content and close the tag, in which
  484. * case cached content should be used.
  485. * </p><b>Note:</b> Web adapter does not currently implement caching and
  486. * this function always returns false.
  487. * @param paintable The paintable to start
  488. * @param tagName The name of the start tag
  489. * @return false
  490. * @see com.itmill.toolkit.terminal.PaintTarget#startTag(Paintable, String),
  491. * #startTag(String)
  492. * @since 3.1
  493. */
  494. public boolean startTag(Paintable paintable, String tag)
  495. throws PaintException {
  496. startTag(tag);
  497. return false;
  498. }
  499. /** Add CDATA node to target UIDL-tree.
  500. * @param text Character data to add
  501. * @since 3.1
  502. */
  503. public void addCharacterData(String text) throws PaintException {
  504. addUIDL("<![CDATA["+text+"]]>");
  505. }
  506. }