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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360
  1. /*
  2. * Copyright 2011 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.io.Serializable;
  18. import java.lang.reflect.Method;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Locale;
  25. import java.util.regex.Matcher;
  26. import java.util.regex.Pattern;
  27. import com.vaadin.Application;
  28. import com.vaadin.event.ActionManager;
  29. import com.vaadin.event.EventRouter;
  30. import com.vaadin.event.MethodEventSource;
  31. import com.vaadin.event.ShortcutListener;
  32. import com.vaadin.shared.ComponentState;
  33. import com.vaadin.terminal.AbstractClientConnector;
  34. import com.vaadin.terminal.ErrorMessage;
  35. import com.vaadin.terminal.Resource;
  36. import com.vaadin.terminal.Terminal;
  37. import com.vaadin.terminal.gwt.server.ClientConnector;
  38. import com.vaadin.terminal.gwt.server.ComponentSizeValidator;
  39. import com.vaadin.terminal.gwt.server.ResourceReference;
  40. import com.vaadin.tools.ReflectTools;
  41. /**
  42. * An abstract class that defines default implementation for the
  43. * {@link Component} interface. Basic UI components that are not derived from an
  44. * external component can inherit this class to easily qualify as Vaadin
  45. * components. Most components in Vaadin do just that.
  46. *
  47. * @author Vaadin Ltd.
  48. * @since 3.0
  49. */
  50. @SuppressWarnings("serial")
  51. public abstract class AbstractComponent extends AbstractClientConnector
  52. implements Component, MethodEventSource {
  53. /* Private members */
  54. /**
  55. * Application specific data object. The component does not use or modify
  56. * this.
  57. */
  58. private Object applicationData;
  59. /**
  60. * The EventRouter used for the event model.
  61. */
  62. private EventRouter eventRouter = null;
  63. /**
  64. * The internal error message of the component.
  65. */
  66. private ErrorMessage componentError = null;
  67. /**
  68. * Locale of this component.
  69. */
  70. private Locale locale;
  71. /**
  72. * The component should receive focus (if {@link Focusable}) when attached.
  73. */
  74. private boolean delayedFocus;
  75. /* Sizeable fields */
  76. private float width = SIZE_UNDEFINED;
  77. private float height = SIZE_UNDEFINED;
  78. private Unit widthUnit = Unit.PIXELS;
  79. private Unit heightUnit = Unit.PIXELS;
  80. private static final Pattern sizePattern = Pattern
  81. .compile("^(-?\\d+(\\.\\d+)?)(%|px|em|ex|in|cm|mm|pt|pc)?$");
  82. private ComponentErrorHandler errorHandler = null;
  83. /**
  84. * Keeps track of the Actions added to this component; the actual
  85. * handling/notifying is delegated, usually to the containing window.
  86. */
  87. private ActionManager actionManager;
  88. /* Constructor */
  89. /**
  90. * Constructs a new Component.
  91. */
  92. public AbstractComponent() {
  93. // ComponentSizeValidator.setCreationLocation(this);
  94. }
  95. /* Get/Set component properties */
  96. @Override
  97. public void setDebugId(String id) {
  98. getState().setDebugId(id);
  99. }
  100. @Override
  101. public String getDebugId() {
  102. return getState().getDebugId();
  103. }
  104. /*
  105. * Gets the component's style. Don't add a JavaDoc comment here, we use the
  106. * default documentation from implemented interface.
  107. */
  108. @Override
  109. public String getStyleName() {
  110. String s = "";
  111. if (getState().getStyles() != null) {
  112. for (final Iterator<String> it = getState().getStyles().iterator(); it
  113. .hasNext();) {
  114. s += it.next();
  115. if (it.hasNext()) {
  116. s += " ";
  117. }
  118. }
  119. }
  120. return s;
  121. }
  122. /*
  123. * Sets the component's style. Don't add a JavaDoc comment here, we use the
  124. * default documentation from implemented interface.
  125. */
  126. @Override
  127. public void setStyleName(String style) {
  128. if (style == null || "".equals(style)) {
  129. getState().setStyles(null);
  130. requestRepaint();
  131. return;
  132. }
  133. if (getState().getStyles() == null) {
  134. getState().setStyles(new ArrayList<String>());
  135. }
  136. List<String> styles = getState().getStyles();
  137. styles.clear();
  138. String[] styleParts = style.split(" +");
  139. for (String part : styleParts) {
  140. if (part.length() > 0) {
  141. styles.add(part);
  142. }
  143. }
  144. requestRepaint();
  145. }
  146. @Override
  147. public void addStyleName(String style) {
  148. if (style == null || "".equals(style)) {
  149. return;
  150. }
  151. if (style.contains(" ")) {
  152. // Split space separated style names and add them one by one.
  153. for (String realStyle : style.split(" ")) {
  154. addStyleName(realStyle);
  155. }
  156. return;
  157. }
  158. if (getState().getStyles() == null) {
  159. getState().setStyles(new ArrayList<String>());
  160. }
  161. List<String> styles = getState().getStyles();
  162. if (!styles.contains(style)) {
  163. styles.add(style);
  164. requestRepaint();
  165. }
  166. }
  167. @Override
  168. public void removeStyleName(String style) {
  169. if (getState().getStyles() != null) {
  170. String[] styleParts = style.split(" +");
  171. for (String part : styleParts) {
  172. if (part.length() > 0) {
  173. getState().getStyles().remove(part);
  174. }
  175. }
  176. requestRepaint();
  177. }
  178. }
  179. /*
  180. * Get's the component's caption. Don't add a JavaDoc comment here, we use
  181. * the default documentation from implemented interface.
  182. */
  183. @Override
  184. public String getCaption() {
  185. return getState().getCaption();
  186. }
  187. /**
  188. * Sets the component's caption <code>String</code>. Caption is the visible
  189. * name of the component. This method will trigger a
  190. * {@link RepaintRequestEvent}.
  191. *
  192. * @param caption
  193. * the new caption <code>String</code> for the component.
  194. */
  195. @Override
  196. public void setCaption(String caption) {
  197. getState().setCaption(caption);
  198. requestRepaint();
  199. }
  200. /*
  201. * Don't add a JavaDoc comment here, we use the default documentation from
  202. * implemented interface.
  203. */
  204. @Override
  205. public Locale getLocale() {
  206. if (locale != null) {
  207. return locale;
  208. }
  209. HasComponents parent = getParent();
  210. if (parent != null) {
  211. return parent.getLocale();
  212. }
  213. final Application app = getApplication();
  214. if (app != null) {
  215. return app.getLocale();
  216. }
  217. return null;
  218. }
  219. /**
  220. * Sets the locale of this component.
  221. *
  222. * <pre>
  223. * // Component for which the locale is meaningful
  224. * InlineDateField date = new InlineDateField(&quot;Datum&quot;);
  225. *
  226. * // German language specified with ISO 639-1 language
  227. * // code and ISO 3166-1 alpha-2 country code.
  228. * date.setLocale(new Locale(&quot;de&quot;, &quot;DE&quot;));
  229. *
  230. * date.setResolution(DateField.RESOLUTION_DAY);
  231. * layout.addComponent(date);
  232. * </pre>
  233. *
  234. *
  235. * @param locale
  236. * the locale to become this component's locale.
  237. */
  238. public void setLocale(Locale locale) {
  239. this.locale = locale;
  240. // FIXME: Reload value if there is a converter
  241. requestRepaint();
  242. }
  243. /*
  244. * Gets the component's icon resource. Don't add a JavaDoc comment here, we
  245. * use the default documentation from implemented interface.
  246. */
  247. @Override
  248. public Resource getIcon() {
  249. return ResourceReference.getResource(getState().getIcon());
  250. }
  251. /**
  252. * Sets the component's icon. This method will trigger a
  253. * {@link RepaintRequestEvent}.
  254. *
  255. * @param icon
  256. * the icon to be shown with the component's caption.
  257. */
  258. @Override
  259. public void setIcon(Resource icon) {
  260. getState().setIcon(ResourceReference.create(icon));
  261. requestRepaint();
  262. }
  263. /*
  264. * (non-Javadoc)
  265. *
  266. * @see com.vaadin.ui.Component#isEnabled()
  267. */
  268. @Override
  269. public boolean isEnabled() {
  270. return getState().isEnabled();
  271. }
  272. /*
  273. * (non-Javadoc)
  274. *
  275. * @see com.vaadin.ui.Component#setEnabled(boolean)
  276. */
  277. @Override
  278. public void setEnabled(boolean enabled) {
  279. if (getState().isEnabled() != enabled) {
  280. getState().setEnabled(enabled);
  281. requestRepaint();
  282. }
  283. }
  284. /*
  285. * (non-Javadoc)
  286. *
  287. * @see com.vaadin.terminal.gwt.client.Connector#isConnectorEnabled()
  288. */
  289. @Override
  290. public boolean isConnectorEnabled() {
  291. if (!isVisible()) {
  292. return false;
  293. } else if (!isEnabled()) {
  294. return false;
  295. } else if (!super.isConnectorEnabled()) {
  296. return false;
  297. } else if (!getParent().isComponentVisible(this)) {
  298. return false;
  299. } else {
  300. return true;
  301. }
  302. }
  303. /*
  304. * Tests if the component is in the immediate mode. Don't add a JavaDoc
  305. * comment here, we use the default documentation from implemented
  306. * interface.
  307. */
  308. public boolean isImmediate() {
  309. return getState().isImmediate();
  310. }
  311. /**
  312. * Sets the component's immediate mode to the specified status. This method
  313. * will trigger a {@link RepaintRequestEvent}.
  314. *
  315. * @param immediate
  316. * the boolean value specifying if the component should be in the
  317. * immediate mode after the call.
  318. * @see Component#isImmediate()
  319. */
  320. public void setImmediate(boolean immediate) {
  321. getState().setImmediate(immediate);
  322. requestRepaint();
  323. }
  324. /*
  325. * (non-Javadoc)
  326. *
  327. * @see com.vaadin.ui.Component#isVisible()
  328. */
  329. @Override
  330. public boolean isVisible() {
  331. return getState().isVisible();
  332. }
  333. /*
  334. * (non-Javadoc)
  335. *
  336. * @see com.vaadin.ui.Component#setVisible(boolean)
  337. */
  338. @Override
  339. public void setVisible(boolean visible) {
  340. if (getState().isVisible() == visible) {
  341. return;
  342. }
  343. getState().setVisible(visible);
  344. requestRepaint();
  345. if (getParent() != null) {
  346. // Must always repaint the parent (at least the hierarchy) when
  347. // visibility of a child component changes.
  348. getParent().requestRepaint();
  349. }
  350. }
  351. /**
  352. * <p>
  353. * Gets the component's description, used in tooltips and can be displayed
  354. * directly in certain other components such as forms. The description can
  355. * be used to briefly describe the state of the component to the user. The
  356. * description string may contain certain XML tags:
  357. * </p>
  358. *
  359. * <p>
  360. * <table border=1>
  361. * <tr>
  362. * <td width=120><b>Tag</b></td>
  363. * <td width=120><b>Description</b></td>
  364. * <td width=120><b>Example</b></td>
  365. * </tr>
  366. * <tr>
  367. * <td>&lt;b></td>
  368. * <td>bold</td>
  369. * <td><b>bold text</b></td>
  370. * </tr>
  371. * <tr>
  372. * <td>&lt;i></td>
  373. * <td>italic</td>
  374. * <td><i>italic text</i></td>
  375. * </tr>
  376. * <tr>
  377. * <td>&lt;u></td>
  378. * <td>underlined</td>
  379. * <td><u>underlined text</u></td>
  380. * </tr>
  381. * <tr>
  382. * <td>&lt;br></td>
  383. * <td>linebreak</td>
  384. * <td>N/A</td>
  385. * </tr>
  386. * <tr>
  387. * <td>&lt;ul><br>
  388. * &lt;li>item1<br>
  389. * &lt;li>item1<br>
  390. * &lt;/ul></td>
  391. * <td>item list</td>
  392. * <td>
  393. * <ul>
  394. * <li>item1
  395. * <li>item2
  396. * </ul>
  397. * </td>
  398. * </tr>
  399. * </table>
  400. * </p>
  401. *
  402. * <p>
  403. * These tags may be nested.
  404. * </p>
  405. *
  406. * @return component's description <code>String</code>
  407. */
  408. public String getDescription() {
  409. return getState().getDescription();
  410. }
  411. /**
  412. * Sets the component's description. See {@link #getDescription()} for more
  413. * information on what the description is. This method will trigger a
  414. * {@link RepaintRequestEvent}.
  415. *
  416. * The description is displayed as HTML/XHTML in tooltips or directly in
  417. * certain components so care should be taken to avoid creating the
  418. * possibility for HTML injection and possibly XSS vulnerabilities.
  419. *
  420. * @param description
  421. * the new description string for the component.
  422. */
  423. public void setDescription(String description) {
  424. getState().setDescription(description);
  425. requestRepaint();
  426. }
  427. /*
  428. * Gets the component's parent component. Don't add a JavaDoc comment here,
  429. * we use the default documentation from implemented interface.
  430. */
  431. @Override
  432. public HasComponents getParent() {
  433. return (HasComponents) super.getParent();
  434. }
  435. @Override
  436. public void setParent(ClientConnector parent) {
  437. if (parent == null || parent instanceof HasComponents) {
  438. super.setParent(parent);
  439. } else {
  440. throw new IllegalArgumentException(
  441. "The parent of a Component must implement HasComponents, which "
  442. + parent.getClass() + " doesn't do.");
  443. }
  444. }
  445. /**
  446. * Returns the closest ancestor with the given type.
  447. * <p>
  448. * To find the Window that contains the component, use {@code Window w =
  449. * getParent(Window.class);}
  450. * </p>
  451. *
  452. * @param <T>
  453. * The type of the ancestor
  454. * @param parentType
  455. * The ancestor class we are looking for
  456. * @return The first ancestor that can be assigned to the given class. Null
  457. * if no ancestor with the correct type could be found.
  458. */
  459. public <T extends HasComponents> T findAncestor(Class<T> parentType) {
  460. HasComponents p = getParent();
  461. while (p != null) {
  462. if (parentType.isAssignableFrom(p.getClass())) {
  463. return parentType.cast(p);
  464. }
  465. p = p.getParent();
  466. }
  467. return null;
  468. }
  469. /**
  470. * Gets the error message for this component.
  471. *
  472. * @return ErrorMessage containing the description of the error state of the
  473. * component or null, if the component contains no errors. Extending
  474. * classes should override this method if they support other error
  475. * message types such as validation errors or buffering errors. The
  476. * returned error message contains information about all the errors.
  477. */
  478. public ErrorMessage getErrorMessage() {
  479. return componentError;
  480. }
  481. /**
  482. * Gets the component's error message.
  483. *
  484. * @link Terminal.ErrorMessage#ErrorMessage(String, int)
  485. *
  486. * @return the component's error message.
  487. */
  488. public ErrorMessage getComponentError() {
  489. return componentError;
  490. }
  491. /**
  492. * Sets the component's error message. The message may contain certain XML
  493. * tags, for more information see
  494. *
  495. * @link Component.ErrorMessage#ErrorMessage(String, int)
  496. *
  497. * @param componentError
  498. * the new <code>ErrorMessage</code> of the component.
  499. */
  500. public void setComponentError(ErrorMessage componentError) {
  501. this.componentError = componentError;
  502. fireComponentErrorEvent();
  503. requestRepaint();
  504. }
  505. /*
  506. * Tests if the component is in read-only mode. Don't add a JavaDoc comment
  507. * here, we use the default documentation from implemented interface.
  508. */
  509. @Override
  510. public boolean isReadOnly() {
  511. return getState().isReadOnly();
  512. }
  513. /*
  514. * Sets the component's read-only mode. Don't add a JavaDoc comment here, we
  515. * use the default documentation from implemented interface.
  516. */
  517. @Override
  518. public void setReadOnly(boolean readOnly) {
  519. getState().setReadOnly(readOnly);
  520. requestRepaint();
  521. }
  522. /*
  523. * Gets the parent window of the component. Don't add a JavaDoc comment
  524. * here, we use the default documentation from implemented interface.
  525. */
  526. @Override
  527. public Root getRoot() {
  528. // Just make method from implemented Component interface public
  529. return super.getRoot();
  530. }
  531. /*
  532. * Notify the component that it's attached to a window. Don't add a JavaDoc
  533. * comment here, we use the default documentation from implemented
  534. * interface.
  535. */
  536. @Override
  537. public void attach() {
  538. super.attach();
  539. if (delayedFocus) {
  540. focus();
  541. }
  542. setActionManagerViewer();
  543. }
  544. /*
  545. * Detach the component from application. Don't add a JavaDoc comment here,
  546. * we use the default documentation from implemented interface.
  547. */
  548. @Override
  549. public void detach() {
  550. super.detach();
  551. if (actionManager != null) {
  552. // Remove any existing viewer. Root cast is just to make the
  553. // compiler happy
  554. actionManager.setViewer((Root) null);
  555. }
  556. }
  557. /**
  558. * Sets the focus for this component if the component is {@link Focusable}.
  559. */
  560. protected void focus() {
  561. if (this instanceof Focusable) {
  562. final Application app = getApplication();
  563. if (app != null) {
  564. getRoot().setFocusedComponent((Focusable) this);
  565. delayedFocus = false;
  566. } else {
  567. delayedFocus = true;
  568. }
  569. }
  570. }
  571. /**
  572. * Gets the application object to which the component is attached.
  573. *
  574. * <p>
  575. * The method will return {@code null} if the component is not currently
  576. * attached to an application. This is often a problem in constructors of
  577. * regular components and in the initializers of custom composite
  578. * components. A standard workaround is to move the problematic
  579. * initialization to {@link #attach()}, as described in the documentation of
  580. * the method.
  581. * </p>
  582. * <p>
  583. * <b>This method is not meant to be overridden. Due to CDI requirements we
  584. * cannot declare it as final even though it should be final.</b>
  585. * </p>
  586. *
  587. * @return the parent application of the component or <code>null</code>.
  588. * @see #attach()
  589. */
  590. @Override
  591. public Application getApplication() {
  592. // Just make method inherited from Component interface public
  593. return super.getApplication();
  594. }
  595. /**
  596. * Build CSS compatible string representation of height.
  597. *
  598. * @return CSS height
  599. */
  600. private String getCSSHeight() {
  601. if (getHeightUnits() == Unit.PIXELS) {
  602. return ((int) getHeight()) + getHeightUnits().getSymbol();
  603. } else {
  604. return getHeight() + getHeightUnits().getSymbol();
  605. }
  606. }
  607. /**
  608. * Build CSS compatible string representation of width.
  609. *
  610. * @return CSS width
  611. */
  612. private String getCSSWidth() {
  613. if (getWidthUnits() == Unit.PIXELS) {
  614. return ((int) getWidth()) + getWidthUnits().getSymbol();
  615. } else {
  616. return getWidth() + getWidthUnits().getSymbol();
  617. }
  618. }
  619. /**
  620. * Returns the shared state bean with information to be sent from the server
  621. * to the client.
  622. *
  623. * Subclasses should override this method and set any relevant fields of the
  624. * state returned by super.getState().
  625. *
  626. * @since 7.0
  627. *
  628. * @return updated component shared state
  629. */
  630. @Override
  631. protected ComponentState getState() {
  632. return (ComponentState) super.getState();
  633. }
  634. @Override
  635. public void beforeClientResponse(boolean initial) {
  636. super.beforeClientResponse(initial);
  637. // TODO This logic should be on the client side and the state should
  638. // simply be a data object with "width" and "height".
  639. if (getHeight() >= 0
  640. && (getHeightUnits() != Unit.PERCENTAGE || ComponentSizeValidator
  641. .parentCanDefineHeight(this))) {
  642. getState().setHeight("" + getCSSHeight());
  643. } else {
  644. getState().setHeight("");
  645. }
  646. if (getWidth() >= 0
  647. && (getWidthUnits() != Unit.PERCENTAGE || ComponentSizeValidator
  648. .parentCanDefineWidth(this))) {
  649. getState().setWidth("" + getCSSWidth());
  650. } else {
  651. getState().setWidth("");
  652. }
  653. ErrorMessage error = getErrorMessage();
  654. if (null != error) {
  655. getState().setErrorMessage(error.getFormattedHtmlMessage());
  656. } else {
  657. getState().setErrorMessage(null);
  658. }
  659. }
  660. /* Documentation copied from interface */
  661. @Override
  662. public void requestRepaint() {
  663. // Invisible components (by flag in this particular component) do not
  664. // need repaints
  665. if (!getState().isVisible()) {
  666. return;
  667. }
  668. super.requestRepaint();
  669. }
  670. /* General event framework */
  671. private static final Method COMPONENT_EVENT_METHOD = ReflectTools
  672. .findMethod(Component.Listener.class, "componentEvent",
  673. Component.Event.class);
  674. /**
  675. * <p>
  676. * Registers a new listener with the specified activation method to listen
  677. * events generated by this component. If the activation method does not
  678. * have any arguments the event object will not be passed to it when it's
  679. * called.
  680. * </p>
  681. *
  682. * <p>
  683. * This method additionally informs the event-api to route events with the
  684. * given eventIdentifier to the components handleEvent function call.
  685. * </p>
  686. *
  687. * <p>
  688. * For more information on the inheritable event mechanism see the
  689. * {@link com.vaadin.event com.vaadin.event package documentation}.
  690. * </p>
  691. *
  692. * @param eventIdentifier
  693. * the identifier of the event to listen for
  694. * @param eventType
  695. * the type of the listened event. Events of this type or its
  696. * subclasses activate the listener.
  697. * @param target
  698. * the object instance who owns the activation method.
  699. * @param method
  700. * the activation method.
  701. *
  702. * @since 6.2
  703. */
  704. protected void addListener(String eventIdentifier, Class<?> eventType,
  705. Object target, Method method) {
  706. if (eventRouter == null) {
  707. eventRouter = new EventRouter();
  708. }
  709. boolean needRepaint = !eventRouter.hasListeners(eventType);
  710. eventRouter.addListener(eventType, target, method);
  711. if (needRepaint) {
  712. getState().addRegisteredEventListener(eventIdentifier);
  713. requestRepaint();
  714. }
  715. }
  716. /**
  717. * Checks if the given {@link Event} type is listened for this component.
  718. *
  719. * @param eventType
  720. * the event type to be checked
  721. * @return true if a listener is registered for the given event type
  722. */
  723. protected boolean hasListeners(Class<?> eventType) {
  724. return eventRouter != null && eventRouter.hasListeners(eventType);
  725. }
  726. /**
  727. * Removes all registered listeners matching the given parameters. Since
  728. * this method receives the event type and the listener object as
  729. * parameters, it will unregister all <code>object</code>'s methods that are
  730. * registered to listen to events of type <code>eventType</code> generated
  731. * by this component.
  732. *
  733. * <p>
  734. * This method additionally informs the event-api to stop routing events
  735. * with the given eventIdentifier to the components handleEvent function
  736. * call.
  737. * </p>
  738. *
  739. * <p>
  740. * For more information on the inheritable event mechanism see the
  741. * {@link com.vaadin.event com.vaadin.event package documentation}.
  742. * </p>
  743. *
  744. * @param eventIdentifier
  745. * the identifier of the event to stop listening for
  746. * @param eventType
  747. * the exact event type the <code>object</code> listens to.
  748. * @param target
  749. * the target object that has registered to listen to events of
  750. * type <code>eventType</code> with one or more methods.
  751. *
  752. * @since 6.2
  753. */
  754. protected void removeListener(String eventIdentifier, Class<?> eventType,
  755. Object target) {
  756. if (eventRouter != null) {
  757. eventRouter.removeListener(eventType, target);
  758. if (!eventRouter.hasListeners(eventType)) {
  759. getState().removeRegisteredEventListener(eventIdentifier);
  760. requestRepaint();
  761. }
  762. }
  763. }
  764. /**
  765. * <p>
  766. * Registers a new listener with the specified activation method to listen
  767. * events generated by this component. If the activation method does not
  768. * have any arguments the event object will not be passed to it when it's
  769. * called.
  770. * </p>
  771. *
  772. * <p>
  773. * For more information on the inheritable event mechanism see the
  774. * {@link com.vaadin.event com.vaadin.event package documentation}.
  775. * </p>
  776. *
  777. * @param eventType
  778. * the type of the listened event. Events of this type or its
  779. * subclasses activate the listener.
  780. * @param target
  781. * the object instance who owns the activation method.
  782. * @param method
  783. * the activation method.
  784. */
  785. @Override
  786. public void addListener(Class<?> eventType, Object target, Method method) {
  787. if (eventRouter == null) {
  788. eventRouter = new EventRouter();
  789. }
  790. eventRouter.addListener(eventType, target, method);
  791. }
  792. /**
  793. * <p>
  794. * Convenience method for registering a new listener with the specified
  795. * activation method to listen events generated by this component. If the
  796. * activation method does not have any arguments the event object will not
  797. * be passed to it when it's called.
  798. * </p>
  799. *
  800. * <p>
  801. * This version of <code>addListener</code> gets the name of the activation
  802. * method as a parameter. The actual method is reflected from
  803. * <code>object</code>, and unless exactly one match is found,
  804. * <code>java.lang.IllegalArgumentException</code> is thrown.
  805. * </p>
  806. *
  807. * <p>
  808. * For more information on the inheritable event mechanism see the
  809. * {@link com.vaadin.event com.vaadin.event package documentation}.
  810. * </p>
  811. *
  812. * <p>
  813. * Note: Using this method is discouraged because it cannot be checked
  814. * during compilation. Use {@link #addListener(Class, Object, Method)} or
  815. * {@link #addListener(com.vaadin.ui.Component.Listener)} instead.
  816. * </p>
  817. *
  818. * @param eventType
  819. * the type of the listened event. Events of this type or its
  820. * subclasses activate the listener.
  821. * @param target
  822. * the object instance who owns the activation method.
  823. * @param methodName
  824. * the name of the activation method.
  825. */
  826. @Override
  827. public void addListener(Class<?> eventType, Object target, String methodName) {
  828. if (eventRouter == null) {
  829. eventRouter = new EventRouter();
  830. }
  831. eventRouter.addListener(eventType, target, methodName);
  832. }
  833. /**
  834. * Removes all registered listeners matching the given parameters. Since
  835. * this method receives the event type and the listener object as
  836. * parameters, it will unregister all <code>object</code>'s methods that are
  837. * registered to listen to events of type <code>eventType</code> generated
  838. * by this component.
  839. *
  840. * <p>
  841. * For more information on the inheritable event mechanism see the
  842. * {@link com.vaadin.event com.vaadin.event package documentation}.
  843. * </p>
  844. *
  845. * @param eventType
  846. * the exact event type the <code>object</code> listens to.
  847. * @param target
  848. * the target object that has registered to listen to events of
  849. * type <code>eventType</code> with one or more methods.
  850. */
  851. @Override
  852. public void removeListener(Class<?> eventType, Object target) {
  853. if (eventRouter != null) {
  854. eventRouter.removeListener(eventType, target);
  855. }
  856. }
  857. /**
  858. * Removes one registered listener method. The given method owned by the
  859. * given object will no longer be called when the specified events are
  860. * generated by this component.
  861. *
  862. * <p>
  863. * For more information on the inheritable event mechanism see the
  864. * {@link com.vaadin.event com.vaadin.event package documentation}.
  865. * </p>
  866. *
  867. * @param eventType
  868. * the exact event type the <code>object</code> listens to.
  869. * @param target
  870. * target object that has registered to listen to events of type
  871. * <code>eventType</code> with one or more methods.
  872. * @param method
  873. * the method owned by <code>target</code> that's registered to
  874. * listen to events of type <code>eventType</code>.
  875. */
  876. @Override
  877. public void removeListener(Class<?> eventType, Object target, Method method) {
  878. if (eventRouter != null) {
  879. eventRouter.removeListener(eventType, target, method);
  880. }
  881. }
  882. /**
  883. * <p>
  884. * Removes one registered listener method. The given method owned by the
  885. * given object will no longer be called when the specified events are
  886. * generated by this component.
  887. * </p>
  888. *
  889. * <p>
  890. * This version of <code>removeListener</code> gets the name of the
  891. * activation method as a parameter. The actual method is reflected from
  892. * <code>target</code>, and unless exactly one match is found,
  893. * <code>java.lang.IllegalArgumentException</code> is thrown.
  894. * </p>
  895. *
  896. * <p>
  897. * For more information on the inheritable event mechanism see the
  898. * {@link com.vaadin.event com.vaadin.event package documentation}.
  899. * </p>
  900. *
  901. * @param eventType
  902. * the exact event type the <code>object</code> listens to.
  903. * @param target
  904. * the target object that has registered to listen to events of
  905. * type <code>eventType</code> with one or more methods.
  906. * @param methodName
  907. * the name of the method owned by <code>target</code> that's
  908. * registered to listen to events of type <code>eventType</code>.
  909. */
  910. @Override
  911. public void removeListener(Class<?> eventType, Object target,
  912. String methodName) {
  913. if (eventRouter != null) {
  914. eventRouter.removeListener(eventType, target, methodName);
  915. }
  916. }
  917. /**
  918. * Returns all listeners that are registered for the given event type or one
  919. * of its subclasses.
  920. *
  921. * @param eventType
  922. * The type of event to return listeners for.
  923. * @return A collection with all registered listeners. Empty if no listeners
  924. * are found.
  925. */
  926. public Collection<?> getListeners(Class<?> eventType) {
  927. if (eventRouter == null) {
  928. return Collections.EMPTY_LIST;
  929. }
  930. return eventRouter.getListeners(eventType);
  931. }
  932. /**
  933. * Sends the event to all listeners.
  934. *
  935. * @param event
  936. * the Event to be sent to all listeners.
  937. */
  938. protected void fireEvent(Component.Event event) {
  939. if (eventRouter != null) {
  940. eventRouter.fireEvent(event);
  941. }
  942. }
  943. /* Component event framework */
  944. /*
  945. * Registers a new listener to listen events generated by this component.
  946. * Don't add a JavaDoc comment here, we use the default documentation from
  947. * implemented interface.
  948. */
  949. @Override
  950. public void addListener(Component.Listener listener) {
  951. addListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
  952. }
  953. /*
  954. * Removes a previously registered listener from this component. Don't add a
  955. * JavaDoc comment here, we use the default documentation from implemented
  956. * interface.
  957. */
  958. @Override
  959. public void removeListener(Component.Listener listener) {
  960. removeListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
  961. }
  962. /**
  963. * Emits the component event. It is transmitted to all registered listeners
  964. * interested in such events.
  965. */
  966. protected void fireComponentEvent() {
  967. fireEvent(new Component.Event(this));
  968. }
  969. /**
  970. * Emits the component error event. It is transmitted to all registered
  971. * listeners interested in such events.
  972. */
  973. protected void fireComponentErrorEvent() {
  974. fireEvent(new Component.ErrorEvent(getComponentError(), this));
  975. }
  976. /**
  977. * Sets the data object, that can be used for any application specific data.
  978. * The component does not use or modify this data.
  979. *
  980. * @param data
  981. * the Application specific data.
  982. * @since 3.1
  983. */
  984. public void setData(Object data) {
  985. applicationData = data;
  986. }
  987. /**
  988. * Gets the application specific data. See {@link #setData(Object)}.
  989. *
  990. * @return the Application specific data set with setData function.
  991. * @since 3.1
  992. */
  993. public Object getData() {
  994. return applicationData;
  995. }
  996. /* Sizeable and other size related methods */
  997. /*
  998. * (non-Javadoc)
  999. *
  1000. * @see com.vaadin.terminal.Sizeable#getHeight()
  1001. */
  1002. @Override
  1003. public float getHeight() {
  1004. return height;
  1005. }
  1006. /*
  1007. * (non-Javadoc)
  1008. *
  1009. * @see com.vaadin.terminal.Sizeable#getHeightUnits()
  1010. */
  1011. @Override
  1012. public Unit getHeightUnits() {
  1013. return heightUnit;
  1014. }
  1015. /*
  1016. * (non-Javadoc)
  1017. *
  1018. * @see com.vaadin.terminal.Sizeable#getWidth()
  1019. */
  1020. @Override
  1021. public float getWidth() {
  1022. return width;
  1023. }
  1024. /*
  1025. * (non-Javadoc)
  1026. *
  1027. * @see com.vaadin.terminal.Sizeable#getWidthUnits()
  1028. */
  1029. @Override
  1030. public Unit getWidthUnits() {
  1031. return widthUnit;
  1032. }
  1033. /*
  1034. * (non-Javadoc)
  1035. *
  1036. * @see com.vaadin.terminal.Sizeable#setHeight(float, Unit)
  1037. */
  1038. @Override
  1039. public void setHeight(float height, Unit unit) {
  1040. if (unit == null) {
  1041. throw new IllegalArgumentException("Unit can not be null");
  1042. }
  1043. this.height = height;
  1044. heightUnit = unit;
  1045. requestRepaint();
  1046. // ComponentSizeValidator.setHeightLocation(this);
  1047. }
  1048. /*
  1049. * (non-Javadoc)
  1050. *
  1051. * @see com.vaadin.terminal.Sizeable#setSizeFull()
  1052. */
  1053. @Override
  1054. public void setSizeFull() {
  1055. setWidth(100, Unit.PERCENTAGE);
  1056. setHeight(100, Unit.PERCENTAGE);
  1057. }
  1058. /*
  1059. * (non-Javadoc)
  1060. *
  1061. * @see com.vaadin.terminal.Sizeable#setSizeUndefined()
  1062. */
  1063. @Override
  1064. public void setSizeUndefined() {
  1065. setWidth(-1, Unit.PIXELS);
  1066. setHeight(-1, Unit.PIXELS);
  1067. }
  1068. /*
  1069. * (non-Javadoc)
  1070. *
  1071. * @see com.vaadin.terminal.Sizeable#setWidth(float, Unit)
  1072. */
  1073. @Override
  1074. public void setWidth(float width, Unit unit) {
  1075. if (unit == null) {
  1076. throw new IllegalArgumentException("Unit can not be null");
  1077. }
  1078. this.width = width;
  1079. widthUnit = unit;
  1080. requestRepaint();
  1081. // ComponentSizeValidator.setWidthLocation(this);
  1082. }
  1083. /*
  1084. * (non-Javadoc)
  1085. *
  1086. * @see com.vaadin.terminal.Sizeable#setWidth(java.lang.String)
  1087. */
  1088. @Override
  1089. public void setWidth(String width) {
  1090. Size size = parseStringSize(width);
  1091. if (size != null) {
  1092. setWidth(size.getSize(), size.getUnit());
  1093. } else {
  1094. setWidth(-1, Unit.PIXELS);
  1095. }
  1096. }
  1097. /*
  1098. * (non-Javadoc)
  1099. *
  1100. * @see com.vaadin.terminal.Sizeable#setHeight(java.lang.String)
  1101. */
  1102. @Override
  1103. public void setHeight(String height) {
  1104. Size size = parseStringSize(height);
  1105. if (size != null) {
  1106. setHeight(size.getSize(), size.getUnit());
  1107. } else {
  1108. setHeight(-1, Unit.PIXELS);
  1109. }
  1110. }
  1111. /*
  1112. * Returns array with size in index 0 unit in index 1. Null or empty string
  1113. * will produce {-1,Unit#PIXELS}
  1114. */
  1115. private static Size parseStringSize(String s) {
  1116. if (s == null) {
  1117. return null;
  1118. }
  1119. s = s.trim();
  1120. if ("".equals(s)) {
  1121. return null;
  1122. }
  1123. float size = 0;
  1124. Unit unit = null;
  1125. Matcher matcher = sizePattern.matcher(s);
  1126. if (matcher.find()) {
  1127. size = Float.parseFloat(matcher.group(1));
  1128. if (size < 0) {
  1129. size = -1;
  1130. unit = Unit.PIXELS;
  1131. } else {
  1132. String symbol = matcher.group(3);
  1133. unit = Unit.getUnitFromSymbol(symbol);
  1134. }
  1135. } else {
  1136. throw new IllegalArgumentException("Invalid size argument: \"" + s
  1137. + "\" (should match " + sizePattern.pattern() + ")");
  1138. }
  1139. return new Size(size, unit);
  1140. }
  1141. private static class Size implements Serializable {
  1142. float size;
  1143. Unit unit;
  1144. public Size(float size, Unit unit) {
  1145. this.size = size;
  1146. this.unit = unit;
  1147. }
  1148. public float getSize() {
  1149. return size;
  1150. }
  1151. public Unit getUnit() {
  1152. return unit;
  1153. }
  1154. }
  1155. public interface ComponentErrorEvent extends Terminal.ErrorEvent {
  1156. }
  1157. public interface ComponentErrorHandler extends Serializable {
  1158. /**
  1159. * Handle the component error
  1160. *
  1161. * @param event
  1162. * @return True if the error has been handled False, otherwise
  1163. */
  1164. public boolean handleComponentError(ComponentErrorEvent event);
  1165. }
  1166. /**
  1167. * Gets the error handler for the component.
  1168. *
  1169. * The error handler is dispatched whenever there is an error processing the
  1170. * data coming from the client.
  1171. *
  1172. * @return
  1173. */
  1174. public ComponentErrorHandler getErrorHandler() {
  1175. return errorHandler;
  1176. }
  1177. /**
  1178. * Sets the error handler for the component.
  1179. *
  1180. * The error handler is dispatched whenever there is an error processing the
  1181. * data coming from the client.
  1182. *
  1183. * If the error handler is not set, the application error handler is used to
  1184. * handle the exception.
  1185. *
  1186. * @param errorHandler
  1187. * AbstractField specific error handler
  1188. */
  1189. public void setErrorHandler(ComponentErrorHandler errorHandler) {
  1190. this.errorHandler = errorHandler;
  1191. }
  1192. /**
  1193. * Handle the component error event.
  1194. *
  1195. * @param error
  1196. * Error event to handle
  1197. * @return True if the error has been handled False, otherwise. If the error
  1198. * haven't been handled by this component, it will be handled in the
  1199. * application error handler.
  1200. */
  1201. public boolean handleError(ComponentErrorEvent error) {
  1202. if (errorHandler != null) {
  1203. return errorHandler.handleComponentError(error);
  1204. }
  1205. return false;
  1206. }
  1207. /*
  1208. * Actions
  1209. */
  1210. /**
  1211. * Gets the {@link ActionManager} used to manage the
  1212. * {@link ShortcutListener}s added to this {@link Field}.
  1213. *
  1214. * @return the ActionManager in use
  1215. */
  1216. protected ActionManager getActionManager() {
  1217. if (actionManager == null) {
  1218. actionManager = new ActionManager();
  1219. setActionManagerViewer();
  1220. }
  1221. return actionManager;
  1222. }
  1223. /**
  1224. * Set a viewer for the action manager to be the parent sub window (if the
  1225. * component is in a window) or the root (otherwise). This is still a
  1226. * simplification of the real case as this should be handled by the parent
  1227. * VOverlay (on the client side) if the component is inside an VOverlay
  1228. * component.
  1229. */
  1230. private void setActionManagerViewer() {
  1231. if (actionManager != null && getRoot() != null) {
  1232. // Attached and has action manager
  1233. Window w = findAncestor(Window.class);
  1234. if (w != null) {
  1235. actionManager.setViewer(w);
  1236. } else {
  1237. actionManager.setViewer(getRoot());
  1238. }
  1239. }
  1240. }
  1241. public void addShortcutListener(ShortcutListener shortcut) {
  1242. getActionManager().addAction(shortcut);
  1243. }
  1244. public void removeShortcutListener(ShortcutListener shortcut) {
  1245. if (actionManager != null) {
  1246. actionManager.removeAction(shortcut);
  1247. }
  1248. }
  1249. }