All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.ui.AbstractComponentContainer Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * Copyright (C) 2000-2024 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * license.
 */

package com.vaadin.ui;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;

import com.vaadin.server.ComponentSizeValidator;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.AbstractComponentContainerState;

/**
 * Extension to {@link AbstractComponent} that defines the default
 * implementation for the methods in {@link ComponentContainer}. Basic UI
 * components that need to contain other components inherit this class to easily
 * qualify as a component container.
 *
 * @author Vaadin Ltd
 * @since 3.0
 */
@SuppressWarnings("serial")
public abstract class AbstractComponentContainer extends AbstractComponent
        implements ComponentContainer {

    /**
     * Constructs a new component container.
     */
    public AbstractComponentContainer() {
        super();
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.vaadin.ui.ComponentContainer#addComponents(com.vaadin.ui.Component[])
     */
    @Override
    public void addComponents(Component... components) {
        for (Component c : components) {
            addComponent(c);
        }
    }

    /**
     * Removes all components from the container. This should probably be
     * re-implemented in extending classes for a more powerful implementation.
     */
    @Override
    public void removeAllComponents() {
        final LinkedList l = new LinkedList<>();

        // Adds all components
        for (final Iterator i = getComponentIterator(); i
                .hasNext();) {
            l.add(i.next());
        }

        // Removes all component
        for (Component aL : l) {
            removeComponent(aL);
        }
    }

    /*
     * Moves all components from an another container into this container. Don't
     * add a JavaDoc comment here, we use the default documentation from
     * implemented interface.
     */
    @Override
    public void moveComponentsFrom(ComponentContainer source) {
        final LinkedList components = new LinkedList<>();
        for (final Iterator i = source.getComponentIterator(); i
                .hasNext();) {
            components.add(i.next());
        }

        for (final Component c : components) {
            source.removeComponent(c);
            addComponent(c);
        }
    }

    /* documented in interface */
    @Override
    public Registration addComponentAttachListener(
            ComponentAttachListener listener) {
        return addListener(ComponentAttachEvent.class, listener,
                ComponentAttachListener.attachMethod);
    }

    /* documented in interface */
    @Override
    @Deprecated
    public void removeComponentAttachListener(
            ComponentAttachListener listener) {
        removeListener(ComponentAttachEvent.class, listener,
                ComponentAttachListener.attachMethod);
    }

    /* documented in interface */
    @Override
    public Registration addComponentDetachListener(
            ComponentDetachListener listener) {
        return addListener(ComponentDetachEvent.class, listener,
                ComponentDetachListener.detachMethod);
    }

    /* documented in interface */
    @Override
    @Deprecated
    public void removeComponentDetachListener(
            ComponentDetachListener listener) {
        removeListener(ComponentDetachEvent.class, listener,
                ComponentDetachListener.detachMethod);
    }

    /**
     * Fires the component attached event. This should be called by the
     * addComponent methods after the component have been added to this
     * container.
     *
     * @param component
     *            the component that has been added to this container.
     */
    protected void fireComponentAttachEvent(Component component) {
        fireEvent(new ComponentAttachEvent(this, component));
    }

    /**
     * Fires the component detached event. This should be called by the
     * removeComponent methods after the component have been removed from this
     * container.
     *
     * @param component
     *            the component that has been removed from this container.
     */
    protected void fireComponentDetachEvent(Component component) {
        fireEvent(new ComponentDetachEvent(this, component));
    }

    /**
     * This only implements the events and component parent calls. The extending
     * classes must implement component list maintenance and call this method
     * after component list maintenance.
     *
     * @see com.vaadin.ui.ComponentContainer#addComponent(Component)
     */
    @Override
    public void addComponent(Component c) {
        // Make sure we're not adding the component inside it's own content
        if (isOrHasAncestor(c)) {
            throw new IllegalArgumentException(
                    "Component cannot be added inside it's own content");
        }

        if (c.getParent() != null) {
            // If the component already has a parent, try to remove it
            AbstractSingleComponentContainer.removeFromParent(c);
        }

        c.setParent(this);
        fireComponentAttachEvent(c);
        markAsDirty();
    }

    /**
     * This only implements the events and component parent calls. The extending
     * classes must implement component list maintenance and call this method
     * before component list maintenance.
     *
     * @see com.vaadin.ui.ComponentContainer#removeComponent(Component)
     */
    @Override
    public void removeComponent(Component c) {
        if (equals(c.getParent())) {
            c.setParent(null);
            fireComponentDetachEvent(c);
            markAsDirty();
        }
    }

    @Override
    public void setWidth(float width, Unit unit) {
        /*
         * child tree repaints may be needed, due to our fall back support for
         * invalid relative sizes
         */
        Collection dirtyChildren = null;
        boolean childrenMayBecomeUndefined = false;
        if (getWidth() == SIZE_UNDEFINED && width != SIZE_UNDEFINED) {
            // children currently in invalid state may need repaint
            dirtyChildren = getInvalidSizedChildren(false);
        } else if ((width == SIZE_UNDEFINED && getWidth() != SIZE_UNDEFINED)
                || (unit == Unit.PERCENTAGE
                        && getWidthUnits() != Unit.PERCENTAGE
                        && !ComponentSizeValidator
                                .parentCanDefineWidth(this))) {
            /*
             * relative width children may get to invalid state if width becomes
             * invalid. Width may also become invalid if units become percentage
             * due to the fallback support
             */
            childrenMayBecomeUndefined = true;
            dirtyChildren = getInvalidSizedChildren(false);
        }
        super.setWidth(width, unit);
        repaintChangedChildTrees(dirtyChildren, childrenMayBecomeUndefined,
                false);
    }

    private void repaintChangedChildTrees(Collection invalidChildren,
            boolean childrenMayBecomeUndefined, boolean vertical) {
        if (childrenMayBecomeUndefined) {
            Collection previouslyInvalidComponents = invalidChildren;
            invalidChildren = getInvalidSizedChildren(vertical);
            if (previouslyInvalidComponents != null
                    && invalidChildren != null) {
                for (Iterator iterator = invalidChildren
                        .iterator(); iterator.hasNext();) {
                    Component component = iterator.next();
                    if (previouslyInvalidComponents.contains(component)) {
                        // still invalid don't repaint
                        iterator.remove();
                    }
                }
            }
        } else if (invalidChildren != null) {
            Collection stillInvalidChildren = getInvalidSizedChildren(
                    vertical);
            if (stillInvalidChildren != null) {
                for (Component component : stillInvalidChildren) {
                    // didn't become valid
                    invalidChildren.remove(component);
                }
            }
        }
        if (invalidChildren != null) {
            repaintChildTrees(invalidChildren);
        }
    }

    private Collection getInvalidSizedChildren(
            final boolean vertical) {
        HashSet components = null;
        for (Component component : this) {
            boolean valid = vertical
                    ? ComponentSizeValidator.checkHeights(component)
                    : ComponentSizeValidator.checkWidths(component);
            if (!valid) {
                if (components == null) {
                    components = new HashSet<>();
                }
                components.add(component);
            }
        }
        return components;
    }

    private void repaintChildTrees(Collection dirtyChildren) {
        for (Component c : dirtyChildren) {
            c.markAsDirtyRecursive();
        }
    }

    @Override
    public void setHeight(float height, Unit unit) {
        /*
         * child tree repaints may be needed, due to our fall back support for
         * invalid relative sizes
         */
        Collection dirtyChildren = null;
        boolean childrenMayBecomeUndefined = false;
        if (getHeight() == SIZE_UNDEFINED && height != SIZE_UNDEFINED) {
            // children currently in invalid state may need repaint
            dirtyChildren = getInvalidSizedChildren(true);
        } else if ((height == SIZE_UNDEFINED && getHeight() != SIZE_UNDEFINED)
                || (unit == Unit.PERCENTAGE
                        && getHeightUnits() != Unit.PERCENTAGE
                        && !ComponentSizeValidator
                                .parentCanDefineHeight(this))) {
            /*
             * relative height children may get to invalid state if height
             * becomes invalid. Height may also become invalid if units become
             * percentage due to the fallback support.
             */
            childrenMayBecomeUndefined = true;
            dirtyChildren = getInvalidSizedChildren(true);
        }
        super.setHeight(height, unit);
        repaintChangedChildTrees(dirtyChildren, childrenMayBecomeUndefined,
                true);
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated As of 7.0, use {@link #iterator()} instead.
     */
    @Deprecated
    @Override
    public Iterator getComponentIterator() {
        return iterator();
    }

    @Override
    protected AbstractComponentContainerState getState() {
        return (AbstractComponentContainerState) super.getState();
    }

    @Override
    protected AbstractComponentContainerState getState(boolean markAsDirty) {
        return (AbstractComponentContainerState) super.getState(markAsDirty);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy