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 51KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676
  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. if (styles != null && styles.size() > 0) {
  693. target.addAttribute(
  694. VAbstractPaintableWidget.ATTRIBUTE_STYLE,
  695. getStyle());
  696. }
  697. if (isReadOnly()) {
  698. target.addAttribute(
  699. VAbstractPaintableWidget.ATTRIBUTE_READONLY, true);
  700. }
  701. if (isImmediate()) {
  702. target.addAttribute(
  703. VAbstractPaintableWidget.ATTRIBUTE_IMMEDIATE, true);
  704. }
  705. if (!isEnabled()) {
  706. target.addAttribute(
  707. VAbstractPaintableWidget.ATTRIBUTE_DISABLED, true);
  708. }
  709. if (getCaption() != null) {
  710. target.addAttribute(
  711. VAbstractPaintableWidget.ATTRIBUTE_CAPTION,
  712. getCaption());
  713. }
  714. if (getIcon() != null) {
  715. target.addAttribute(
  716. VAbstractPaintableWidget.ATTRIBUTE_ICON, getIcon());
  717. }
  718. if (getDescription() != null && getDescription().length() > 0) {
  719. target.addAttribute(
  720. VAbstractPaintableWidget.ATTRIBUTE_DESCRIPTION,
  721. getDescription());
  722. }
  723. if (eventIdentifiers != null) {
  724. target.addAttribute("eventListeners",
  725. eventIdentifiers.toArray());
  726. }
  727. paintContent(target);
  728. final ErrorMessage error = getErrorMessage();
  729. if (error != null) {
  730. error.paint(target);
  731. }
  732. } else {
  733. target.addAttribute("invisible", true);
  734. }
  735. }
  736. target.endPaintable(this);
  737. repaintRequestListenersNotified = false;
  738. }
  739. /**
  740. * Build CSS compatible string representation of height.
  741. *
  742. * @return CSS height
  743. */
  744. private String getCSSHeight() {
  745. if (getHeightUnits() == Unit.PIXELS) {
  746. return ((int) getHeight()) + getHeightUnits().getSymbol();
  747. } else {
  748. return getHeight() + getHeightUnits().getSymbol();
  749. }
  750. }
  751. /**
  752. * Build CSS compatible string representation of width.
  753. *
  754. * @return CSS width
  755. */
  756. private String getCSSWidth() {
  757. if (getWidthUnits() == Unit.PIXELS) {
  758. return ((int) getWidth()) + getWidthUnits().getSymbol();
  759. } else {
  760. return getWidth() + getWidthUnits().getSymbol();
  761. }
  762. }
  763. /**
  764. * Paints any needed component-specific things to the given UIDL stream. The
  765. * more general {@link #paint(PaintTarget)} method handles all general
  766. * attributes common to all components, and it calls this method to paint
  767. * any component-specific attributes to the UIDL stream.
  768. *
  769. * @param target
  770. * the target UIDL stream where the component should paint itself
  771. * to
  772. * @throws PaintException
  773. * if the paint operation failed.
  774. */
  775. public void paintContent(PaintTarget target) throws PaintException {
  776. }
  777. /**
  778. * Returns the shared state bean with information to be sent from the server
  779. * to the client.
  780. *
  781. * Subclasses should override this method and set any relevant fields of the
  782. * state returned by super.getState().
  783. *
  784. * @since 7.0
  785. *
  786. * @return updated component shared state
  787. */
  788. public ComponentState getState() {
  789. if (null == sharedState) {
  790. sharedState = createState();
  791. }
  792. // basic state: caption, size, enabled, ...
  793. if (!isVisible()) {
  794. return null;
  795. }
  796. // TODO for now, this superclass always recreates the state from
  797. // scratch, whereas subclasses should only modify it
  798. // Map<String, Object> state = new HashMap<String, Object>();
  799. if (getHeight() >= 0
  800. && (getHeightUnits() != Unit.PERCENTAGE || ComponentSizeValidator
  801. .parentCanDefineHeight(this))) {
  802. sharedState.setHeight("" + getCSSHeight());
  803. }
  804. if (getWidth() >= 0
  805. && (getWidthUnits() != Unit.PERCENTAGE || ComponentSizeValidator
  806. .parentCanDefineWidth(this))) {
  807. sharedState.setWidth("" + getCSSWidth());
  808. }
  809. // if (getCaption() != null) {
  810. // state.put(ComponentState.STATE_CAPTION, getCaption());
  811. // }
  812. // TODO use the rest on the client side
  813. // if (styles != null && styles.size() > 0) {
  814. // state.put(ComponentState.STATE_STYLE, getStyle());
  815. // }
  816. // if (isReadOnly()) {
  817. // state.put(ComponentState.STATE_READONLY, true);
  818. // }
  819. //
  820. // if (isImmediate()) {
  821. // state.put(ComponentState.STATE_IMMEDIATE, true);
  822. // }
  823. // if (!isEnabled()) {
  824. // state.put(ComponentState.STATE_DISABLED, true);
  825. // }
  826. // // TODO add icon (Resource)
  827. // if (getDescription() != null && getDescription().length() > 0) {
  828. // state.put(ComponentState.STATE_DESCRIPTION, getDescription());
  829. // }
  830. //
  831. // sharedState.setState(state);
  832. return sharedState;
  833. }
  834. /**
  835. * Creates the shared state bean to be used in server to client
  836. * communication.
  837. *
  838. * Subclasses should implement this method and return a new instance of the
  839. * correct state class.
  840. *
  841. * All configuration of the values of the state should be performed in
  842. * {@link #getState()}, not in {@link #createState()}.
  843. *
  844. * @since 7.0
  845. *
  846. * @return new shared state object
  847. */
  848. protected ComponentState createState() {
  849. return new ComponentState();
  850. }
  851. /* Documentation copied from interface */
  852. public void requestRepaint() {
  853. // The effect of the repaint request is identical to case where a
  854. // child requests repaint
  855. childRequestedRepaint(null);
  856. }
  857. /* Documentation copied from interface */
  858. public void childRequestedRepaint(
  859. Collection<RepaintRequestListener> alreadyNotified) {
  860. // Invisible components (by flag in this particular component) do not
  861. // need repaints
  862. if (!visible) {
  863. return;
  864. }
  865. fireRequestRepaintEvent(alreadyNotified);
  866. }
  867. /**
  868. * Fires the repaint request event.
  869. *
  870. * @param alreadyNotified
  871. */
  872. private void fireRequestRepaintEvent(
  873. Collection<RepaintRequestListener> alreadyNotified) {
  874. // Notify listeners only once
  875. if (!repaintRequestListenersNotified) {
  876. // Notify the listeners
  877. if (repaintRequestListeners != null
  878. && !repaintRequestListeners.isEmpty()) {
  879. final Object[] listeners = repaintRequestListeners.toArray();
  880. final RepaintRequestEvent event = new RepaintRequestEvent(this);
  881. for (int i = 0; i < listeners.length; i++) {
  882. if (alreadyNotified == null) {
  883. alreadyNotified = new LinkedList<RepaintRequestListener>();
  884. }
  885. if (!alreadyNotified.contains(listeners[i])) {
  886. ((RepaintRequestListener) listeners[i])
  887. .repaintRequested(event);
  888. alreadyNotified
  889. .add((RepaintRequestListener) listeners[i]);
  890. repaintRequestListenersNotified = true;
  891. }
  892. }
  893. }
  894. // Notify the parent
  895. final Component parent = getParent();
  896. if (parent != null) {
  897. parent.childRequestedRepaint(alreadyNotified);
  898. }
  899. }
  900. }
  901. /* Documentation copied from interface */
  902. public void addListener(RepaintRequestListener listener) {
  903. if (repaintRequestListeners == null) {
  904. repaintRequestListeners = new LinkedList<RepaintRequestListener>();
  905. }
  906. if (!repaintRequestListeners.contains(listener)) {
  907. repaintRequestListeners.add(listener);
  908. }
  909. }
  910. /* Documentation copied from interface */
  911. public void removeListener(RepaintRequestListener listener) {
  912. if (repaintRequestListeners != null) {
  913. repaintRequestListeners.remove(listener);
  914. if (repaintRequestListeners.isEmpty()) {
  915. repaintRequestListeners = null;
  916. }
  917. }
  918. }
  919. /* Component variable changes */
  920. /*
  921. * Invoked when the value of a variable has changed. Don't add a JavaDoc
  922. * comment here, we use the default documentation from implemented
  923. * interface.
  924. */
  925. public void changeVariables(Object source, Map<String, Object> variables) {
  926. }
  927. /* General event framework */
  928. private static final Method COMPONENT_EVENT_METHOD = ReflectTools
  929. .findMethod(Component.Listener.class, "componentEvent",
  930. Component.Event.class);
  931. /**
  932. * <p>
  933. * Registers a new listener with the specified activation method to listen
  934. * events generated by this component. If the activation method does not
  935. * have any arguments the event object will not be passed to it when it's
  936. * called.
  937. * </p>
  938. *
  939. * <p>
  940. * This method additionally informs the event-api to route events with the
  941. * given eventIdentifier to the components handleEvent function call.
  942. * </p>
  943. *
  944. * <p>
  945. * For more information on the inheritable event mechanism see the
  946. * {@link com.vaadin.event com.vaadin.event package documentation}.
  947. * </p>
  948. *
  949. * @param eventIdentifier
  950. * the identifier of the event to listen for
  951. * @param eventType
  952. * the type of the listened event. Events of this type or its
  953. * subclasses activate the listener.
  954. * @param target
  955. * the object instance who owns the activation method.
  956. * @param method
  957. * the activation method.
  958. *
  959. * @since 6.2
  960. */
  961. protected void addListener(String eventIdentifier, Class<?> eventType,
  962. Object target, Method method) {
  963. if (eventRouter == null) {
  964. eventRouter = new EventRouter();
  965. }
  966. if (eventIdentifiers == null) {
  967. eventIdentifiers = new HashSet<String>();
  968. }
  969. boolean needRepaint = !eventRouter.hasListeners(eventType);
  970. eventRouter.addListener(eventType, target, method);
  971. if (needRepaint) {
  972. eventIdentifiers.add(eventIdentifier);
  973. requestRepaint();
  974. }
  975. }
  976. /**
  977. * Checks if the given {@link Event} type is listened for this component.
  978. *
  979. * @param eventType
  980. * the event type to be checked
  981. * @return true if a listener is registered for the given event type
  982. */
  983. protected boolean hasListeners(Class<?> eventType) {
  984. return eventRouter != null && eventRouter.hasListeners(eventType);
  985. }
  986. /**
  987. * Removes all registered listeners matching the given parameters. Since
  988. * this method receives the event type and the listener object as
  989. * parameters, it will unregister all <code>object</code>'s methods that are
  990. * registered to listen to events of type <code>eventType</code> generated
  991. * by this component.
  992. *
  993. * <p>
  994. * This method additionally informs the event-api to stop routing events
  995. * with the given eventIdentifier to the components handleEvent function
  996. * call.
  997. * </p>
  998. *
  999. * <p>
  1000. * For more information on the inheritable event mechanism see the
  1001. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1002. * </p>
  1003. *
  1004. * @param eventIdentifier
  1005. * the identifier of the event to stop listening for
  1006. * @param eventType
  1007. * the exact event type the <code>object</code> listens to.
  1008. * @param target
  1009. * the target object that has registered to listen to events of
  1010. * type <code>eventType</code> with one or more methods.
  1011. *
  1012. * @since 6.2
  1013. */
  1014. protected void removeListener(String eventIdentifier, Class<?> eventType,
  1015. Object target) {
  1016. if (eventRouter != null) {
  1017. eventRouter.removeListener(eventType, target);
  1018. if (!eventRouter.hasListeners(eventType)) {
  1019. eventIdentifiers.remove(eventIdentifier);
  1020. requestRepaint();
  1021. }
  1022. }
  1023. }
  1024. /**
  1025. * <p>
  1026. * Registers a new listener with the specified activation method to listen
  1027. * events generated by this component. If the activation method does not
  1028. * have any arguments the event object will not be passed to it when it's
  1029. * called.
  1030. * </p>
  1031. *
  1032. * <p>
  1033. * For more information on the inheritable event mechanism see the
  1034. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1035. * </p>
  1036. *
  1037. * @param eventType
  1038. * the type of the listened event. Events of this type or its
  1039. * subclasses activate the listener.
  1040. * @param target
  1041. * the object instance who owns the activation method.
  1042. * @param method
  1043. * the activation method.
  1044. */
  1045. public void addListener(Class<?> eventType, Object target, Method method) {
  1046. if (eventRouter == null) {
  1047. eventRouter = new EventRouter();
  1048. }
  1049. eventRouter.addListener(eventType, target, method);
  1050. }
  1051. /**
  1052. * <p>
  1053. * Convenience method for registering a new listener with the specified
  1054. * activation method to listen events generated by this component. If the
  1055. * activation method does not have any arguments the event object will not
  1056. * be passed to it when it's called.
  1057. * </p>
  1058. *
  1059. * <p>
  1060. * This version of <code>addListener</code> gets the name of the activation
  1061. * method as a parameter. The actual method is reflected from
  1062. * <code>object</code>, and unless exactly one match is found,
  1063. * <code>java.lang.IllegalArgumentException</code> is thrown.
  1064. * </p>
  1065. *
  1066. * <p>
  1067. * For more information on the inheritable event mechanism see the
  1068. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1069. * </p>
  1070. *
  1071. * <p>
  1072. * Note: Using this method is discouraged because it cannot be checked
  1073. * during compilation. Use {@link #addListener(Class, Object, Method)} or
  1074. * {@link #addListener(com.vaadin.ui.Component.Listener)} instead.
  1075. * </p>
  1076. *
  1077. * @param eventType
  1078. * the type of the listened event. Events of this type or its
  1079. * subclasses activate the listener.
  1080. * @param target
  1081. * the object instance who owns the activation method.
  1082. * @param methodName
  1083. * the name of the activation method.
  1084. */
  1085. public void addListener(Class<?> eventType, Object target, String methodName) {
  1086. if (eventRouter == null) {
  1087. eventRouter = new EventRouter();
  1088. }
  1089. eventRouter.addListener(eventType, target, methodName);
  1090. }
  1091. /**
  1092. * Removes all registered listeners matching the given parameters. Since
  1093. * this method receives the event type and the listener object as
  1094. * parameters, it will unregister all <code>object</code>'s methods that are
  1095. * registered to listen to events of type <code>eventType</code> generated
  1096. * by this component.
  1097. *
  1098. * <p>
  1099. * For more information on the inheritable event mechanism see the
  1100. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1101. * </p>
  1102. *
  1103. * @param eventType
  1104. * the exact event type the <code>object</code> listens to.
  1105. * @param target
  1106. * the target object that has registered to listen to events of
  1107. * type <code>eventType</code> with one or more methods.
  1108. */
  1109. public void removeListener(Class<?> eventType, Object target) {
  1110. if (eventRouter != null) {
  1111. eventRouter.removeListener(eventType, target);
  1112. }
  1113. }
  1114. /**
  1115. * Removes one registered listener method. The given method owned by the
  1116. * given object will no longer be called when the specified events are
  1117. * generated by this component.
  1118. *
  1119. * <p>
  1120. * For more information on the inheritable event mechanism see the
  1121. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1122. * </p>
  1123. *
  1124. * @param eventType
  1125. * the exact event type the <code>object</code> listens to.
  1126. * @param target
  1127. * target object that has registered to listen to events of type
  1128. * <code>eventType</code> with one or more methods.
  1129. * @param method
  1130. * the method owned by <code>target</code> that's registered to
  1131. * listen to events of type <code>eventType</code>.
  1132. */
  1133. public void removeListener(Class<?> eventType, Object target, Method method) {
  1134. if (eventRouter != null) {
  1135. eventRouter.removeListener(eventType, target, method);
  1136. }
  1137. }
  1138. /**
  1139. * <p>
  1140. * Removes one registered listener method. The given method owned by the
  1141. * given object will no longer be called when the specified events are
  1142. * generated by this component.
  1143. * </p>
  1144. *
  1145. * <p>
  1146. * This version of <code>removeListener</code> gets the name of the
  1147. * activation method as a parameter. The actual method is reflected from
  1148. * <code>target</code>, and unless exactly one match is found,
  1149. * <code>java.lang.IllegalArgumentException</code> is thrown.
  1150. * </p>
  1151. *
  1152. * <p>
  1153. * For more information on the inheritable event mechanism see the
  1154. * {@link com.vaadin.event com.vaadin.event package documentation}.
  1155. * </p>
  1156. *
  1157. * @param eventType
  1158. * the exact event type the <code>object</code> listens to.
  1159. * @param target
  1160. * the target object that has registered to listen to events of
  1161. * type <code>eventType</code> with one or more methods.
  1162. * @param methodName
  1163. * the name of the method owned by <code>target</code> that's
  1164. * registered to listen to events of type <code>eventType</code>.
  1165. */
  1166. public void removeListener(Class<?> eventType, Object target,
  1167. String methodName) {
  1168. if (eventRouter != null) {
  1169. eventRouter.removeListener(eventType, target, methodName);
  1170. }
  1171. }
  1172. /**
  1173. * Returns all listeners that are registered for the given event type or one
  1174. * of its subclasses.
  1175. *
  1176. * @param eventType
  1177. * The type of event to return listeners for.
  1178. * @return A collection with all registered listeners. Empty if no listeners
  1179. * are found.
  1180. */
  1181. public Collection<?> getListeners(Class<?> eventType) {
  1182. if (eventType.isAssignableFrom(RepaintRequestEvent.class)) {
  1183. // RepaintRequestListeners are not stored in eventRouter
  1184. if (repaintRequestListeners == null) {
  1185. return Collections.EMPTY_LIST;
  1186. } else {
  1187. return Collections
  1188. .unmodifiableCollection(repaintRequestListeners);
  1189. }
  1190. }
  1191. if (eventRouter == null) {
  1192. return Collections.EMPTY_LIST;
  1193. }
  1194. return eventRouter.getListeners(eventType);
  1195. }
  1196. /**
  1197. * Sends the event to all listeners.
  1198. *
  1199. * @param event
  1200. * the Event to be sent to all listeners.
  1201. */
  1202. protected void fireEvent(Component.Event event) {
  1203. if (eventRouter != null) {
  1204. eventRouter.fireEvent(event);
  1205. }
  1206. }
  1207. /* Component event framework */
  1208. /*
  1209. * Registers a new listener to listen events generated by this component.
  1210. * Don't add a JavaDoc comment here, we use the default documentation from
  1211. * implemented interface.
  1212. */
  1213. public void addListener(Component.Listener listener) {
  1214. addListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
  1215. }
  1216. /*
  1217. * Removes a previously registered listener from this component. Don't add a
  1218. * JavaDoc comment here, we use the default documentation from implemented
  1219. * interface.
  1220. */
  1221. public void removeListener(Component.Listener listener) {
  1222. removeListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
  1223. }
  1224. /**
  1225. * Emits the component event. It is transmitted to all registered listeners
  1226. * interested in such events.
  1227. */
  1228. protected void fireComponentEvent() {
  1229. fireEvent(new Component.Event(this));
  1230. }
  1231. /**
  1232. * Emits the component error event. It is transmitted to all registered
  1233. * listeners interested in such events.
  1234. */
  1235. protected void fireComponentErrorEvent() {
  1236. fireEvent(new Component.ErrorEvent(getComponentError(), this));
  1237. }
  1238. /**
  1239. * Sets the data object, that can be used for any application specific data.
  1240. * The component does not use or modify this data.
  1241. *
  1242. * @param data
  1243. * the Application specific data.
  1244. * @since 3.1
  1245. */
  1246. public void setData(Object data) {
  1247. applicationData = data;
  1248. }
  1249. /**
  1250. * Gets the application specific data. See {@link #setData(Object)}.
  1251. *
  1252. * @return the Application specific data set with setData function.
  1253. * @since 3.1
  1254. */
  1255. public Object getData() {
  1256. return applicationData;
  1257. }
  1258. /* Sizeable and other size related methods */
  1259. /*
  1260. * (non-Javadoc)
  1261. *
  1262. * @see com.vaadin.terminal.Sizeable#getHeight()
  1263. */
  1264. public float getHeight() {
  1265. return height;
  1266. }
  1267. /*
  1268. * (non-Javadoc)
  1269. *
  1270. * @see com.vaadin.terminal.Sizeable#getHeightUnits()
  1271. */
  1272. public Unit getHeightUnits() {
  1273. return heightUnit;
  1274. }
  1275. /*
  1276. * (non-Javadoc)
  1277. *
  1278. * @see com.vaadin.terminal.Sizeable#getWidth()
  1279. */
  1280. public float getWidth() {
  1281. return width;
  1282. }
  1283. /*
  1284. * (non-Javadoc)
  1285. *
  1286. * @see com.vaadin.terminal.Sizeable#getWidthUnits()
  1287. */
  1288. public Unit getWidthUnits() {
  1289. return widthUnit;
  1290. }
  1291. /*
  1292. * (non-Javadoc)
  1293. *
  1294. * @see com.vaadin.terminal.Sizeable#setHeight(float, Unit)
  1295. */
  1296. public void setHeight(float height, Unit unit) {
  1297. if (unit == null) {
  1298. throw new IllegalArgumentException("Unit can not be null");
  1299. }
  1300. this.height = height;
  1301. heightUnit = unit;
  1302. requestRepaint();
  1303. // ComponentSizeValidator.setHeightLocation(this);
  1304. }
  1305. /*
  1306. * (non-Javadoc)
  1307. *
  1308. * @see com.vaadin.terminal.Sizeable#setSizeFull()
  1309. */
  1310. public void setSizeFull() {
  1311. setWidth(100, Unit.PERCENTAGE);
  1312. setHeight(100, Unit.PERCENTAGE);
  1313. }
  1314. /*
  1315. * (non-Javadoc)
  1316. *
  1317. * @see com.vaadin.terminal.Sizeable#setSizeUndefined()
  1318. */
  1319. public void setSizeUndefined() {
  1320. setWidth(-1, Unit.PIXELS);
  1321. setHeight(-1, Unit.PIXELS);
  1322. }
  1323. /*
  1324. * (non-Javadoc)
  1325. *
  1326. * @see com.vaadin.terminal.Sizeable#setWidth(float, Unit)
  1327. */
  1328. public void setWidth(float width, Unit unit) {
  1329. if (unit == null) {
  1330. throw new IllegalArgumentException("Unit can not be null");
  1331. }
  1332. this.width = width;
  1333. widthUnit = unit;
  1334. requestRepaint();
  1335. // ComponentSizeValidator.setWidthLocation(this);
  1336. }
  1337. /*
  1338. * (non-Javadoc)
  1339. *
  1340. * @see com.vaadin.terminal.Sizeable#setWidth(java.lang.String)
  1341. */
  1342. public void setWidth(String width) {
  1343. Size size = parseStringSize(width);
  1344. if (size != null) {
  1345. setWidth(size.getSize(), size.getUnit());
  1346. } else {
  1347. setWidth(-1, Unit.PIXELS);
  1348. }
  1349. }
  1350. /*
  1351. * (non-Javadoc)
  1352. *
  1353. * @see com.vaadin.terminal.Sizeable#setHeight(java.lang.String)
  1354. */
  1355. public void setHeight(String height) {
  1356. Size size = parseStringSize(height);
  1357. if (size != null) {
  1358. setHeight(size.getSize(), size.getUnit());
  1359. } else {
  1360. setHeight(-1, Unit.PIXELS);
  1361. }
  1362. }
  1363. /*
  1364. * Returns array with size in index 0 unit in index 1. Null or empty string
  1365. * will produce {-1,Unit#PIXELS}
  1366. */
  1367. private static Size parseStringSize(String s) {
  1368. if (s == null) {
  1369. return null;
  1370. }
  1371. s = s.trim();
  1372. if ("".equals(s)) {
  1373. return null;
  1374. }
  1375. float size = 0;
  1376. Unit unit = null;
  1377. Matcher matcher = sizePattern.matcher(s);
  1378. if (matcher.find()) {
  1379. size = Float.parseFloat(matcher.group(1));
  1380. if (size < 0) {
  1381. size = -1;
  1382. unit = Unit.PIXELS;
  1383. } else {
  1384. String symbol = matcher.group(3);
  1385. unit = Unit.getUnitFromSymbol(symbol);
  1386. }
  1387. } else {
  1388. throw new IllegalArgumentException("Invalid size argument: \"" + s
  1389. + "\" (should match " + sizePattern.pattern() + ")");
  1390. }
  1391. return new Size(size, unit);
  1392. }
  1393. private static class Size implements Serializable {
  1394. float size;
  1395. Unit unit;
  1396. public Size(float size, Unit unit) {
  1397. this.size = size;
  1398. this.unit = unit;
  1399. }
  1400. public float getSize() {
  1401. return size;
  1402. }
  1403. public Unit getUnit() {
  1404. return unit;
  1405. }
  1406. }
  1407. public interface ComponentErrorEvent extends Terminal.ErrorEvent {
  1408. }
  1409. public interface ComponentErrorHandler extends Serializable {
  1410. /**
  1411. * Handle the component error
  1412. *
  1413. * @param event
  1414. * @return True if the error has been handled False, otherwise
  1415. */
  1416. public boolean handleComponentError(ComponentErrorEvent event);
  1417. }
  1418. /**
  1419. * Gets the error handler for the component.
  1420. *
  1421. * The error handler is dispatched whenever there is an error processing the
  1422. * data coming from the client.
  1423. *
  1424. * @return
  1425. */
  1426. public ComponentErrorHandler getErrorHandler() {
  1427. return errorHandler;
  1428. }
  1429. /**
  1430. * Sets the error handler for the component.
  1431. *
  1432. * The error handler is dispatched whenever there is an error processing the
  1433. * data coming from the client.
  1434. *
  1435. * If the error handler is not set, the application error handler is used to
  1436. * handle the exception.
  1437. *
  1438. * @param errorHandler
  1439. * AbstractField specific error handler
  1440. */
  1441. public void setErrorHandler(ComponentErrorHandler errorHandler) {
  1442. this.errorHandler = errorHandler;
  1443. }
  1444. /**
  1445. * Handle the component error event.
  1446. *
  1447. * @param error
  1448. * Error event to handle
  1449. * @return True if the error has been handled False, otherwise. If the error
  1450. * haven't been handled by this component, it will be handled in the
  1451. * application error handler.
  1452. */
  1453. public boolean handleError(ComponentErrorEvent error) {
  1454. if (errorHandler != null) {
  1455. return errorHandler.handleComponentError(error);
  1456. }
  1457. return false;
  1458. }
  1459. /*
  1460. * Actions
  1461. */
  1462. /**
  1463. * Gets the {@link ActionManager} used to manage the
  1464. * {@link ShortcutListener}s added to this {@link Field}.
  1465. *
  1466. * @return the ActionManager in use
  1467. */
  1468. protected ActionManager getActionManager() {
  1469. if (actionManager == null) {
  1470. actionManager = new ActionManager();
  1471. if (getRoot() != null) {
  1472. actionManager.setViewer(getRoot());
  1473. }
  1474. }
  1475. return actionManager;
  1476. }
  1477. public void addShortcutListener(ShortcutListener shortcut) {
  1478. getActionManager().addAction(shortcut);
  1479. }
  1480. public void removeShortcutListener(ShortcutListener shortcut) {
  1481. if (actionManager != null) {
  1482. actionManager.removeAction(shortcut);
  1483. }
  1484. }
  1485. /**
  1486. * Registers an RPC interface implementation for this component.
  1487. *
  1488. * A component can listen to multiple RPC interfaces, and subclasses can
  1489. * register additional implementations.
  1490. *
  1491. * @since 7.0
  1492. *
  1493. * @param implementation
  1494. * RPC interface implementation
  1495. * @param rpcInterfaceType
  1496. * RPC interface class for which the implementation should be
  1497. * registered
  1498. */
  1499. protected <T> void registerRpcImplementation(T implementation,
  1500. Class<T> rpcInterfaceType) {
  1501. if (this instanceof RpcTarget) {
  1502. rpcManagerMap.put(rpcInterfaceType, new ServerRpcManager<T>(
  1503. (RpcTarget) this, implementation, rpcInterfaceType));
  1504. } else {
  1505. throw new RuntimeException(
  1506. "Cannot register an RPC implementation for a component that is not an RpcTarget");
  1507. }
  1508. }
  1509. public RpcManager getRpcManager(Class<?> rpcInterface) {
  1510. return rpcManagerMap.get(rpcInterface);
  1511. }
  1512. }