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

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