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

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