Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

AbstractComponent.java 53KB

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