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.

Label.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.ui;
  5. import java.lang.reflect.Method;
  6. import com.itmill.toolkit.data.Property;
  7. import com.itmill.toolkit.data.util.ObjectProperty;
  8. import com.itmill.toolkit.terminal.PaintException;
  9. import com.itmill.toolkit.terminal.PaintTarget;
  10. /**
  11. * Label component for showing non-editable short texts.
  12. *
  13. * The label content can be set to the modes specified by the final members
  14. * CONTENT_*
  15. *
  16. * <p>
  17. * The contents of the label may contain simple formatting:
  18. * <ul>
  19. * <li><b>&lt;b></b> Bold
  20. * <li><b>&lt;i></b> Italic
  21. * <li><b>&lt;u></b> Underlined
  22. * <li><b>&lt;br/></b> Linebreak
  23. * <li><b>&lt;ul>&lt;li>item 1&lt;/li>&lt;li>item 2&lt;/li>&lt;/ul></b> List of
  24. * items
  25. * </ul>
  26. * The <b>b</b>,<b>i</b>,<b>u</b> and <b>li</b> tags can contain all the tags in
  27. * the list recursively.
  28. * </p>
  29. *
  30. * @author IT Mill Ltd.
  31. * @version
  32. * @VERSION@
  33. * @since 3.0
  34. */
  35. public class Label extends AbstractComponent implements Property,
  36. Property.Viewer, Property.ValueChangeListener,
  37. Property.ValueChangeNotifier, Comparable {
  38. /**
  39. * Content mode, where the label contains only plain text. The getValue()
  40. * result is coded to XML when painting.
  41. */
  42. public static final int CONTENT_TEXT = 0;
  43. /**
  44. * Content mode, where the label contains preformatted text.
  45. */
  46. public static final int CONTENT_PREFORMATTED = 1;
  47. /**
  48. * Formatted content mode, where the contents is XML restricted to the UIDL
  49. * 1.0 formatting markups.
  50. */
  51. public static final int CONTENT_UIDL = 2;
  52. /**
  53. * Content mode, where the label contains XHTML. Contents is then enclosed
  54. * in DIV elements having namespace of
  55. * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".
  56. */
  57. public static final int CONTENT_XHTML = 3;
  58. /**
  59. * Content mode, where the label contains well-formed or well-balanced XML.
  60. * Each of the root elements must have their default namespace specified.
  61. */
  62. public static final int CONTENT_XML = 4;
  63. /**
  64. * Content mode, where the label contains RAW output. Output is not required
  65. * to comply to with XML. In Web Adapter output is inserted inside the
  66. * resulting HTML document as-is. This is useful for some specific purposes
  67. * where possibly broken HTML content needs to be shown, but in most cases
  68. * XHTML mode should be preferred.
  69. */
  70. public static final int CONTENT_RAW = 5;
  71. /**
  72. * The default content mode is plain text.
  73. */
  74. public static final int CONTENT_DEFAULT = CONTENT_TEXT;
  75. /** Array of content mode names that are rendered in UIDL as mode attribute. */
  76. private static final String[] CONTENT_MODE_NAME = { "text", "pre", "uidl",
  77. "xhtml", "xml", "raw" };
  78. private Property dataSource;
  79. private int contentMode = CONTENT_DEFAULT;
  80. /**
  81. * Creates an empty Label.
  82. */
  83. public Label() {
  84. setPropertyDataSource(new ObjectProperty("", String.class));
  85. }
  86. /**
  87. * Creates a new instance of Label with text-contents.
  88. *
  89. * @param content
  90. */
  91. public Label(String content) {
  92. setPropertyDataSource(new ObjectProperty(content, String.class));
  93. }
  94. /**
  95. * Creates a new instance of Label with text-contents read from given
  96. * datasource.
  97. *
  98. * @param contentSource
  99. */
  100. public Label(Property contentSource) {
  101. setPropertyDataSource(contentSource);
  102. }
  103. /**
  104. * Creates a new instance of Label with text-contents.
  105. *
  106. * @param content
  107. * @param contentMode
  108. */
  109. public Label(String content, int contentMode) {
  110. setPropertyDataSource(new ObjectProperty(content, String.class));
  111. setContentMode(contentMode);
  112. }
  113. /**
  114. * Creates a new instance of Label with text-contents read from given
  115. * datasource.
  116. *
  117. * @param contentSource
  118. * @param contentMode
  119. */
  120. public Label(Property contentSource, int contentMode) {
  121. setPropertyDataSource(contentSource);
  122. setContentMode(contentMode);
  123. }
  124. /**
  125. * Get the component UIDL tag.
  126. *
  127. * @return the Component UIDL tag as string.
  128. */
  129. public String getTag() {
  130. return "label";
  131. }
  132. /**
  133. * Set the component to read-only. Readonly is not used in label.
  134. *
  135. * @param readOnly
  136. * True to enable read-only mode, False to disable it.
  137. */
  138. public void setReadOnly(boolean readOnly) {
  139. if (dataSource == null) {
  140. throw new IllegalStateException("Datasource must be se");
  141. }
  142. dataSource.setReadOnly(readOnly);
  143. }
  144. /**
  145. * Is the component read-only ? Readonly is not used in label - this returns
  146. * allways false.
  147. *
  148. * @return <code>true</code> if the component is in read only mode.
  149. */
  150. public boolean isReadOnly() {
  151. if (dataSource == null) {
  152. throw new IllegalStateException("Datasource must be se");
  153. }
  154. return dataSource.isReadOnly();
  155. }
  156. /**
  157. * Paints the content of this component.
  158. *
  159. * @param target
  160. * the Paint Event.
  161. * @throws PaintException
  162. * if the Paint Operation fails.
  163. */
  164. public void paintContent(PaintTarget target) throws PaintException {
  165. if (contentMode != CONTENT_TEXT) {
  166. target.addAttribute("mode", CONTENT_MODE_NAME[contentMode]);
  167. }
  168. if (contentMode == CONTENT_TEXT) {
  169. target.addText(toString());
  170. } else if (contentMode == CONTENT_UIDL) {
  171. target.addUIDL(toString());
  172. } else if (contentMode == CONTENT_XHTML) {
  173. target.startTag("data");
  174. target.addXMLSection("div", toString(),
  175. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
  176. target.endTag("data");
  177. } else if (contentMode == CONTENT_PREFORMATTED) {
  178. target.startTag("pre");
  179. target.addText(toString());
  180. target.endTag("pre");
  181. } else if (contentMode == CONTENT_XML) {
  182. target.addXMLSection("data", toString(), null);
  183. } else if (contentMode == CONTENT_RAW) {
  184. target.startTag("data");
  185. target.addAttribute("escape", false);
  186. target.addText(toString());
  187. target.endTag("data");
  188. }
  189. }
  190. /**
  191. * Gets the value of the label. Value of the label is the XML contents of
  192. * the label.
  193. *
  194. * @return the Value of the label.
  195. */
  196. public Object getValue() {
  197. if (dataSource == null) {
  198. throw new IllegalStateException("Datasource must be se");
  199. }
  200. return dataSource.getValue();
  201. }
  202. /**
  203. * Set the value of the label. Value of the label is the XML contents of the
  204. * label.
  205. *
  206. * @param newValue
  207. * the New value of the label.
  208. */
  209. public void setValue(Object newValue) {
  210. if (dataSource == null) {
  211. throw new IllegalStateException("Datasource must be se");
  212. }
  213. dataSource.setValue(newValue);
  214. }
  215. /**
  216. * @see java.lang.Object#toString()
  217. */
  218. public String toString() {
  219. if (dataSource == null) {
  220. throw new IllegalStateException("Datasource must be se");
  221. }
  222. return dataSource.toString();
  223. }
  224. /**
  225. * Gets the type of the Property.
  226. *
  227. * @see com.itmill.toolkit.data.Property#getType()
  228. */
  229. public Class getType() {
  230. if (dataSource == null) {
  231. throw new IllegalStateException("Datasource must be se");
  232. }
  233. return dataSource.getType();
  234. }
  235. /**
  236. * Gets the viewing data-source property.
  237. *
  238. * @return the data source property.
  239. * @see com.itmill.toolkit.data.Property.Viewer#getPropertyDataSource()
  240. */
  241. public Property getPropertyDataSource() {
  242. return dataSource;
  243. }
  244. /**
  245. * Sets the property as data-source for viewing.
  246. *
  247. * @param newDataSource
  248. * the new data source Property
  249. * @see com.itmill.toolkit.data.Property.Viewer#setPropertyDataSource(com.itmill.toolkit.data.Property)
  250. */
  251. public void setPropertyDataSource(Property newDataSource) {
  252. // Stops listening the old data source changes
  253. if (dataSource != null
  254. && Property.ValueChangeNotifier.class
  255. .isAssignableFrom(dataSource.getClass())) {
  256. ((Property.ValueChangeNotifier) dataSource).removeListener(this);
  257. }
  258. // Sets the new data source
  259. dataSource = newDataSource;
  260. // Listens the new data source if possible
  261. if (dataSource != null
  262. && Property.ValueChangeNotifier.class
  263. .isAssignableFrom(dataSource.getClass())) {
  264. ((Property.ValueChangeNotifier) dataSource).addListener(this);
  265. }
  266. }
  267. /**
  268. * Gets the content mode of the Label.
  269. *
  270. * <p>
  271. * Possible content modes include:
  272. * <ul>
  273. * <li><b>CONTENT_TEXT</b> Content mode, where the label contains only plain
  274. * text. The getValue() result is coded to XML when painting.</li>
  275. * <li><b>CONTENT_PREFORMATTED</b> Content mode, where the label contains
  276. * preformatted text.</li>
  277. * <li><b>CONTENT_UIDL</b> Formatted content mode, where the contents is XML
  278. * restricted to the UIDL 1.0 formatting markups.</li>
  279. * <li><b>CONTENT_XHTML</b> Content mode, where the label contains XHTML.
  280. * Contents is then enclosed in DIV elements having namespace of
  281. * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".</li>
  282. * <li><b>CONTENT_XML</b> Content mode, where the label contains well-formed
  283. * or well-balanced XML. Each of the root elements must have their default
  284. * namespace specified.</li>
  285. * <li><b>CONTENT_RAW</b> Content mode, where the label contains RAW output.
  286. * Output is not required to comply to with XML. In Web Adapter output is
  287. * inserted inside the resulting HTML document as-is. This is useful for
  288. * some specific purposes where possibly broken HTML content needs to be
  289. * shown, but in most cases XHTML mode should be preferred.</li>
  290. * </ul>
  291. * </p>
  292. *
  293. * @return the Content mode of the label.
  294. */
  295. public int getContentMode() {
  296. return contentMode;
  297. }
  298. /**
  299. * Sets the content mode of the Label.
  300. *
  301. * <p>
  302. * Possible content modes include:
  303. * <ul>
  304. * <li><b>CONTENT_TEXT</b> Content mode, where the label contains only plain
  305. * text. The getValue() result is coded to XML when painting.</li>
  306. * <li><b>CONTENT_PREFORMATTED</b> Content mode, where the label contains
  307. * preformatted text.</li>
  308. * <li><b>CONTENT_UIDL</b> Formatted content mode, where the contents is XML
  309. * restricted to the UIDL 1.0 formatting markups.</li>
  310. * <li><b>CONTENT_XHTML</b> Content mode, where the label contains XHTML.
  311. * Contents is then enclosed in DIV elements having namespace of
  312. * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd".</li>
  313. * <li><b>CONTENT_XML</b> Content mode, where the label contains well-formed
  314. * or well-balanced XML. Each of the root elements must have their default
  315. * namespace specified.</li>
  316. * <li><b>CONTENT_RAW</b> Content mode, where the label contains RAW output.
  317. * Output is not required to comply to with XML. In Web Adapter output is
  318. * inserted inside the resulting HTML document as-is. This is useful for
  319. * some specific purposes where possibly broken HTML content needs to be
  320. * shown, but in most cases XHTML mode should be preferred.</li>
  321. * </ul>
  322. * </p>
  323. *
  324. * @param contentMode
  325. * the New content mode of the label.
  326. */
  327. public void setContentMode(int contentMode) {
  328. if (contentMode >= CONTENT_TEXT && contentMode <= CONTENT_RAW) {
  329. this.contentMode = contentMode;
  330. }
  331. }
  332. /* Value change events */
  333. private static final Method VALUE_CHANGE_METHOD;
  334. static {
  335. try {
  336. VALUE_CHANGE_METHOD = Property.ValueChangeListener.class
  337. .getDeclaredMethod("valueChange",
  338. new Class[] { Property.ValueChangeEvent.class });
  339. } catch (final java.lang.NoSuchMethodException e) {
  340. // This should never happen
  341. throw new java.lang.RuntimeException(
  342. "Internal error finding methods in Label");
  343. }
  344. }
  345. /**
  346. * Value change event
  347. *
  348. * @author IT Mill Ltd.
  349. * @version
  350. * @VERSION@
  351. * @since 3.0
  352. */
  353. public class ValueChangeEvent extends Component.Event implements
  354. Property.ValueChangeEvent {
  355. /**
  356. * Serial generated by eclipse.
  357. */
  358. private static final long serialVersionUID = 3906084563938586935L;
  359. /**
  360. * New instance of text change event
  361. *
  362. * @param source
  363. * the Source of the event.
  364. */
  365. public ValueChangeEvent(Label source) {
  366. super(source);
  367. }
  368. /**
  369. * Gets the Property that has been modified.
  370. *
  371. * @see com.itmill.toolkit.data.Property.ValueChangeEvent#getProperty()
  372. */
  373. public Property getProperty() {
  374. return (Property) getSource();
  375. }
  376. }
  377. /**
  378. * Adds the value change listener.
  379. *
  380. * @param listener
  381. * the Listener to be added.
  382. * @see com.itmill.toolkit.data.Property.ValueChangeNotifier#addListener(com.itmill.toolkit.data.Property.ValueChangeListener)
  383. */
  384. public void addListener(Property.ValueChangeListener listener) {
  385. addListener(Label.ValueChangeEvent.class, listener, VALUE_CHANGE_METHOD);
  386. }
  387. /**
  388. * Removes the value change listener.
  389. *
  390. * @param listener
  391. * the Listener to be removed.
  392. * @see com.itmill.toolkit.data.Property.ValueChangeNotifier#removeListener(com.itmill.toolkit.data.Property.ValueChangeListener)
  393. */
  394. public void removeListener(Property.ValueChangeListener listener) {
  395. removeListener(Label.ValueChangeEvent.class, listener,
  396. VALUE_CHANGE_METHOD);
  397. }
  398. /**
  399. * Emits the options change event.
  400. */
  401. protected void fireValueChange() {
  402. // Set the error message
  403. fireEvent(new Label.ValueChangeEvent(this));
  404. requestRepaint();
  405. }
  406. /**
  407. * Listens the value change events from data source.
  408. *
  409. * @see com.itmill.toolkit.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
  410. */
  411. public void valueChange(Property.ValueChangeEvent event) {
  412. fireValueChange();
  413. }
  414. /**
  415. * Compares the Label to other objects.
  416. *
  417. * <p>
  418. * Labels can be compared to other labels for sorting label contents. This
  419. * is especially handy for sorting table columns.
  420. * </p>
  421. *
  422. * <p>
  423. * In RAW, PREFORMATTED and TEXT modes, the label contents are compared as
  424. * is. In XML, UIDL and XHTML modes, only CDATA is compared and tags
  425. * ignored. If the other object is not a Label, its toString() return value
  426. * is used in comparison.
  427. * </p>
  428. *
  429. * @param other
  430. * the Other object to compare to.
  431. * @return a negative integer, zero, or a positive integer as this object is
  432. * less than, equal to, or greater than the specified object.
  433. * @see java.lang.Comparable#compareTo(java.lang.Object)
  434. */
  435. public int compareTo(Object other) {
  436. String thisValue;
  437. String otherValue;
  438. if (contentMode == CONTENT_XML || contentMode == CONTENT_UIDL
  439. || contentMode == CONTENT_XHTML) {
  440. thisValue = stripTags(toString());
  441. } else {
  442. thisValue = toString();
  443. }
  444. if (other instanceof Label
  445. && (((Label) other).getContentMode() == CONTENT_XML
  446. || ((Label) other).getContentMode() == CONTENT_UIDL || ((Label) other)
  447. .getContentMode() == CONTENT_XHTML)) {
  448. otherValue = stripTags(other.toString());
  449. } else {
  450. otherValue = other.toString();
  451. }
  452. return thisValue.compareTo(otherValue);
  453. }
  454. /**
  455. * Strips the tags from the XML.
  456. *
  457. * @param xml
  458. * the String containing a XML snippet.
  459. * @return the original XML without tags.
  460. */
  461. private String stripTags(String xml) {
  462. final StringBuffer res = new StringBuffer();
  463. int processed = 0;
  464. final int xmlLen = xml.length();
  465. while (processed < xmlLen) {
  466. int next = xml.indexOf('<', processed);
  467. if (next < 0) {
  468. next = xmlLen;
  469. }
  470. res.append(xml.substring(processed, next));
  471. if (processed < xmlLen) {
  472. next = xml.indexOf('>', processed);
  473. if (next < 0) {
  474. next = xmlLen;
  475. }
  476. processed = next + 1;
  477. }
  478. }
  479. return res.toString();
  480. }
  481. }