aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin/ui/AbstractComponent.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/com/vaadin/ui/AbstractComponent.java')
-rw-r--r--server/src/com/vaadin/ui/AbstractComponent.java1382
1 files changed, 1382 insertions, 0 deletions
diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java
new file mode 100644
index 0000000000..e7cb38256c
--- /dev/null
+++ b/server/src/com/vaadin/ui/AbstractComponent.java
@@ -0,0 +1,1382 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.vaadin.Application;
+import com.vaadin.event.ActionManager;
+import com.vaadin.event.EventRouter;
+import com.vaadin.event.MethodEventSource;
+import com.vaadin.event.ShortcutListener;
+import com.vaadin.shared.ComponentState;
+import com.vaadin.terminal.AbstractClientConnector;
+import com.vaadin.terminal.ErrorMessage;
+import com.vaadin.terminal.Resource;
+import com.vaadin.terminal.Terminal;
+import com.vaadin.terminal.gwt.server.ClientConnector;
+import com.vaadin.terminal.gwt.server.ComponentSizeValidator;
+import com.vaadin.terminal.gwt.server.ResourceReference;
+import com.vaadin.tools.ReflectTools;
+
+/**
+ * An abstract class that defines default implementation for the
+ * {@link Component} interface. Basic UI components that are not derived from an
+ * external component can inherit this class to easily qualify as Vaadin
+ * components. Most components in Vaadin do just that.
+ *
+ * @author Vaadin Ltd.
+ * @version
+ * @VERSION@
+ * @since 3.0
+ */
+@SuppressWarnings("serial")
+public abstract class AbstractComponent extends AbstractClientConnector
+ implements Component, MethodEventSource {
+
+ /* Private members */
+
+ /**
+ * Application specific data object. The component does not use or modify
+ * this.
+ */
+ private Object applicationData;
+
+ /**
+ * The EventRouter used for the event model.
+ */
+ private EventRouter eventRouter = null;
+
+ /**
+ * The internal error message of the component.
+ */
+ private ErrorMessage componentError = null;
+
+ /**
+ * Locale of this component.
+ */
+ private Locale locale;
+
+ /**
+ * The component should receive focus (if {@link Focusable}) when attached.
+ */
+ private boolean delayedFocus;
+
+ /* Sizeable fields */
+
+ private float width = SIZE_UNDEFINED;
+ private float height = SIZE_UNDEFINED;
+ private Unit widthUnit = Unit.PIXELS;
+ private Unit heightUnit = Unit.PIXELS;
+ private static final Pattern sizePattern = Pattern
+ .compile("^(-?\\d+(\\.\\d+)?)(%|px|em|ex|in|cm|mm|pt|pc)?$");
+
+ private ComponentErrorHandler errorHandler = null;
+
+ /**
+ * Keeps track of the Actions added to this component; the actual
+ * handling/notifying is delegated, usually to the containing window.
+ */
+ private ActionManager actionManager;
+
+ /* Constructor */
+
+ /**
+ * Constructs a new Component.
+ */
+ public AbstractComponent() {
+ // ComponentSizeValidator.setCreationLocation(this);
+ }
+
+ /* Get/Set component properties */
+
+ @Override
+ public void setDebugId(String id) {
+ getState().setDebugId(id);
+ }
+
+ @Override
+ public String getDebugId() {
+ return getState().getDebugId();
+ }
+
+ /**
+ * Gets style for component. Multiple styles are joined with spaces.
+ *
+ * @return the component's styleValue of property style.
+ * @deprecated Use getStyleName() instead; renamed for consistency and to
+ * indicate that "style" should not be used to switch client
+ * side implementation, only to style the component.
+ */
+ @Deprecated
+ public String getStyle() {
+ return getStyleName();
+ }
+
+ /**
+ * Sets and replaces all previous style names of the component. This method
+ * will trigger a {@link RepaintRequestEvent}.
+ *
+ * @param style
+ * the new style of the component.
+ * @deprecated Use setStyleName() instead; renamed for consistency and to
+ * indicate that "style" should not be used to switch client
+ * side implementation, only to style the component.
+ */
+ @Deprecated
+ public void setStyle(String style) {
+ setStyleName(style);
+ }
+
+ /*
+ * Gets the component's style. Don't add a JavaDoc comment here, we use the
+ * default documentation from implemented interface.
+ */
+ @Override
+ public String getStyleName() {
+ String s = "";
+ if (getState().getStyles() != null) {
+ for (final Iterator<String> it = getState().getStyles().iterator(); it
+ .hasNext();) {
+ s += it.next();
+ if (it.hasNext()) {
+ s += " ";
+ }
+ }
+ }
+ return s;
+ }
+
+ /*
+ * Sets the component's style. Don't add a JavaDoc comment here, we use the
+ * default documentation from implemented interface.
+ */
+ @Override
+ public void setStyleName(String style) {
+ if (style == null || "".equals(style)) {
+ getState().setStyles(null);
+ requestRepaint();
+ return;
+ }
+ if (getState().getStyles() == null) {
+ getState().setStyles(new ArrayList<String>());
+ }
+ List<String> styles = getState().getStyles();
+ styles.clear();
+ String[] styleParts = style.split(" +");
+ for (String part : styleParts) {
+ if (part.length() > 0) {
+ styles.add(part);
+ }
+ }
+ requestRepaint();
+ }
+
+ @Override
+ public void addStyleName(String style) {
+ if (style == null || "".equals(style)) {
+ return;
+ }
+ if (style.contains(" ")) {
+ // Split space separated style names and add them one by one.
+ for (String realStyle : style.split(" ")) {
+ addStyleName(realStyle);
+ }
+ return;
+ }
+
+ if (getState().getStyles() == null) {
+ getState().setStyles(new ArrayList<String>());
+ }
+ List<String> styles = getState().getStyles();
+ if (!styles.contains(style)) {
+ styles.add(style);
+ requestRepaint();
+ }
+ }
+
+ @Override
+ public void removeStyleName(String style) {
+ if (getState().getStyles() != null) {
+ String[] styleParts = style.split(" +");
+ for (String part : styleParts) {
+ if (part.length() > 0) {
+ getState().getStyles().remove(part);
+ }
+ }
+ requestRepaint();
+ }
+ }
+
+ /*
+ * Get's the component's caption. Don't add a JavaDoc comment here, we use
+ * the default documentation from implemented interface.
+ */
+ @Override
+ public String getCaption() {
+ return getState().getCaption();
+ }
+
+ /**
+ * Sets the component's caption <code>String</code>. Caption is the visible
+ * name of the component. This method will trigger a
+ * {@link RepaintRequestEvent}.
+ *
+ * @param caption
+ * the new caption <code>String</code> for the component.
+ */
+ @Override
+ public void setCaption(String caption) {
+ getState().setCaption(caption);
+ requestRepaint();
+ }
+
+ /*
+ * Don't add a JavaDoc comment here, we use the default documentation from
+ * implemented interface.
+ */
+ @Override
+ public Locale getLocale() {
+ if (locale != null) {
+ return locale;
+ }
+ HasComponents parent = getParent();
+ if (parent != null) {
+ return parent.getLocale();
+ }
+ final Application app = getApplication();
+ if (app != null) {
+ return app.getLocale();
+ }
+ return null;
+ }
+
+ /**
+ * Sets the locale of this component.
+ *
+ * <pre>
+ * // Component for which the locale is meaningful
+ * InlineDateField date = new InlineDateField(&quot;Datum&quot;);
+ *
+ * // German language specified with ISO 639-1 language
+ * // code and ISO 3166-1 alpha-2 country code.
+ * date.setLocale(new Locale(&quot;de&quot;, &quot;DE&quot;));
+ *
+ * date.setResolution(DateField.RESOLUTION_DAY);
+ * layout.addComponent(date);
+ * </pre>
+ *
+ *
+ * @param locale
+ * the locale to become this component's locale.
+ */
+ public void setLocale(Locale locale) {
+ this.locale = locale;
+
+ // FIXME: Reload value if there is a converter
+ requestRepaint();
+ }
+
+ /*
+ * Gets the component's icon resource. Don't add a JavaDoc comment here, we
+ * use the default documentation from implemented interface.
+ */
+ @Override
+ public Resource getIcon() {
+ return ResourceReference.getResource(getState().getIcon());
+ }
+
+ /**
+ * Sets the component's icon. This method will trigger a
+ * {@link RepaintRequestEvent}.
+ *
+ * @param icon
+ * the icon to be shown with the component's caption.
+ */
+ @Override
+ public void setIcon(Resource icon) {
+ getState().setIcon(ResourceReference.create(icon));
+ requestRepaint();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component#isEnabled()
+ */
+ @Override
+ public boolean isEnabled() {
+ return getState().isEnabled();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component#setEnabled(boolean)
+ */
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (getState().isEnabled() != enabled) {
+ getState().setEnabled(enabled);
+ requestRepaint();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.gwt.client.Connector#isConnectorEnabled()
+ */
+ @Override
+ public boolean isConnectorEnabled() {
+ if (!isVisible()) {
+ return false;
+ } else if (!isEnabled()) {
+ return false;
+ } else if (!super.isConnectorEnabled()) {
+ return false;
+ } else if (!getParent().isComponentVisible(this)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /*
+ * Tests if the component is in the immediate mode. Don't add a JavaDoc
+ * comment here, we use the default documentation from implemented
+ * interface.
+ */
+ public boolean isImmediate() {
+ return getState().isImmediate();
+ }
+
+ /**
+ * Sets the component's immediate mode to the specified status. This method
+ * will trigger a {@link RepaintRequestEvent}.
+ *
+ * @param immediate
+ * the boolean value specifying if the component should be in the
+ * immediate mode after the call.
+ * @see Component#isImmediate()
+ */
+ public void setImmediate(boolean immediate) {
+ getState().setImmediate(immediate);
+ requestRepaint();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component#isVisible()
+ */
+ @Override
+ public boolean isVisible() {
+ return getState().isVisible();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component#setVisible(boolean)
+ */
+ @Override
+ public void setVisible(boolean visible) {
+ if (getState().isVisible() == visible) {
+ return;
+ }
+
+ getState().setVisible(visible);
+ requestRepaint();
+ if (getParent() != null) {
+ // Must always repaint the parent (at least the hierarchy) when
+ // visibility of a child component changes.
+ getParent().requestRepaint();
+ }
+ }
+
+ /**
+ * <p>
+ * Gets the component's description, used in tooltips and can be displayed
+ * directly in certain other components such as forms. The description can
+ * be used to briefly describe the state of the component to the user. The
+ * description string may contain certain XML tags:
+ * </p>
+ *
+ * <p>
+ * <table border=1>
+ * <tr>
+ * <td width=120><b>Tag</b></td>
+ * <td width=120><b>Description</b></td>
+ * <td width=120><b>Example</b></td>
+ * </tr>
+ * <tr>
+ * <td>&lt;b></td>
+ * <td>bold</td>
+ * <td><b>bold text</b></td>
+ * </tr>
+ * <tr>
+ * <td>&lt;i></td>
+ * <td>italic</td>
+ * <td><i>italic text</i></td>
+ * </tr>
+ * <tr>
+ * <td>&lt;u></td>
+ * <td>underlined</td>
+ * <td><u>underlined text</u></td>
+ * </tr>
+ * <tr>
+ * <td>&lt;br></td>
+ * <td>linebreak</td>
+ * <td>N/A</td>
+ * </tr>
+ * <tr>
+ * <td>&lt;ul><br>
+ * &lt;li>item1<br>
+ * &lt;li>item1<br>
+ * &lt;/ul></td>
+ * <td>item list</td>
+ * <td>
+ * <ul>
+ * <li>item1
+ * <li>item2
+ * </ul>
+ * </td>
+ * </tr>
+ * </table>
+ * </p>
+ *
+ * <p>
+ * These tags may be nested.
+ * </p>
+ *
+ * @return component's description <code>String</code>
+ */
+ public String getDescription() {
+ return getState().getDescription();
+ }
+
+ /**
+ * Sets the component's description. See {@link #getDescription()} for more
+ * information on what the description is. This method will trigger a
+ * {@link RepaintRequestEvent}.
+ *
+ * The description is displayed as HTML/XHTML in tooltips or directly in
+ * certain components so care should be taken to avoid creating the
+ * possibility for HTML injection and possibly XSS vulnerabilities.
+ *
+ * @param description
+ * the new description string for the component.
+ */
+ public void setDescription(String description) {
+ getState().setDescription(description);
+ requestRepaint();
+ }
+
+ /*
+ * Gets the component's parent component. Don't add a JavaDoc comment here,
+ * we use the default documentation from implemented interface.
+ */
+ @Override
+ public HasComponents getParent() {
+ return (HasComponents) super.getParent();
+ }
+
+ @Override
+ public void setParent(ClientConnector parent) {
+ if (parent == null || parent instanceof HasComponents) {
+ super.setParent(parent);
+ } else {
+ throw new IllegalArgumentException(
+ "The parent of a Component must implement HasComponents, which "
+ + parent.getClass() + " doesn't do.");
+ }
+ }
+
+ /**
+ * Returns the closest ancestor with the given type.
+ * <p>
+ * To find the Window that contains the component, use {@code Window w =
+ * getParent(Window.class);}
+ * </p>
+ *
+ * @param <T>
+ * The type of the ancestor
+ * @param parentType
+ * The ancestor class we are looking for
+ * @return The first ancestor that can be assigned to the given class. Null
+ * if no ancestor with the correct type could be found.
+ */
+ public <T extends HasComponents> T findAncestor(Class<T> parentType) {
+ HasComponents p = getParent();
+ while (p != null) {
+ if (parentType.isAssignableFrom(p.getClass())) {
+ return parentType.cast(p);
+ }
+ p = p.getParent();
+ }
+ return null;
+ }
+
+ /**
+ * Gets the error message for this component.
+ *
+ * @return ErrorMessage containing the description of the error state of the
+ * component or null, if the component contains no errors. Extending
+ * classes should override this method if they support other error
+ * message types such as validation errors or buffering errors. The
+ * returned error message contains information about all the errors.
+ */
+ public ErrorMessage getErrorMessage() {
+ return componentError;
+ }
+
+ /**
+ * Gets the component's error message.
+ *
+ * @link Terminal.ErrorMessage#ErrorMessage(String, int)
+ *
+ * @return the component's error message.
+ */
+ public ErrorMessage getComponentError() {
+ return componentError;
+ }
+
+ /**
+ * Sets the component's error message. The message may contain certain XML
+ * tags, for more information see
+ *
+ * @link Component.ErrorMessage#ErrorMessage(String, int)
+ *
+ * @param componentError
+ * the new <code>ErrorMessage</code> of the component.
+ */
+ public void setComponentError(ErrorMessage componentError) {
+ this.componentError = componentError;
+ fireComponentErrorEvent();
+ requestRepaint();
+ }
+
+ /*
+ * Tests if the component is in read-only mode. Don't add a JavaDoc comment
+ * here, we use the default documentation from implemented interface.
+ */
+ @Override
+ public boolean isReadOnly() {
+ return getState().isReadOnly();
+ }
+
+ /*
+ * Sets the component's read-only mode. Don't add a JavaDoc comment here, we
+ * use the default documentation from implemented interface.
+ */
+ @Override
+ public void setReadOnly(boolean readOnly) {
+ getState().setReadOnly(readOnly);
+ requestRepaint();
+ }
+
+ /*
+ * Gets the parent window of the component. Don't add a JavaDoc comment
+ * here, we use the default documentation from implemented interface.
+ */
+ @Override
+ public Root getRoot() {
+ // Just make method from implemented Component interface public
+ return super.getRoot();
+ }
+
+ /*
+ * Notify the component that it's attached to a window. Don't add a JavaDoc
+ * comment here, we use the default documentation from implemented
+ * interface.
+ */
+ @Override
+ public void attach() {
+ super.attach();
+ if (delayedFocus) {
+ focus();
+ }
+ setActionManagerViewer();
+ }
+
+ /*
+ * Detach the component from application. Don't add a JavaDoc comment here,
+ * we use the default documentation from implemented interface.
+ */
+ @Override
+ public void detach() {
+ super.detach();
+ if (actionManager != null) {
+ // Remove any existing viewer. Root cast is just to make the
+ // compiler happy
+ actionManager.setViewer((Root) null);
+ }
+ }
+
+ /**
+ * Sets the focus for this component if the component is {@link Focusable}.
+ */
+ protected void focus() {
+ if (this instanceof Focusable) {
+ final Application app = getApplication();
+ if (app != null) {
+ getRoot().setFocusedComponent((Focusable) this);
+ delayedFocus = false;
+ } else {
+ delayedFocus = true;
+ }
+ }
+ }
+
+ /**
+ * Gets the application object to which the component is attached.
+ *
+ * <p>
+ * The method will return {@code null} if the component is not currently
+ * attached to an application. This is often a problem in constructors of
+ * regular components and in the initializers of custom composite
+ * components. A standard workaround is to move the problematic
+ * initialization to {@link #attach()}, as described in the documentation of
+ * the method.
+ * </p>
+ * <p>
+ * <b>This method is not meant to be overridden. Due to CDI requirements we
+ * cannot declare it as final even though it should be final.</b>
+ * </p>
+ *
+ * @return the parent application of the component or <code>null</code>.
+ * @see #attach()
+ */
+ @Override
+ public Application getApplication() {
+ // Just make method inherited from Component interface public
+ return super.getApplication();
+ }
+
+ /**
+ * Build CSS compatible string representation of height.
+ *
+ * @return CSS height
+ */
+ private String getCSSHeight() {
+ if (getHeightUnits() == Unit.PIXELS) {
+ return ((int) getHeight()) + getHeightUnits().getSymbol();
+ } else {
+ return getHeight() + getHeightUnits().getSymbol();
+ }
+ }
+
+ /**
+ * Build CSS compatible string representation of width.
+ *
+ * @return CSS width
+ */
+ private String getCSSWidth() {
+ if (getWidthUnits() == Unit.PIXELS) {
+ return ((int) getWidth()) + getWidthUnits().getSymbol();
+ } else {
+ return getWidth() + getWidthUnits().getSymbol();
+ }
+ }
+
+ /**
+ * Returns the shared state bean with information to be sent from the server
+ * to the client.
+ *
+ * Subclasses should override this method and set any relevant fields of the
+ * state returned by super.getState().
+ *
+ * @since 7.0
+ *
+ * @return updated component shared state
+ */
+ @Override
+ public ComponentState getState() {
+ return (ComponentState) super.getState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.Component#updateState()
+ */
+ @Override
+ public void updateState() {
+ // TODO This logic should be on the client side and the state should
+ // simply be a data object with "width" and "height".
+ if (getHeight() >= 0
+ && (getHeightUnits() != Unit.PERCENTAGE || ComponentSizeValidator
+ .parentCanDefineHeight(this))) {
+ getState().setHeight("" + getCSSHeight());
+ } else {
+ getState().setHeight("");
+ }
+
+ if (getWidth() >= 0
+ && (getWidthUnits() != Unit.PERCENTAGE || ComponentSizeValidator
+ .parentCanDefineWidth(this))) {
+ getState().setWidth("" + getCSSWidth());
+ } else {
+ getState().setWidth("");
+ }
+
+ ErrorMessage error = getErrorMessage();
+ if (null != error) {
+ getState().setErrorMessage(error.getFormattedHtmlMessage());
+ } else {
+ getState().setErrorMessage(null);
+ }
+ }
+
+ /* Documentation copied from interface */
+ @Override
+ public void requestRepaint() {
+ // Invisible components (by flag in this particular component) do not
+ // need repaints
+ if (!getState().isVisible()) {
+ return;
+ }
+ super.requestRepaint();
+ }
+
+ /* General event framework */
+
+ private static final Method COMPONENT_EVENT_METHOD = ReflectTools
+ .findMethod(Component.Listener.class, "componentEvent",
+ Component.Event.class);
+
+ /**
+ * <p>
+ * Registers a new listener with the specified activation method to listen
+ * events generated by this component. If the activation method does not
+ * have any arguments the event object will not be passed to it when it's
+ * called.
+ * </p>
+ *
+ * <p>
+ * This method additionally informs the event-api to route events with the
+ * given eventIdentifier to the components handleEvent function call.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventIdentifier
+ * the identifier of the event to listen for
+ * @param eventType
+ * the type of the listened event. Events of this type or its
+ * subclasses activate the listener.
+ * @param target
+ * the object instance who owns the activation method.
+ * @param method
+ * the activation method.
+ *
+ * @since 6.2
+ */
+ protected void addListener(String eventIdentifier, Class<?> eventType,
+ Object target, Method method) {
+ if (eventRouter == null) {
+ eventRouter = new EventRouter();
+ }
+ boolean needRepaint = !eventRouter.hasListeners(eventType);
+ eventRouter.addListener(eventType, target, method);
+
+ if (needRepaint) {
+ getState().addRegisteredEventListener(eventIdentifier);
+ requestRepaint();
+ }
+ }
+
+ /**
+ * Checks if the given {@link Event} type is listened for this component.
+ *
+ * @param eventType
+ * the event type to be checked
+ * @return true if a listener is registered for the given event type
+ */
+ protected boolean hasListeners(Class<?> eventType) {
+ return eventRouter != null && eventRouter.hasListeners(eventType);
+ }
+
+ /**
+ * Removes all registered listeners matching the given parameters. Since
+ * this method receives the event type and the listener object as
+ * parameters, it will unregister all <code>object</code>'s methods that are
+ * registered to listen to events of type <code>eventType</code> generated
+ * by this component.
+ *
+ * <p>
+ * This method additionally informs the event-api to stop routing events
+ * with the given eventIdentifier to the components handleEvent function
+ * call.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventIdentifier
+ * the identifier of the event to stop listening for
+ * @param eventType
+ * the exact event type the <code>object</code> listens to.
+ * @param target
+ * the target object that has registered to listen to events of
+ * type <code>eventType</code> with one or more methods.
+ *
+ * @since 6.2
+ */
+ protected void removeListener(String eventIdentifier, Class<?> eventType,
+ Object target) {
+ if (eventRouter != null) {
+ eventRouter.removeListener(eventType, target);
+ if (!eventRouter.hasListeners(eventType)) {
+ getState().removeRegisteredEventListener(eventIdentifier);
+ requestRepaint();
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Registers a new listener with the specified activation method to listen
+ * events generated by this component. If the activation method does not
+ * have any arguments the event object will not be passed to it when it's
+ * called.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the type of the listened event. Events of this type or its
+ * subclasses activate the listener.
+ * @param target
+ * the object instance who owns the activation method.
+ * @param method
+ * the activation method.
+ */
+ @Override
+ public void addListener(Class<?> eventType, Object target, Method method) {
+ if (eventRouter == null) {
+ eventRouter = new EventRouter();
+ }
+ eventRouter.addListener(eventType, target, method);
+ }
+
+ /**
+ * <p>
+ * Convenience method for registering a new listener with the specified
+ * activation method to listen events generated by this component. If the
+ * activation method does not have any arguments the event object will not
+ * be passed to it when it's called.
+ * </p>
+ *
+ * <p>
+ * This version of <code>addListener</code> gets the name of the activation
+ * method as a parameter. The actual method is reflected from
+ * <code>object</code>, and unless exactly one match is found,
+ * <code>java.lang.IllegalArgumentException</code> is thrown.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * <p>
+ * Note: Using this method is discouraged because it cannot be checked
+ * during compilation. Use {@link #addListener(Class, Object, Method)} or
+ * {@link #addListener(com.vaadin.ui.Component.Listener)} instead.
+ * </p>
+ *
+ * @param eventType
+ * the type of the listened event. Events of this type or its
+ * subclasses activate the listener.
+ * @param target
+ * the object instance who owns the activation method.
+ * @param methodName
+ * the name of the activation method.
+ */
+ @Override
+ public void addListener(Class<?> eventType, Object target, String methodName) {
+ if (eventRouter == null) {
+ eventRouter = new EventRouter();
+ }
+ eventRouter.addListener(eventType, target, methodName);
+ }
+
+ /**
+ * Removes all registered listeners matching the given parameters. Since
+ * this method receives the event type and the listener object as
+ * parameters, it will unregister all <code>object</code>'s methods that are
+ * registered to listen to events of type <code>eventType</code> generated
+ * by this component.
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the exact event type the <code>object</code> listens to.
+ * @param target
+ * the target object that has registered to listen to events of
+ * type <code>eventType</code> with one or more methods.
+ */
+ @Override
+ public void removeListener(Class<?> eventType, Object target) {
+ if (eventRouter != null) {
+ eventRouter.removeListener(eventType, target);
+ }
+ }
+
+ /**
+ * Removes one registered listener method. The given method owned by the
+ * given object will no longer be called when the specified events are
+ * generated by this component.
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the exact event type the <code>object</code> listens to.
+ * @param target
+ * target object that has registered to listen to events of type
+ * <code>eventType</code> with one or more methods.
+ * @param method
+ * the method owned by <code>target</code> that's registered to
+ * listen to events of type <code>eventType</code>.
+ */
+ @Override
+ public void removeListener(Class<?> eventType, Object target, Method method) {
+ if (eventRouter != null) {
+ eventRouter.removeListener(eventType, target, method);
+ }
+ }
+
+ /**
+ * <p>
+ * Removes one registered listener method. The given method owned by the
+ * given object will no longer be called when the specified events are
+ * generated by this component.
+ * </p>
+ *
+ * <p>
+ * This version of <code>removeListener</code> gets the name of the
+ * activation method as a parameter. The actual method is reflected from
+ * <code>target</code>, and unless exactly one match is found,
+ * <code>java.lang.IllegalArgumentException</code> is thrown.
+ * </p>
+ *
+ * <p>
+ * For more information on the inheritable event mechanism see the
+ * {@link com.vaadin.event com.vaadin.event package documentation}.
+ * </p>
+ *
+ * @param eventType
+ * the exact event type the <code>object</code> listens to.
+ * @param target
+ * the target object that has registered to listen to events of
+ * type <code>eventType</code> with one or more methods.
+ * @param methodName
+ * the name of the method owned by <code>target</code> that's
+ * registered to listen to events of type <code>eventType</code>.
+ */
+ @Override
+ public void removeListener(Class<?> eventType, Object target,
+ String methodName) {
+ if (eventRouter != null) {
+ eventRouter.removeListener(eventType, target, methodName);
+ }
+ }
+
+ /**
+ * Returns all listeners that are registered for the given event type or one
+ * of its subclasses.
+ *
+ * @param eventType
+ * The type of event to return listeners for.
+ * @return A collection with all registered listeners. Empty if no listeners
+ * are found.
+ */
+ public Collection<?> getListeners(Class<?> eventType) {
+ if (eventRouter == null) {
+ return Collections.EMPTY_LIST;
+ }
+
+ return eventRouter.getListeners(eventType);
+ }
+
+ /**
+ * Sends the event to all listeners.
+ *
+ * @param event
+ * the Event to be sent to all listeners.
+ */
+ protected void fireEvent(Component.Event event) {
+ if (eventRouter != null) {
+ eventRouter.fireEvent(event);
+ }
+
+ }
+
+ /* Component event framework */
+
+ /*
+ * Registers a new listener to listen events generated by this component.
+ * Don't add a JavaDoc comment here, we use the default documentation from
+ * implemented interface.
+ */
+ @Override
+ public void addListener(Component.Listener listener) {
+ addListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
+ }
+
+ /*
+ * Removes a previously registered listener from this component. Don't add a
+ * JavaDoc comment here, we use the default documentation from implemented
+ * interface.
+ */
+ @Override
+ public void removeListener(Component.Listener listener) {
+ removeListener(Component.Event.class, listener, COMPONENT_EVENT_METHOD);
+ }
+
+ /**
+ * Emits the component event. It is transmitted to all registered listeners
+ * interested in such events.
+ */
+ protected void fireComponentEvent() {
+ fireEvent(new Component.Event(this));
+ }
+
+ /**
+ * Emits the component error event. It is transmitted to all registered
+ * listeners interested in such events.
+ */
+ protected void fireComponentErrorEvent() {
+ fireEvent(new Component.ErrorEvent(getComponentError(), this));
+ }
+
+ /**
+ * Sets the data object, that can be used for any application specific data.
+ * The component does not use or modify this data.
+ *
+ * @param data
+ * the Application specific data.
+ * @since 3.1
+ */
+ public void setData(Object data) {
+ applicationData = data;
+ }
+
+ /**
+ * Gets the application specific data. See {@link #setData(Object)}.
+ *
+ * @return the Application specific data set with setData function.
+ * @since 3.1
+ */
+ public Object getData() {
+ return applicationData;
+ }
+
+ /* Sizeable and other size related methods */
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#getHeight()
+ */
+ @Override
+ public float getHeight() {
+ return height;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#getHeightUnits()
+ */
+ @Override
+ public Unit getHeightUnits() {
+ return heightUnit;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#getWidth()
+ */
+ @Override
+ public float getWidth() {
+ return width;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#getWidthUnits()
+ */
+ @Override
+ public Unit getWidthUnits() {
+ return widthUnit;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#setHeight(float, Unit)
+ */
+ @Override
+ public void setHeight(float height, Unit unit) {
+ if (unit == null) {
+ throw new IllegalArgumentException("Unit can not be null");
+ }
+ this.height = height;
+ heightUnit = unit;
+ requestRepaint();
+ // ComponentSizeValidator.setHeightLocation(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#setSizeFull()
+ */
+ @Override
+ public void setSizeFull() {
+ setWidth(100, Unit.PERCENTAGE);
+ setHeight(100, Unit.PERCENTAGE);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#setSizeUndefined()
+ */
+ @Override
+ public void setSizeUndefined() {
+ setWidth(-1, Unit.PIXELS);
+ setHeight(-1, Unit.PIXELS);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#setWidth(float, Unit)
+ */
+ @Override
+ public void setWidth(float width, Unit unit) {
+ if (unit == null) {
+ throw new IllegalArgumentException("Unit can not be null");
+ }
+ this.width = width;
+ widthUnit = unit;
+ requestRepaint();
+ // ComponentSizeValidator.setWidthLocation(this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#setWidth(java.lang.String)
+ */
+ @Override
+ public void setWidth(String width) {
+ Size size = parseStringSize(width);
+ if (size != null) {
+ setWidth(size.getSize(), size.getUnit());
+ } else {
+ setWidth(-1, Unit.PIXELS);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.terminal.Sizeable#setHeight(java.lang.String)
+ */
+ @Override
+ public void setHeight(String height) {
+ Size size = parseStringSize(height);
+ if (size != null) {
+ setHeight(size.getSize(), size.getUnit());
+ } else {
+ setHeight(-1, Unit.PIXELS);
+ }
+ }
+
+ /*
+ * Returns array with size in index 0 unit in index 1. Null or empty string
+ * will produce {-1,Unit#PIXELS}
+ */
+ private static Size parseStringSize(String s) {
+ if (s == null) {
+ return null;
+ }
+ s = s.trim();
+ if ("".equals(s)) {
+ return null;
+ }
+ float size = 0;
+ Unit unit = null;
+ Matcher matcher = sizePattern.matcher(s);
+ if (matcher.find()) {
+ size = Float.parseFloat(matcher.group(1));
+ if (size < 0) {
+ size = -1;
+ unit = Unit.PIXELS;
+ } else {
+ String symbol = matcher.group(3);
+ unit = Unit.getUnitFromSymbol(symbol);
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid size argument: \"" + s
+ + "\" (should match " + sizePattern.pattern() + ")");
+ }
+ return new Size(size, unit);
+ }
+
+ private static class Size implements Serializable {
+ float size;
+ Unit unit;
+
+ public Size(float size, Unit unit) {
+ this.size = size;
+ this.unit = unit;
+ }
+
+ public float getSize() {
+ return size;
+ }
+
+ public Unit getUnit() {
+ return unit;
+ }
+ }
+
+ public interface ComponentErrorEvent extends Terminal.ErrorEvent {
+ }
+
+ public interface ComponentErrorHandler extends Serializable {
+ /**
+ * Handle the component error
+ *
+ * @param event
+ * @return True if the error has been handled False, otherwise
+ */
+ public boolean handleComponentError(ComponentErrorEvent event);
+ }
+
+ /**
+ * Gets the error handler for the component.
+ *
+ * The error handler is dispatched whenever there is an error processing the
+ * data coming from the client.
+ *
+ * @return
+ */
+ public ComponentErrorHandler getErrorHandler() {
+ return errorHandler;
+ }
+
+ /**
+ * Sets the error handler for the component.
+ *
+ * The error handler is dispatched whenever there is an error processing the
+ * data coming from the client.
+ *
+ * If the error handler is not set, the application error handler is used to
+ * handle the exception.
+ *
+ * @param errorHandler
+ * AbstractField specific error handler
+ */
+ public void setErrorHandler(ComponentErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ /**
+ * Handle the component error event.
+ *
+ * @param error
+ * Error event to handle
+ * @return True if the error has been handled False, otherwise. If the error
+ * haven't been handled by this component, it will be handled in the
+ * application error handler.
+ */
+ public boolean handleError(ComponentErrorEvent error) {
+ if (errorHandler != null) {
+ return errorHandler.handleComponentError(error);
+ }
+ return false;
+
+ }
+
+ /*
+ * Actions
+ */
+
+ /**
+ * Gets the {@link ActionManager} used to manage the
+ * {@link ShortcutListener}s added to this {@link Field}.
+ *
+ * @return the ActionManager in use
+ */
+ protected ActionManager getActionManager() {
+ if (actionManager == null) {
+ actionManager = new ActionManager();
+ setActionManagerViewer();
+ }
+ return actionManager;
+ }
+
+ /**
+ * Set a viewer for the action manager to be the parent sub window (if the
+ * component is in a window) or the root (otherwise). This is still a
+ * simplification of the real case as this should be handled by the parent
+ * VOverlay (on the client side) if the component is inside an VOverlay
+ * component.
+ */
+ private void setActionManagerViewer() {
+ if (actionManager != null && getRoot() != null) {
+ // Attached and has action manager
+ Window w = findAncestor(Window.class);
+ if (w != null) {
+ actionManager.setViewer(w);
+ } else {
+ actionManager.setViewer(getRoot());
+ }
+ }
+
+ }
+
+ public void addShortcutListener(ShortcutListener shortcut) {
+ getActionManager().addAction(shortcut);
+ }
+
+ public void removeShortcutListener(ShortcutListener shortcut) {
+ if (actionManager != null) {
+ actionManager.removeAction(shortcut);
+ }
+ }
+}