123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- /*
- * Copyright 2000-2014 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- package com.vaadin.ui;
-
- import java.util.Collections;
- import java.util.Iterator;
-
- import org.jsoup.nodes.Element;
-
- import com.vaadin.server.ComponentSizeValidator;
- import com.vaadin.server.VaadinService;
- import com.vaadin.server.VaadinSession;
- import com.vaadin.ui.declarative.DesignContext;
- import com.vaadin.ui.declarative.DesignException;
-
- /**
- * Abstract base class for component containers that have only one child
- * component.
- *
- * For component containers that support multiple children, inherit
- * {@link AbstractComponentContainer} instead of this class.
- *
- * @since 7.0
- */
- public abstract class AbstractSingleComponentContainer extends
- AbstractComponent implements SingleComponentContainer {
-
- private Component content;
-
- @Override
- public int getComponentCount() {
- return (content != null) ? 1 : 0;
- }
-
- @Override
- public Iterator<Component> iterator() {
- if (content != null) {
- return Collections.singletonList(content).iterator();
- } else {
- return Collections.<Component> emptyList().iterator();
- }
- }
-
- /* documented in interface */
- @Override
- public void addComponentAttachListener(ComponentAttachListener listener) {
- addListener(ComponentAttachEvent.class, listener,
- ComponentAttachListener.attachMethod);
-
- }
-
- /* documented in interface */
- @Override
- public void removeComponentAttachListener(ComponentAttachListener listener) {
- removeListener(ComponentAttachEvent.class, listener,
- ComponentAttachListener.attachMethod);
- }
-
- /* documented in interface */
- @Override
- public void addComponentDetachListener(ComponentDetachListener listener) {
- addListener(ComponentDetachEvent.class, listener,
- ComponentDetachListener.detachMethod);
- }
-
- /* documented in interface */
- @Override
- public void removeComponentDetachListener(ComponentDetachListener listener) {
- removeListener(ComponentDetachEvent.class, listener,
- ComponentDetachListener.detachMethod);
- }
-
- /**
- * Fires the component attached event. This is called by the
- * {@link #setContent(Component)} method after the component has been set as
- * the content.
- *
- * @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 is called by the
- * {@link #setContent(Component)} method after the content component has
- * been replaced by other content.
- *
- * @param component
- * the component that has been removed from this container.
- */
- protected void fireComponentDetachEvent(Component component) {
- fireEvent(new ComponentDetachEvent(this, component));
- }
-
- @Override
- public Component getContent() {
- return content;
- }
-
- /**
- * Sets the content of this container. The content is a component that
- * serves as the outermost item of the visual contents.
- *
- * The content must always be set, either with a constructor parameter or by
- * calling this method.
- *
- * Previous versions of Vaadin used a {@link VerticalLayout} with margins
- * enabled as the default content but that is no longer the case.
- *
- * @param content
- * a component (typically a layout) to use as content
- */
- @Override
- public void setContent(Component content) {
- // Make sure we're not adding the component inside it's own content
- if (isOrHasAncestor(content)) {
- throw new IllegalArgumentException(
- "Component cannot be added inside it's own content");
- }
-
- Component oldContent = getContent();
- if (oldContent == content) {
- // do not set the same content twice
- return;
- }
- if (oldContent != null && equals(oldContent.getParent())) {
- oldContent.setParent(null);
- fireComponentDetachEvent(oldContent);
- }
- this.content = content;
- if (content != null) {
- removeFromParent(content);
-
- content.setParent(this);
- fireComponentAttachEvent(content);
- }
-
- markAsDirty();
- }
-
- /**
- * Utility method for removing a component from its parent (if possible).
- *
- * @param content
- * component to remove
- */
- // TODO move utility method elsewhere?
- public static void removeFromParent(Component content)
- throws IllegalArgumentException {
- // Verify the appropriate session is locked
- UI parentUI = content.getUI();
- if (parentUI != null) {
- VaadinSession parentSession = parentUI.getSession();
- if (parentSession != null && !parentSession.hasLock()) {
- String message = "Cannot remove from parent when the session is not locked.";
- if (VaadinService.isOtherSessionLocked(parentSession)) {
- message += " Furthermore, there is another locked session, indicating that the component might be about to be moved from one session to another.";
- }
- throw new IllegalStateException(message);
- }
- }
-
- HasComponents parent = content.getParent();
- if (parent instanceof ComponentContainer) {
- // If the component already has a parent, try to remove it
- ComponentContainer oldParent = (ComponentContainer) parent;
- oldParent.removeComponent(content);
- } else if (parent instanceof SingleComponentContainer) {
- SingleComponentContainer oldParent = (SingleComponentContainer) parent;
- if (oldParent.getContent() == content) {
- oldParent.setContent(null);
- }
- } else if (parent != null) {
- throw new IllegalArgumentException(
- "Content is already attached to another parent");
- }
- }
-
- // the setHeight()/setWidth() methods duplicated and simplified from
- // AbstractComponentContainer
-
- @Override
- public void setWidth(float width, Unit unit) {
- /*
- * child tree repaints may be needed, due to our fall back support for
- * invalid relative sizes
- */
- boolean dirtyChild = false;
- boolean childrenMayBecomeUndefined = false;
- if (getWidth() == SIZE_UNDEFINED && width != SIZE_UNDEFINED) {
- // children currently in invalid state may need repaint
- dirtyChild = getInvalidSizedChild(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;
- dirtyChild = getInvalidSizedChild(false);
- }
- super.setWidth(width, unit);
- repaintChangedChildTree(dirtyChild, childrenMayBecomeUndefined, false);
- }
-
- private void repaintChangedChildTree(boolean invalidChild,
- boolean childrenMayBecomeUndefined, boolean vertical) {
- if (getContent() == null) {
- return;
- }
- boolean needRepaint = false;
- if (childrenMayBecomeUndefined) {
- // if became invalid now
- needRepaint = !invalidChild && getInvalidSizedChild(vertical);
- } else if (invalidChild) {
- // if not still invalid
- needRepaint = !getInvalidSizedChild(vertical);
- }
- if (needRepaint) {
- getContent().markAsDirtyRecursive();
- }
- }
-
- private boolean getInvalidSizedChild(final boolean vertical) {
- Component content = getContent();
- if (content == null) {
- return false;
- }
- if (vertical) {
- return !ComponentSizeValidator.checkHeights(content);
- } else {
- return !ComponentSizeValidator.checkWidths(content);
- }
- }
-
- @Override
- public void setHeight(float height, Unit unit) {
- /*
- * child tree repaints may be needed, due to our fall back support for
- * invalid relative sizes
- */
- boolean dirtyChild = false;
- boolean childrenMayBecomeUndefined = false;
- if (getHeight() == SIZE_UNDEFINED && height != SIZE_UNDEFINED) {
- // children currently in invalid state may need repaint
- dirtyChild = getInvalidSizedChild(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;
- dirtyChild = getInvalidSizedChild(true);
- }
- super.setHeight(height, unit);
- repaintChangedChildTree(dirtyChild, childrenMayBecomeUndefined, true);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractComponent#readDesign(org.jsoup.nodes .Element,
- * com.vaadin.ui.declarative.DesignContext)
- */
- @Override
- public void readDesign(Element design, DesignContext designContext) {
- // process default attributes
- super.readDesign(design, designContext);
- // handle child element, checking that the design specifies at most one
- // child
- int childCount = design.children().size();
- if (childCount > 1) {
- throw new DesignException("The container of type "
- + getClass().toString()
- + " can have only one child component.");
- } else if (childCount == 1) {
- Element childElement = design.children().get(0);
- Component newChild = designContext.readDesign(childElement);
- setContent(newChild);
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractComponent#writeDesign(org.jsoup.nodes.Element
- * , com.vaadin.ui.declarative.DesignContext)
- */
- @Override
- public void writeDesign(Element design, DesignContext designContext) {
- // write default attributes (also clears children and attributes)
- super.writeDesign(design, designContext);
- AbstractSingleComponentContainer def = designContext
- .getDefaultInstance(this);
- if (!designContext.shouldWriteChildren(this, def)) {
- return;
- }
- // handle child component
- Component child = getContent();
- if (child != null) {
- Element childNode = designContext.createElement(child);
- design.appendChild(childNode);
- }
- }
- }
|