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.

AbstractComponent.java 50KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.io.Serializable;
  6. import java.lang.reflect.Method;
  7. import java.util.ArrayList;
  8. import java.util.Collection;
  9. import java.util.Collections;
  10. import java.util.HashMap;
  11. import java.util.HashSet;
  12. import java.util.Iterator;
  13. import java.util.LinkedList;
  14. import java.util.Locale;
  15. import java.util.Map;
  16. import java.util.Set;
  17. import java.util.regex.Matcher;
  18. import java.util.regex.Pattern;
  19. import com.vaadin.Application;
  20. import com.vaadin.event.ActionManager;
  21. import com.vaadin.event.EventRouter;
  22. import com.vaadin.event.MethodEventSource;
  23. import com.vaadin.event.ShortcutListener;
  24. import com.vaadin.terminal.ErrorMessage;
  25. import com.vaadin.terminal.PaintException;
  26. import com.vaadin.terminal.PaintTarget;
  27. import com.vaadin.terminal.PaintTarget.PaintStatus;
  28. import com.vaadin.terminal.Resource;
  29. import com.vaadin.terminal.Terminal;
  30. import com.vaadin.terminal.gwt.client.ComponentState;
  31. import com.vaadin.terminal.gwt.client.ui.VAbstractPaintableWidget;
  32. import com.vaadin.terminal.gwt.server.ComponentSizeValidator;
  33. import com.vaadin.terminal.gwt.server.RpcManager;
  34. import com.vaadin.terminal.gwt.server.RpcTarget;
  35. import com.vaadin.terminal.gwt.server.ServerRpcManager;
  36. import com.vaadin.tools.ReflectTools;
  37. /**
  38. * An abstract class that defines default implementation for the
  39. * {@link Component} interface. Basic UI components that are not derived from an
  40. * external component can inherit this class to easily qualify as Vaadin
  41. * components. Most components in Vaadin do just that.
  42. *
  43. * @author Vaadin Ltd.
  44. * @version
  45. * @VERSION@
  46. * @since 3.0
  47. */
  48. @SuppressWarnings("serial")
  49. public abstract class AbstractComponent implements Component, MethodEventSource {
  50. /* Private members */
  51. /**
  52. * Style names.
  53. */
  54. private ArrayList<String> styles;
  55. /**
  56. * Caption text.
  57. */
  58. private String caption;
  59. /**
  60. * Application specific data object. The component does not use or modify
  61. * this.
  62. */
  63. private Object applicationData;
  64. /**
  65. * Icon to be shown together with caption.
  66. */
  67. private Resource icon;
  68. /**
  69. * Is the component enabled (its normal usage is allowed).
  70. */
  71. private boolean enabled = true;
  72. /**
  73. * Is the component visible (it is rendered).
  74. */
  75. private boolean visible = true;
  76. /**
  77. * Is the component read-only ?
  78. */
  79. private boolean readOnly = false;
  80. /**
  81. * Description of the usage (XML).
  82. */
  83. private String description = null;
  84. /**
  85. * The container this component resides in.
  86. */
  87. private Component parent = null;
  88. /**
  89. * The EventRouter used for the event model.
  90. */
  91. private EventRouter eventRouter = null;
  92. /**
  93. * A set of event identifiers with registered listeners.
  94. */
  95. private Set<String> eventIdentifiers = null;
  96. /**
  97. * The internal error message of the component.
  98. */
  99. private ErrorMessage componentError = null;
  100. /**
  101. * Immediate mode: if true, all variable changes are required to be sent
  102. * from the terminal immediately.
  103. */
  104. private boolean immediate = false;
  105. /**
  106. * Locale of this component.
  107. */
  108. private Locale locale;
  109. /**
  110. * The component should receive focus (if {@link Focusable}) when attached.
  111. */
  112. private boolean delayedFocus;
  113. /**
  114. * List of repaint request listeners or null if not listened at all.
  115. */
  116. private LinkedList<RepaintRequestListener> repaintRequestListeners = null;
  117. /**
  118. * Are all the repaint listeners notified about recent changes ?
  119. */
  120. private boolean repaintRequestListenersNotified = false;
  121. private String testingId;
  122. /* Sizeable fields */
  123. private float width = SIZE_UNDEFINED;
  124. private float height = SIZE_UNDEFINED;
  125. private Unit widthUnit = Unit.PIXELS;
  126. private Unit heightUnit = Unit.PIXELS;
  127. private static final Pattern sizePattern = Pattern
  128. .compile("^(-?\\d+(\\.\\d+)?)(%|px|em|ex|in|cm|mm|pt|pc)?$");
  129. private ComponentErrorHandler errorHandler = null;
  130. /**
  131. * Keeps track of the Actions added to this component; the actual
  132. * handling/notifying is delegated, usually to the containing window.
  133. */
  134. private ActionManager actionManager;
  135. /**
  136. * A map from RPC interface class to the RPC call manager that handles
  137. * incoming RPC calls for that interface.
  138. */
  139. private Map<Class<?>, RpcManager> rpcManagerMap = new HashMap<Class<?>, RpcManager>();
  140. /**
  141. * Shared state object to be communicated from the server to the client when
  142. * modified.
  143. */
  144. private ComponentState sharedState;
  145. /* Constructor */
  146. /**
  147. * Constructs a new Component.
  148. */
  149. public AbstractComponent() {
  150. // ComponentSizeValidator.setCreationLocation(this);
  151. }
  152. /* Get/Set component properties */
  153. public void setDebugId(String id) {
  154. testingId = id;
  155. }
  156. public String getDebugId() {
  157. return testingId;
  158. }
  159. /**
  160. * Gets style for component. Multiple styles are joined with spaces.
  161. *
  162. * @return the component's styleValue of property style.
  163. * @deprecated Use getStyleName() instead; renamed for consistency and to
  164. * indicate that "style" should not be used to switch client
  165. * side implementation, only to style the component.
  166. */
  167. @Deprecated
  168. public String getStyle() {
  169. return getStyleName();
  170. }
  171. /**
  172. * Sets and replaces all previous style names of the component. This method
  173. * will trigger a {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
  174. * RepaintRequestEvent}.
  175. *
  176. * @param style
  177. * the new style of the component.
  178. * @deprecated Use setStyleName() instead; renamed for consistency and to
  179. * indicate that "style" should not be used to switch client
  180. * side implementation, only to style the component.
  181. */
  182. @Deprecated
  183. public void setStyle(String style) {
  184. setStyleName(style);
  185. }
  186. /*
  187. * Gets the component's style. Don't add a JavaDoc comment here, we use the
  188. * default documentation from implemented interface.
  189. */
  190. public String getStyleName() {
  191. String s = "";
  192. if (styles != null) {
  193. for (final Iterator<String> it = styles.iterator(); it.hasNext();) {
  194. s += it.next();
  195. if (it.hasNext()) {
  196. s += " ";
  197. }
  198. }
  199. }
  200. return s;
  201. }
  202. /*
  203. * Sets the component's style. Don't add a JavaDoc comment here, we use the
  204. * default documentation from implemented interface.
  205. */
  206. public void setStyleName(String style) {
  207. if (style == null || "".equals(style)) {
  208. styles = null;
  209. requestRepaint();
  210. return;
  211. }
  212. if (styles == null) {
  213. styles = new ArrayList<String>();
  214. }
  215. styles.clear();
  216. String[] styleParts = style.split(" +");
  217. for (String part : styleParts) {
  218. if (part.length() > 0) {
  219. styles.add(part);
  220. }
  221. }
  222. requestRepaint();
  223. }
  224. public void addStyleName(String style) {
  225. if (style == null || "".equals(style)) {
  226. return;
  227. }
  228. if (styles == null) {
  229. styles = new ArrayList<String>();
  230. }
  231. if (!styles.contains(style)) {
  232. styles.add(style);
  233. requestRepaint();
  234. }
  235. }
  236. public void removeStyleName(String style) {
  237. if (styles != null) {
  238. String[] styleParts = style.split(" +");
  239. for (String part : styleParts) {
  240. if (part.length() > 0) {
  241. styles.remove(part);
  242. }
  243. }
  244. requestRepaint();
  245. }
  246. }
  247. /*
  248. * Get's the component's caption. Don't add a JavaDoc comment here, we use
  249. * the default documentation from implemented interface.
  250. */
  251. public String getCaption() {
  252. return caption;
  253. }
  254. /**
  255. * Sets the component's caption <code>String</code>. Caption is the visible
  256. * name of the component. This method will trigger a
  257. * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
  258. * RepaintRequestEvent}.
  259. *
  260. * @param caption
  261. * the new caption <code>String</code> for the component.
  262. */
  263. public void setCaption(String caption) {
  264. this.caption = caption;
  265. requestRepaint();
  266. }
  267. /*
  268. * Don't add a JavaDoc comment here, we use the default documentation from
  269. * implemented interface.
  270. */
  271. public Locale getLocale() {
  272. if (locale != null) {
  273. return locale;
  274. }
  275. if (parent != null) {
  276. return parent.getLocale();
  277. }
  278. final Application app = getApplication();
  279. if (app != null) {
  280. return app.getLocale();
  281. }
  282. return null;
  283. }
  284. /**
  285. * Sets the locale of this component.
  286. *
  287. * <pre>
  288. * // Component for which the locale is meaningful
  289. * InlineDateField date = new InlineDateField(&quot;Datum&quot;);
  290. *
  291. * // German language specified with ISO 639-1 language
  292. * // code and ISO 3166-1 alpha-2 country code.
  293. * date.setLocale(new Locale(&quot;de&quot;, &quot;DE&quot;));
  294. *
  295. * date.setResolution(DateField.RESOLUTION_DAY);
  296. * layout.addComponent(date);
  297. * </pre>
  298. *
  299. *
  300. * @param locale
  301. * the locale to become this component's locale.
  302. */
  303. public void setLocale(Locale locale) {
  304. this.locale = locale;
  305. // FIXME: Reload value if there is a converter
  306. requestRepaint();
  307. }
  308. /*
  309. * Gets the component's icon resource. Don't add a JavaDoc comment here, we
  310. * use the default documentation from implemented interface.
  311. */
  312. public Resource getIcon() {
  313. return icon;
  314. }
  315. /**
  316. * Sets the component's icon. This method will trigger a
  317. * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
  318. * RepaintRequestEvent}.
  319. *
  320. * @param icon
  321. * the icon to be shown with the component's caption.
  322. */
  323. public void setIcon(Resource icon) {
  324. this.icon = icon;
  325. requestRepaint();
  326. }
  327. /*
  328. * Tests if the component is enabled or not. Don't add a JavaDoc comment
  329. * here, we use the default documentation from implemented interface.
  330. */
  331. public boolean isEnabled() {
  332. return enabled && (parent == null || parent.isEnabled()) && isVisible();
  333. }
  334. /*
  335. * Enables or disables the component. Don't add a JavaDoc comment here, we
  336. * use the default documentation from implemented interface.
  337. */
  338. public void setEnabled(boolean enabled) {
  339. if (this.enabled != enabled) {
  340. boolean wasEnabled = this.enabled;
  341. boolean wasEnabledInContext = isEnabled();
  342. this.enabled = enabled;
  343. boolean isEnabled = enabled;
  344. boolean isEnabledInContext = isEnabled();
  345. // If the actual enabled state (as rendered, in context) has not
  346. // changed we do not need to repaint except if the parent is
  347. // invisible.
  348. // If the parent is invisible we must request a repaint so the
  349. // component is repainted with the new enabled state when the parent
  350. // is set visible again. This workaround is needed as isEnabled
  351. // checks isVisible.
  352. boolean needRepaint = (wasEnabledInContext != isEnabledInContext)
  353. || (wasEnabled != isEnabled && (getParent() == null || !getParent()
  354. .isVisible()));
  355. if (needRepaint) {
  356. requestRepaint();
  357. }
  358. }
  359. }
  360. /*
  361. * Tests if the component is in the immediate mode. Don't add a JavaDoc
  362. * comment here, we use the default documentation from implemented
  363. * interface.
  364. */
  365. public boolean isImmediate() {
  366. return immediate;
  367. }
  368. /**
  369. * Sets the component's immediate mode to the specified status. This method
  370. * will trigger a {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
  371. * RepaintRequestEvent}.
  372. *
  373. * @param immediate
  374. * the boolean value specifying if the component should be in the
  375. * immediate mode after the call.
  376. * @see Component#isImmediate()
  377. */
  378. public void setImmediate(boolean immediate) {
  379. this.immediate = immediate;
  380. requestRepaint();
  381. }
  382. /*
  383. * (non-Javadoc)
  384. *
  385. * @see com.vaadin.ui.Component#isVisible()
  386. */
  387. public boolean isVisible() {
  388. return visible && (getParent() == null || getParent().isVisible());
  389. }
  390. /*
  391. * (non-Javadoc)
  392. *
  393. * @see com.vaadin.ui.Component#setVisible(boolean)
  394. */
  395. public void setVisible(boolean visible) {
  396. if (this.visible != visible) {
  397. this.visible = visible;
  398. // Instead of requesting repaint normally we
  399. // fire the event directly to assure that the
  400. // event goes through event in the component might
  401. // now be invisible
  402. fireRequestRepaintEvent(null);
  403. }
  404. }
  405. /**
  406. * <p>
  407. * Gets the component's description, used in tooltips and can be displayed
  408. * directly in certain other components such as forms. The description can
  409. * be used to briefly describe the state of the component to the user. The
  410. * description string may contain certain XML tags:
  411. * </p>
  412. *
  413. * <p>
  414. * <table border=1>
  415. * <tr>
  416. * <td width=120><b>Tag</b></td>
  417. * <td width=120><b>Description</b></td>
  418. * <td width=120><b>Example</b></td>
  419. * </tr>
  420. * <tr>
  421. * <td>&lt;b></td>
  422. * <td>bold</td>
  423. * <td><b>bold text</b></td>
  424. * </tr>
  425. * <tr>
  426. * <td>&lt;i></td>
  427. * <td>italic</td>
  428. * <td><i>italic text</i></td>
  429. * </tr>
  430. * <tr>
  431. * <td>&lt;u></td>
  432. * <td>underlined</td>
  433. * <td><u>underlined text</u></td>
  434. * </tr>
  435. * <tr>
  436. * <td>&lt;br></td>
  437. * <td>linebreak</td>
  438. * <td>N/A</td>
  439. * </tr>
  440. * <tr>
  441. * <td>&lt;ul><br>
  442. * &lt;li>item1<br>
  443. * &lt;li>item1<br>
  444. * &lt;/ul></td>
  445. * <td>item list</td>
  446. * <td>
  447. * <ul>
  448. * <li>item1
  449. * <li>item2
  450. * </ul>
  451. * </td>
  452. * </tr>
  453. * </table>
  454. * </p>
  455. *
  456. * <p>
  457. * These tags may be nested.
  458. * </p>
  459. *
  460. * @return component's description <code>String</code>
  461. */
  462. public String getDescription() {
  463. return description;
  464. }
  465. /**
  466. * Sets the component's description. See {@link #getDescription()} for more
  467. * information on what the description is. This method will trigger a
  468. * {@link com.vaadin.terminal.Paintable.RepaintRequestEvent
  469. * RepaintRequestEvent}.
  470. *
  471. * The description is displayed as HTML/XHTML in tooltips or directly in
  472. * certain components so care should be taken to avoid creating the
  473. * possibility for HTML injection and possibly XSS vulnerabilities.
  474. *
  475. * @param description
  476. * the new description string for the component.
  477. */
  478. public void setDescription(String description) {
  479. this.description = description;
  480. requestRepaint();
  481. }
  482. /*
  483. * Gets the component's parent component. Don't add a JavaDoc comment here,
  484. * we use the default documentation from implemented interface.
  485. */
  486. public Component getParent() {
  487. return parent;
  488. }
  489. /*
  490. * Sets the parent component. Don't add a JavaDoc comment here, we use the
  491. * default documentation from implemented interface.
  492. */
  493. public void setParent(Component parent) {
  494. // If the parent is not changed, don't do anything
  495. if (parent == this.parent) {
  496. return;
  497. }
  498. if (parent != null && this.parent != null) {
  499. throw new IllegalStateException(getClass().getName()
  500. + " already has a parent.");
  501. }
  502. // Send detach event if the component have been connected to a window
  503. if (getApplication() != null) {
  504. detach();
  505. }
  506. // Connect to new parent
  507. this.parent = parent;
  508. // Send attach event if connected to a window
  509. if (getApplication() != null) {
  510. attach();
  511. }
  512. }
  513. /**
  514. * Gets the error message for this component.
  515. *
  516. * @return ErrorMessage containing the description of the error state of the
  517. * component or null, if the component contains no errors. Extending
  518. * classes should override this method if they support other error
  519. * message types such as validation errors or buffering errors. The
  520. * returned error message contains information about all the errors.
  521. */
  522. public ErrorMessage getErrorMessage() {
  523. return componentError;
  524. }
  525. /**
  526. * Gets the component's error message.
  527. *
  528. * @link Terminal.ErrorMessage#ErrorMessage(String, int)
  529. *
  530. * @return the component's error message.
  531. */
  532. public ErrorMessage getComponentError() {
  533. return componentError;
  534. }
  535. /**
  536. * Sets the component's error message. The message may contain certain XML
  537. * tags, for more information see
  538. *
  539. * @link Component.ErrorMessage#ErrorMessage(String, int)
  540. *
  541. * @param componentError
  542. * the new <code>ErrorMessage</code> of the component.
  543. */
  544. public void setComponentError(ErrorMessage componentError) {
  545. this.componentError = componentError;
  546. fireComponentErrorEvent();
  547. requestRepaint();
  548. }
  549. /*
  550. * Tests if the component is in read-only mode. Don't add a JavaDoc comment
  551. * here, we use the default documentation from implemented interface.
  552. */
  553. public boolean isReadOnly() {
  554. return readOnly;
  555. }
  556. /*
  557. * Sets the component's read-only mode. Don't add a JavaDoc comment here, we
  558. * use the default documentation from implemented interface.
  559. */
  560. public void setReadOnly(boolean readOnly) {
  561. this.readOnly = readOnly;
  562. requestRepaint();
  563. }
  564. /*
  565. * Gets the parent window of the component. Don't add a JavaDoc comment
  566. * here, we use the default documentation from implemented interface.
  567. */
  568. public Root getRoot() {
  569. if (parent == null) {
  570. return null;
  571. } else {
  572. return parent.getRoot();
  573. }
  574. }
  575. /*
  576. * Notify the component that it's attached to a window. Don't add a JavaDoc
  577. * comment here, we use the default documentation from implemented
  578. * interface.
  579. */
  580. public void attach() {
  581. requestRepaint();
  582. if (!visible) {
  583. /*
  584. * Bypass the repaint optimization in childRequestedRepaint method
  585. * when attaching. When reattaching (possibly moving) -> must
  586. * repaint
  587. */
  588. fireRequestRepaintEvent(null);
  589. }
  590. if (delayedFocus) {
  591. focus();
  592. }
  593. if (actionManager != null) {
  594. actionManager.setViewer(getRoot());
  595. }
  596. }
  597. /*
  598. * Detach the component from application. Don't add a JavaDoc comment here,
  599. * we use the default documentation from implemented interface.
  600. */
  601. public void detach() {
  602. if (actionManager != null) {
  603. actionManager.setViewer((Root) null);
  604. }
  605. }
  606. /**
  607. * Sets the focus for this component if the component is {@link Focusable}.
  608. */
  609. protected void focus() {
  610. if (this instanceof Focusable) {
  611. final Application app = getApplication();
  612. if (app != null) {
  613. getRoot().setFocusedComponent((Focusable) this);
  614. delayedFocus = false;
  615. } else {
  616. delayedFocus = true;
  617. }
  618. }
  619. }
  620. /**
  621. * Gets the application object to which the component is attached.
  622. *
  623. * <p>
  624. * The method will return {@code null} if the component is not currently
  625. * attached to an application. This is often a problem in constructors of
  626. * regular components and in the initializers of custom composite
  627. * components. A standard workaround is to move the problematic
  628. * initialization to {@link #attach()}, as described in the documentation of
  629. * the method.
  630. * </p>
  631. * <p>
  632. * <b>This method is not meant to be overridden. Due to CDI requirements we
  633. * cannot declare it as final even though it should be final.</b>
  634. * </p>
  635. *
  636. * @return the parent application of the component or <code>null</code>.
  637. * @see #attach()
  638. */
  639. public Application getApplication() {
  640. if (parent == null) {
  641. return null;
  642. } else {
  643. return parent.getApplication();
  644. }
  645. }
  646. /* Component painting */
  647. /* Documented in super interface */
  648. public void requestRepaintRequests() {
  649. repaintRequestListenersNotified = false;
  650. }
  651. /**
  652. *
  653. * <p>
  654. * Paints the Paintable into a UIDL stream. This method creates the UIDL
  655. * sequence describing it and outputs it to the given UIDL stream.
  656. * </p>
  657. *
  658. * <p>
  659. * It is called when the contents of the component should be painted in
  660. * response to the component first being shown or having been altered so
  661. * that its visual representation is changed.
  662. * </p>
  663. *
  664. * <p>
  665. * <b>Do not override this to paint your component.</b> Override
  666. * {@link #paintContent(PaintTarget)} instead.
  667. * </p>
  668. *
  669. *
  670. * @param target
  671. * the target UIDL stream where the component should paint itself
  672. * to.
  673. * @throws PaintException
  674. * if the paint operation failed.
  675. */
  676. public void paint(PaintTarget target) throws PaintException {
  677. final String tag = target.getTag(this);
  678. final PaintStatus status = target.startPaintable(this, tag);
  679. if (PaintStatus.DEFER == status) {
  680. // nothing to do but flag as deferred and close the paintable tag
  681. // paint() will be called again later to paint the contents
  682. target.addAttribute("deferred", true);
  683. } else if (PaintStatus.CACHED == status
  684. && !repaintRequestListenersNotified) {
  685. // Contents have not changed, only cached presentation can be used
  686. target.addAttribute("cached", true);
  687. } else {
  688. // Paint the contents of the component
  689. // Only paint content of visible components.
  690. if (isVisible()) {
  691. // width and height are only in shared state
  692. // TODO probably can remove also icon once all the VCaption
  693. // related code has been updated
  694. if (getIcon() != null) {
  695. target.addAttribute(
  696. VAbstractPaintableWidget.ATTRIBUTE_ICON, getIcon());
  697. }
  698. if (eventIdentifiers != null) {
  699. target.addAttribute("eventListeners",
  700. eventIdentifiers.toArray());
  701. }
  702. paintContent(target);
  703. final ErrorMessage error = getErrorMessage();
  704. if (error != null) {
  705. error.paint(target);
  706. }
  707. } else {
  708. target.addAttribute("invisible", true);
  709. }
  710. }
  711. target.endPaintable(this);
  712. repaintRequestListenersNotified = false;
  713. }
  714. /**
  715. * Build CSS compatible string representation of height.
  716. *
  717. * @return CSS height
  718. */
  719. private String getCSSHeight() {
  720. if (getHeightUnits() == Unit.PIXELS) {
  721. return ((int) getHeight()) + getHeightUnits().getSymbol();
  722. } else {
  723. return getHeight() + getHeightUnits().getSymbol();
  724. }
  725. }
  726. /**
  727. * Build CSS compatible string representation of width.
  728. *
  729. * @return CSS width
  730. */
  731. private String getCSSWidth() {
  732. if (getWidthUnits() == Unit.PIXELS) {
  733. return ((int) getWidth()) + getWidthUnits().getSymbol();
  734. } else {
  735. return getWidth() + getWidthUnits().getSymbol();
  736. }
  737. }
  738. /**
  739. * Paints any needed component-specific things to the given UIDL stream. The
  740. * more general {@link #paint(PaintTarget)} method handles all general
  741. * attributes common to all components, and it calls this method to paint
  742. * any component-specific attributes to the UIDL stream.
  743. *
  744. * @param target
  745. * the target UIDL stream where the component should paint itself
  746. * to
  747. * @throws PaintException
  748. * if the paint operation failed.
  749. */
  750. public void paintContent(PaintTarget target) throws PaintException {
  751. }
  752. /**
  753. * Returns the shared state bean with information to be sent from the server
  754. * to the client.
  755. *
  756. * Subclasses should override this method and set any relevant fields of the
  757. * state returned by super.getState().
  758. *
  759. * @since 7.0
  760. *
  761. * @return updated component shared state
  762. */
  763. public ComponentState getState() {
  764. if (null == sharedState) {
  765. sharedState = createState();
  766. }
  767. // basic state: caption, size, enabled, ...
  768. if (!isVisible()) {
  769. return null;
  770. }
  771. // TODO for now, this superclass always recreates the state from
  772. // scratch, whereas subclasses should only modify it
  773. if (getHeight() >= 0
  774. && (getHeightUnits() != Unit.PERCENTAGE || ComponentSizeValidator
  775. .parentCanDefineHeight(this))) {
  776. sharedState.setHeight("" + getCSSHeight());
  777. }
  778. if (getWidth() >= 0
  779. && (getWidthUnits() != Unit.PERCENTAGE || ComponentSizeValidator
  780. .parentCanDefineWidth(this))) {
  781. sharedState.setWidth("" + getCSSWidth());
  782. }
  783. sharedState.setImmediate(isImmediate());
  784. sharedState.setReadOnly(isReadOnly());
  785. sharedState.setDisabled(!isEnabled());
  786. sharedState.setStyle(getStyleName());
  787. sharedState.setCaption(getCaption());
  788. sharedState.setDescription(getDescription());
  789. // TODO icon also in shared state - how to convert Resource?
  790. return sharedState;
  791. }
  792. /**
  793. * Creates the shared state bean to be used in server to client
  794. * communication.
  795. *
  796. * Subclasses should implement this method and return a new instance of the
  797. * correct state class.
  798. *
  799. * All configuration of the values of the state should be performed in
  800. * {@link #getState()}, not in {@link #createState()}.
  801. *
  802. * @since 7.0
  803. *
  804. * @return new shared state object
  805. */
  806. protected ComponentState createState() {
  807. return new ComponentState();
  808. }
  809. /* Documentation copied from interface */
  810. public void requestRepaint() {
  811. // The effect of the repaint request is identical to case where a
  812. // child requests repaint
  813. childRequestedRepaint(null);
  814. }
  815. /* Documentation copied from interface */
  816. public void childRequestedRepaint(
  817. Collection<RepaintRequestListener> alreadyNotified) {
  818. // Invisible components (by flag in this particular component) do not
  819. // need repaints
  820. if (!visible) {
  821. return;
  822. }
  823. fireRequestRepaintEvent(alreadyNotified);
  824. }
  825. /**
  826. * Fires the repaint request event.
  827. *
  828. * @param alreadyNotified
  829. */
  830. private void fireRequestRepaintEvent(
  831. Collection<RepaintRequestListener> alreadyNotified) {
  832. // Notify listeners only once
  833. if (!repaintRequestListenersNotified) {
  834. // Notify the listeners
  835. if (repaintRequestListeners != null
  836. && !repaintRequestListeners.isEmpty()) {
  837. final Object[] listeners = repaintRequestListeners.toArray();
  838. final RepaintRequestEvent event = new RepaintRequestEvent(this);
  839. for (int i = 0; i < listeners.length; i++) {
  840. if (alreadyNotified == null) {
  841. alreadyNotified = new LinkedList<RepaintRequestListener>();
  842. }
  843. if (!alreadyNotified.contains(listeners[i])) {
  844. ((RepaintRequestListener) listeners[i])
  845. .repaintRequested(event);
  846. alreadyNotified
  847. .add((RepaintRequestListener) listeners[i]);
  848. repaintRequestListenersNotified = true;
  849. }
  850. }
  851. }
  852. // Notify the parent
  853. final Component parent = getParent();
  854. if (parent != null) {
  855. parent.childRequestedRepaint(alreadyNotified);
  856. }
  857. }
  858. }
  859. /* Documentation copied from interface */
  860. public void addListener(RepaintRequestListener listener) {
  861. if (repaintRequestListeners == null) {
  862. repaintRequestListeners = new LinkedList<RepaintRequestListener>();
  863. }
  864. if (!repaintRequestListeners.contains(listener)) {
  865. repaintRequestListeners.add(listener);
  866. }
  867. }
  868. /* Documentation copied from interface */
  869. public void removeListener(RepaintRequestListener listener) {
  870. if (repaintRequestListeners != null) {
  871. repaintRequestListeners.remove(listener);
  872. if (repaintRequestListeners.isEmpty()) {
  873. repaintRequestListeners = null;
  874. }
  875. }
  876. }
  877. /* Component variable changes */
  878. /*
  879. * Invoked when the value of a variable has changed. Don't add a JavaDoc
  880. * comment here, we use the default documentation from implemented
  881. * interface.
  882. */
  883. public void changeVariables(Object source, Map<String, Object> variables) {
  884. }
  885. /* General event framework */
  886. private static final Method COMPONENT_EVENT_METHOD = ReflectTools
  887. .findMethod(Component.Listener.class, "componentEvent",
  888. Component.Event.class);
  889. /**
  890. * <p>
  891. * Registers a new listener with the specified activation method to listen
  892. * events generated by this component. If the activation method does not
  893. * have any arguments the event object will not be passed to it when it's
  894. * called.
  895. * </p>
  896. *
  897. * <p>
  898. * This method additionally informs the event-api to route events with the
  899. * given eventIdentifier to the components handleEvent function call.
  900. * </p>
  901. *
  902. * <p>
  903. * For more information on the inheritable event mechanism see the
  904. * {@link com.vaadin.event com.vaadin.event package documentation}.
  905. * </p>
  906. *
  907. * @param eventIdentifier
  908. * the identifier of the event to listen for
  909. * @param eventType
  910. * the type of the listened event. Events of this type or its
  911. * subclasses activate the listener.
  912. * @param target
  913. * the object instance who owns the activation method.
  914. * @param method
  915. * the activation method.
  916. *
  917. * @since 6.2
  918. */
  919. protected void addListener(String eventIdentifier, Class<?> eventType,
  920. Object target, Method method) {
  921. if (eventRouter == null) {
  922. eventRouter = new EventRouter();
  923. }
  924. if (eventIdentifiers == null) {
  925. eventIdentifiers = new HashSet<String>();
  926. }
  927. boolean needRepaint = !eventRouter.hasListeners(eventType);
  928. eventRouter.addListener(eventType, target, method);
  929. if (needRepaint) {
  930. eventIdentifiers.add(eventIdentifier);
  931. requestRepaint();
  932. }
  933. }
  934. /**
  935. * Checks if the given {@link Event} type is listened for this component.
  936. *
  937. * @param eventType
  938. * the event type to be checked
  939. * @return true if a listener is registered for the given event type
  940. */
  941. protected boolean hasListeners(Class<?> eventType) {
  942. return eventRouter != null && eventRouter.hasListeners(eventType);
  943. }
  944. /**
  945. * Removes all registered listeners matching the given parameters. Since
  946. * this method receives the event type and the listener object as
  947. * parameters, it will unregister all <code>object</code>'s methods that are
  948. * registered to listen to events of type <code>eventType</code> generated
  949. * by this component.
  950. *
  951. * <p>
  952. * This method additionally informs the event-api to stop routing events
  953. * with the given eventIdentifier to the components handleEvent function
  954. * call.
  955. * </p>
  956. *
  957. * <p>
  958. * For more information on the inheritable event mechanism see the
  959. * {@link com.vaadin.event com.vaadin.event package documentation}.
  960. * </p>
  961. *
  962. * @param eventIdentifier
  963. * the identifier of the event to stop listening for
  964. * @param eventType
  965. * the exact event type the <code>object</code> listens to.
  966. * @param target
  967. * the target object that has registered to listen to events of
  968. * type <code>eventType</code> with one or more methods.
  969. *
  970. * @since 6.2
  971. */
  972. protected void removeListener(String eventIdentifier, Class<?> eventType,
  973. Object target) {
  974. if (eventRouter != null) {
  975. eventRouter.removeListener(eventType, target);
  976. if (!eventRouter.hasListeners(eventType)) {
  977. eventIdentifiers.remove(eventIdentifier);
  978. requestRepaint();
  979. }
  980. }
  981. }
  982. /**
  983. * <p>
  984. * Registers a new listener with the specified activation method to listen
  985. * events generated by this component. If the activation method does not
  986. * have any arguments the event object will not be passed to it when it's
  987. * called.
  988. * </p>
  989. *
  990. * <p>
  991. * For more information on the inheritable event mechanism see the
  992. * {@link com.vaadin.event com.vaadin.event package documentation}.
  993. * </p>
  994. *
  995. * @param eventType
  996. * the type of the listened event. Events of this type or its
  997. * subclasses activate the listener.
  998. * @param target
  999. * the object instance who owns the activation method.
  1000. * @param method
  1001. * the activation method.
  1002. */
  1003. public void addListener(Class<?> eventType, Object target, Method method) {
  1004. if (eventRouter == null) {
  1005. eventRouter = new EventRouter();
  1006. }
  1007. eventRouter.addListener(eventType, target, method);
  1008. }
  1009. /**
  1010. * <p>
  1011. * Convenience method for registering a new listener with the specified
  1012. * activation method to listen events generated by this component. If the
  1013. * activation method does not have any arguments the event object will not
  1014. * be passed to it when it's called.
  1015. * </p>
  1016. *
  1017. * <p>
  1018. * This version of <code>addListener</code> gets the name of the activation
  1019. * method as a parameter. The actual method is reflected from
  1020. * <code>object</code>, and unless exactly one match is found,
  1021. * <code>java.lang.IllegalArgumentException</code> is thrown.
  1022. * </p>
  1023. *
  1024. * <p>
  1025. * For more information on the inheritable event mechanism see the
  1026. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1027. * </p>
  1028. *
  1029. * <p>
  1030. * Note: Using this method is discouraged because it cannot be checked
  1031. * during compilation. Use {@link #addListener(Class, Object, Method)} or
  1032. * {@link #addListener(com.vaadin.ui.Component.Listener)} instead.
  1033. * </p>
  1034. *
  1035. * @param eventType
  1036. * the type of the listened event. Events of this type or its
  1037. * subclasses activate the listener.
  1038. * @param target
  1039. * the object instance who owns the activation method.
  1040. * @param methodName
  1041. * the name of the activation method.
  1042. */
  1043. public void addListener(Class<?> eventType, Object target, String methodName) {
  1044. if (eventRouter == null) {
  1045. eventRouter = new EventRouter();
  1046. }
  1047. eventRouter.addListener(eventType, target, methodName);
  1048. }
  1049. /**
  1050. * Removes all registered listeners matching the given parameters. Since
  1051. * this method receives the event type and the listener object as
  1052. * parameters, it will unregister all <code>object</code>'s methods that are
  1053. * registered to listen to events of type <code>eventType</code> generated
  1054. * by this component.
  1055. *
  1056. * <p>
  1057. * For more information on the inheritable event mechanism see the
  1058. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1059. * </p>
  1060. *
  1061. * @param eventType
  1062. * the exact event type the <code>object</code> listens to.
  1063. * @param target
  1064. * the target object that has registered to listen to events of
  1065. * type <code>eventType</code> with one or more methods.
  1066. */
  1067. public void removeListener(Class<?> eventType, Object target) {
  1068. if (eventRouter != null) {
  1069. eventRouter.removeListener(eventType, target);
  1070. }
  1071. }
  1072. /**
  1073. * Removes one registered listener method. The given method owned by the
  1074. * given object will no longer be called when the specified events are
  1075. * generated by this component.
  1076. *
  1077. * <p>
  1078. * For more information on the inheritable event mechanism see the
  1079. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1080. * </p>
  1081. *
  1082. * @param eventType
  1083. * the exact event type the <code>object</code> listens to.
  1084. * @param target
  1085. * target object that has registered to listen to events of type
  1086. * <code>eventType</code> with one or more methods.
  1087. * @param method
  1088. * the method owned by <code>target</code> that's registered to
  1089. * listen to events of type <code>eventType</code>.
  1090. */
  1091. public void removeListener(Class<?> eventType, Object target, Method method) {
  1092. if (eventRouter != null) {
  1093. eventRouter.removeListener(eventType, target, method);
  1094. }
  1095. }
  1096. /**
  1097. * <p>
  1098. * Removes one registered listener method. The given method owned by the
  1099. * given object will no longer be called when the specified events are
  1100. * generated by this component.
  1101. * </p>
  1102. *
  1103. * <p>
  1104. * This version of <code>removeListener</code> gets the name of the
  1105. * activation method as a parameter. The actual method is reflected from
  1106. * <code>target</code>, and unless exactly one match is found,
  1107. * <code>java.lang.IllegalArgumentException</code> is thrown.
  1108. * </p>
  1109. *
  1110. * <p>
  1111. * For more information on the inheritable event mechanism see the
  1112. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1113. * </p>
  1114. *
  1115. * @param eventType
  1116. * the exact event type the <code>object</code> listens to.
  1117. * @param target
  1118. * the target object that has registered to listen to events of
  1119. * type <code>eventType</code> with one or more methods.
  1120. * @param methodName
  1121. * the name of the method owned by <code>target</code> that's
  1122. * registered to listen to events of type <code>eventType</code>.
  1123. */
  1124. public void removeListener(Class<?> eventType, Object target,
  1125. String methodName) {
  1126. if (eventRouter != null) {
  1127. eventRouter.removeListener(eventType, target, methodName);
  1128. }
  1129. }
  1130. /**
  1131. * Returns all listeners that are registered for the given event type or one
  1132. * of its subclasses.
  1133. *
  1134. * @param eventType
  1135. * The type of event to return listeners for.
  1136. * @return A collection with all registered listeners. Empty if no listeners
  1137. * are found.
  1138. */
  1139. public Collection<?> getListeners(Class<?> eventType) {
  1140. if (eventType.isAssignableFrom(RepaintRequestEvent.class)) {
  1141. // RepaintRequestListeners are not stored in eventRouter
  1142. if (repaintRequestListeners == null) {
  1143. return Collections.EMPTY_LIST;
  1144. } else {
  1145. return Collections
  1146. .unmodifiableCollection(repaintRequestListeners);
  1147. }
  1148. }
  1149. if (eventRouter == null) {
  1150. return Collections.EMPTY_LIST;
  1151. }
  1152. return eventRouter.getListeners(eventType);
  1153. }
  1154. /**
  1155. * Sends the event to all listeners.
  1156. *
  1157. * @param event
  1158. * the Event to be sent to all listeners.
  1159. */
  1160. protected void fireEvent(Component.Event event) {
  1161. if (eventRouter != null) {
  1162. eventRouter.fireEvent(event);
  1163. }
  1164. }
  1165. /* Component event framework */
  1166. /*
  1167. * Registers a new listener to listen events generated by this component.
  1168. * Don't add a JavaDoc comment here, we use the default documentation from
  1169. * implemented interface.
  1170. */
  1171. public void addListener(Component.Listener listener) {
  1172. addListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
  1173. }
  1174. /*
  1175. * Removes a previously registered listener from this component. Don't add a
  1176. * JavaDoc comment here, we use the default documentation from implemented
  1177. * interface.
  1178. */
  1179. public void removeListener(Component.Listener listener) {
  1180. removeListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
  1181. }
  1182. /**
  1183. * Emits the component event. It is transmitted to all registered listeners
  1184. * interested in such events.
  1185. */
  1186. protected void fireComponentEvent() {
  1187. fireEvent(new Component.Event(this));
  1188. }
  1189. /**
  1190. * Emits the component error event. It is transmitted to all registered
  1191. * listeners interested in such events.
  1192. */
  1193. protected void fireComponentErrorEvent() {
  1194. fireEvent(new Component.ErrorEvent(getComponentError(), this));
  1195. }
  1196. /**
  1197. * Sets the data object, that can be used for any application specific data.
  1198. * The component does not use or modify this data.
  1199. *
  1200. * @param data
  1201. * the Application specific data.
  1202. * @since 3.1
  1203. */
  1204. public void setData(Object data) {
  1205. applicationData = data;
  1206. }
  1207. /**
  1208. * Gets the application specific data. See {@link #setData(Object)}.
  1209. *
  1210. * @return the Application specific data set with setData function.
  1211. * @since 3.1
  1212. */
  1213. public Object getData() {
  1214. return applicationData;
  1215. }
  1216. /* Sizeable and other size related methods */
  1217. /*
  1218. * (non-Javadoc)
  1219. *
  1220. * @see com.vaadin.terminal.Sizeable#getHeight()
  1221. */
  1222. public float getHeight() {
  1223. return height;
  1224. }
  1225. /*
  1226. * (non-Javadoc)
  1227. *
  1228. * @see com.vaadin.terminal.Sizeable#getHeightUnits()
  1229. */
  1230. public Unit getHeightUnits() {
  1231. return heightUnit;
  1232. }
  1233. /*
  1234. * (non-Javadoc)
  1235. *
  1236. * @see com.vaadin.terminal.Sizeable#getWidth()
  1237. */
  1238. public float getWidth() {
  1239. return width;
  1240. }
  1241. /*
  1242. * (non-Javadoc)
  1243. *
  1244. * @see com.vaadin.terminal.Sizeable#getWidthUnits()
  1245. */
  1246. public Unit getWidthUnits() {
  1247. return widthUnit;
  1248. }
  1249. /*
  1250. * (non-Javadoc)
  1251. *
  1252. * @see com.vaadin.terminal.Sizeable#setHeight(float, Unit)
  1253. */
  1254. public void setHeight(float height, Unit unit) {
  1255. if (unit == null) {
  1256. throw new IllegalArgumentException("Unit can not be null");
  1257. }
  1258. this.height = height;
  1259. heightUnit = unit;
  1260. requestRepaint();
  1261. // ComponentSizeValidator.setHeightLocation(this);
  1262. }
  1263. /*
  1264. * (non-Javadoc)
  1265. *
  1266. * @see com.vaadin.terminal.Sizeable#setSizeFull()
  1267. */
  1268. public void setSizeFull() {
  1269. setWidth(100, Unit.PERCENTAGE);
  1270. setHeight(100, Unit.PERCENTAGE);
  1271. }
  1272. /*
  1273. * (non-Javadoc)
  1274. *
  1275. * @see com.vaadin.terminal.Sizeable#setSizeUndefined()
  1276. */
  1277. public void setSizeUndefined() {
  1278. setWidth(-1, Unit.PIXELS);
  1279. setHeight(-1, Unit.PIXELS);
  1280. }
  1281. /*
  1282. * (non-Javadoc)
  1283. *
  1284. * @see com.vaadin.terminal.Sizeable#setWidth(float, Unit)
  1285. */
  1286. public void setWidth(float width, Unit unit) {
  1287. if (unit == null) {
  1288. throw new IllegalArgumentException("Unit can not be null");
  1289. }
  1290. this.width = width;
  1291. widthUnit = unit;
  1292. requestRepaint();
  1293. // ComponentSizeValidator.setWidthLocation(this);
  1294. }
  1295. /*
  1296. * (non-Javadoc)
  1297. *
  1298. * @see com.vaadin.terminal.Sizeable#setWidth(java.lang.String)
  1299. */
  1300. public void setWidth(String width) {
  1301. Size size = parseStringSize(width);
  1302. if (size != null) {
  1303. setWidth(size.getSize(), size.getUnit());
  1304. } else {
  1305. setWidth(-1, Unit.PIXELS);
  1306. }
  1307. }
  1308. /*
  1309. * (non-Javadoc)
  1310. *
  1311. * @see com.vaadin.terminal.Sizeable#setHeight(java.lang.String)
  1312. */
  1313. public void setHeight(String height) {
  1314. Size size = parseStringSize(height);
  1315. if (size != null) {
  1316. setHeight(size.getSize(), size.getUnit());
  1317. } else {
  1318. setHeight(-1, Unit.PIXELS);
  1319. }
  1320. }
  1321. /*
  1322. * Returns array with size in index 0 unit in index 1. Null or empty string
  1323. * will produce {-1,Unit#PIXELS}
  1324. */
  1325. private static Size parseStringSize(String s) {
  1326. if (s == null) {
  1327. return null;
  1328. }
  1329. s = s.trim();
  1330. if ("".equals(s)) {
  1331. return null;
  1332. }
  1333. float size = 0;
  1334. Unit unit = null;
  1335. Matcher matcher = sizePattern.matcher(s);
  1336. if (matcher.find()) {
  1337. size = Float.parseFloat(matcher.group(1));
  1338. if (size < 0) {
  1339. size = -1;
  1340. unit = Unit.PIXELS;
  1341. } else {
  1342. String symbol = matcher.group(3);
  1343. unit = Unit.getUnitFromSymbol(symbol);
  1344. }
  1345. } else {
  1346. throw new IllegalArgumentException("Invalid size argument: \"" + s
  1347. + "\" (should match " + sizePattern.pattern() + ")");
  1348. }
  1349. return new Size(size, unit);
  1350. }
  1351. private static class Size implements Serializable {
  1352. float size;
  1353. Unit unit;
  1354. public Size(float size, Unit unit) {
  1355. this.size = size;
  1356. this.unit = unit;
  1357. }
  1358. public float getSize() {
  1359. return size;
  1360. }
  1361. public Unit getUnit() {
  1362. return unit;
  1363. }
  1364. }
  1365. public interface ComponentErrorEvent extends Terminal.ErrorEvent {
  1366. }
  1367. public interface ComponentErrorHandler extends Serializable {
  1368. /**
  1369. * Handle the component error
  1370. *
  1371. * @param event
  1372. * @return True if the error has been handled False, otherwise
  1373. */
  1374. public boolean handleComponentError(ComponentErrorEvent event);
  1375. }
  1376. /**
  1377. * Gets the error handler for the component.
  1378. *
  1379. * The error handler is dispatched whenever there is an error processing the
  1380. * data coming from the client.
  1381. *
  1382. * @return
  1383. */
  1384. public ComponentErrorHandler getErrorHandler() {
  1385. return errorHandler;
  1386. }
  1387. /**
  1388. * Sets the error handler for the component.
  1389. *
  1390. * The error handler is dispatched whenever there is an error processing the
  1391. * data coming from the client.
  1392. *
  1393. * If the error handler is not set, the application error handler is used to
  1394. * handle the exception.
  1395. *
  1396. * @param errorHandler
  1397. * AbstractField specific error handler
  1398. */
  1399. public void setErrorHandler(ComponentErrorHandler errorHandler) {
  1400. this.errorHandler = errorHandler;
  1401. }
  1402. /**
  1403. * Handle the component error event.
  1404. *
  1405. * @param error
  1406. * Error event to handle
  1407. * @return True if the error has been handled False, otherwise. If the error
  1408. * haven't been handled by this component, it will be handled in the
  1409. * application error handler.
  1410. */
  1411. public boolean handleError(ComponentErrorEvent error) {
  1412. if (errorHandler != null) {
  1413. return errorHandler.handleComponentError(error);
  1414. }
  1415. return false;
  1416. }
  1417. /*
  1418. * Actions
  1419. */
  1420. /**
  1421. * Gets the {@link ActionManager} used to manage the
  1422. * {@link ShortcutListener}s added to this {@link Field}.
  1423. *
  1424. * @return the ActionManager in use
  1425. */
  1426. protected ActionManager getActionManager() {
  1427. if (actionManager == null) {
  1428. actionManager = new ActionManager();
  1429. if (getRoot() != null) {
  1430. actionManager.setViewer(getRoot());
  1431. }
  1432. }
  1433. return actionManager;
  1434. }
  1435. public void addShortcutListener(ShortcutListener shortcut) {
  1436. getActionManager().addAction(shortcut);
  1437. }
  1438. public void removeShortcutListener(ShortcutListener shortcut) {
  1439. if (actionManager != null) {
  1440. actionManager.removeAction(shortcut);
  1441. }
  1442. }
  1443. /**
  1444. * Registers an RPC interface implementation for this component.
  1445. *
  1446. * A component can listen to multiple RPC interfaces, and subclasses can
  1447. * register additional implementations.
  1448. *
  1449. * @since 7.0
  1450. *
  1451. * @param implementation
  1452. * RPC interface implementation
  1453. * @param rpcInterfaceType
  1454. * RPC interface class for which the implementation should be
  1455. * registered
  1456. */
  1457. protected <T> void registerRpcImplementation(T implementation,
  1458. Class<T> rpcInterfaceType) {
  1459. if (this instanceof RpcTarget) {
  1460. rpcManagerMap.put(rpcInterfaceType, new ServerRpcManager<T>(
  1461. (RpcTarget) this, implementation, rpcInterfaceType));
  1462. } else {
  1463. throw new RuntimeException(
  1464. "Cannot register an RPC implementation for a component that is not an RpcTarget");
  1465. }
  1466. }
  1467. public RpcManager getRpcManager(Class<?> rpcInterface) {
  1468. return rpcManagerMap.get(rpcInterface);
  1469. }
  1470. }