diff options
Diffstat (limited to 'server')
3 files changed, 408 insertions, 1 deletions
diff --git a/server/src/main/java/com/vaadin/ui/ComponentRootSetter.java b/server/src/main/java/com/vaadin/ui/ComponentRootSetter.java new file mode 100644 index 0000000000..1bc3565de8 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/ComponentRootSetter.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2016 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.io.Serializable; + +/** + * Internal utility class. + * + * @since + * @author Vaadin Ltd + */ +public class ComponentRootSetter implements Serializable { + + private ComponentRootSetter() { + // Util methods only + } + + /** + * Sets the composition root for the given custom component or composite. + * <p> + * For internal use only. + * + * @param customComponent + * the custom component or composite + * @param component + * the component to assign as composition root + */ + public static void setRoot(Component customComponent, Component component) { + if (customComponent instanceof CustomComponent) { + ((CustomComponent) customComponent).setCompositionRoot(component); + } else if (customComponent instanceof Composite) { + ((Composite) customComponent).setCompositionRoot(component); + } else { + throw new IllegalArgumentException( + "Parameter is of an unsupported type: " + + customComponent.getClass().getName()); + } + } + +} diff --git a/server/src/main/java/com/vaadin/ui/Composite.java b/server/src/main/java/com/vaadin/ui/Composite.java new file mode 100644 index 0000000000..10bcff0510 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/Composite.java @@ -0,0 +1,339 @@ +/* + * Copyright 2000-2016 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 java.util.Objects; +import java.util.Optional; + +import com.vaadin.server.ErrorMessage; +import com.vaadin.server.Resource; +import com.vaadin.shared.composite.CompositeState; +import com.vaadin.shared.ui.ContentMode; + +/** + * Composite allows creating new UI components by composition of existing + * server-side components. + * <p> + * A composite is created by extending the Composite class and setting the + * composition root component using {@link #setCompositionRoot(Component)}. + * </p> + * <p> + * The composition root itself can contain more components. The advantage of + * wrapping it in a composite is that the details of the composition root, such + * as its public API, are hidden from the users of the composite. + * </p> + * <p> + * A composite itself does not contribute to the DOM in any way (contrary to + * {@link CustomComponent} which adds a {@code <div>} to the DOM. + * </p> + * + * @author Vaadin Ltd. + * @since + */ +public class Composite extends AbstractComponent implements HasComponents { + + private static final String COMPOSITE_HAS_NO_DOM_OR_WIDGET = "A composite has no DOM or widget"; + /** + * The contained component. + */ + private Component root = null; + + /** + * Constructs a new empty composite. + * <p> + * Use {@link #setCompositionRoot(Component)} to define the contents of the + * composite. + */ + public Composite() { + } + + /** + * Constructs a new composite containing the given component. + * + * @param compositionRoot + * the root of the composition component tree. It must not be + * null. + */ + public Composite(AbstractComponent compositionRoot) { + this(); + Objects.requireNonNull(compositionRoot); + setCompositionRoot(compositionRoot); + } + + /** + * Returns the composition root. + * + * @return the Component Composition root. + */ + protected Component getCompositionRoot() { + return root; + } + + /** + * Sets the component contained in the composite. + * <p> + * You must set the composition root to a non-null value before the + * component can be used. It cannot be changed. + * </p> + * + * @param compositionRoot + * the root of the composition component tree. + */ + protected void setCompositionRoot(Component compositionRoot) { + if (root != null) { + throw new IllegalStateException( + "Composition root cannot be changed"); + } + if (compositionRoot == null) { + throw new IllegalArgumentException( + "Composition root cannot be null"); + } + + // set new component + if (compositionRoot.getParent() != null) { + // If the component already has a parent, try to remove it + AbstractSingleComponentContainer.removeFromParent(compositionRoot); + } + + compositionRoot.setParent(this); + root = compositionRoot; + markAsDirty(); + } + + /* Basic component features ------------------------------------------ */ + + @Override + public Iterator<Component> iterator() { + if (getCompositionRoot() != null) { + return Collections.singletonList(getCompositionRoot()).iterator(); + } else { + return Collections.<Component> emptyList().iterator(); + } + } + + /** + * Gets the number of contained components. + * + * @return the number of contained components (zero or one) + */ + public int getComponentCount() { + return (getCompositionRoot() != null ? 1 : 0); + } + + @Override + protected CompositeState getState() { + return (CompositeState) super.getState(); + } + + @Override + protected CompositeState getState(boolean markAsDirty) { + return (CompositeState) super.getState(markAsDirty); + } + + @Override + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); + if (getComponentCount() != 1) { + throw new IllegalStateException( + "A composite must always have a composition root"); + } + } + + @Override + public String getStyleName() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setStyleName(String style) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setStyleName(String style, boolean add) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void addStyleName(String style) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void removeStyleName(String style) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public String getPrimaryStyleName() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setPrimaryStyleName(String style) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + private Component getRootOrThrow() { + return Optional.ofNullable(getCompositionRoot()) + .orElseThrow(() -> new IllegalStateException( + "Composition root has not been set")); + } + + @Override + public float getWidth() { + return getRootOrThrow().getWidth(); + } + + @Override + public float getHeight() { + return getRootOrThrow().getHeight(); + } + + @Override + public Unit getWidthUnits() { + return getRootOrThrow().getWidthUnits(); + } + + @Override + public Unit getHeightUnits() { + return getRootOrThrow().getHeightUnits(); + } + + @Override + public void setHeight(String height) { + getRootOrThrow().setHeight(height); + } + + @Override + public void setWidth(float width, Unit unit) { + getRootOrThrow().setWidth(width, unit); + } + + @Override + public void setHeight(float height, Unit unit) { + getRootOrThrow().setHeight(height, unit); + } + + @Override + public void setWidth(String width) { + getRootOrThrow().setWidth(width); + } + + @Override + public void setSizeFull() { + getRootOrThrow().setSizeFull(); + } + + @Override + public void setSizeUndefined() { + getRootOrThrow().setSizeUndefined(); + } + + @Override + public void setWidthUndefined() { + getRootOrThrow().setWidthUndefined(); + } + + @Override + public void setHeightUndefined() { + getRootOrThrow().setHeightUndefined(); + } + + @Override + public void setId(String id) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public String getId() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setDebugId(String id) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public String getDebugId() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public String getCaption() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setCaption(String caption) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setCaptionAsHtml(boolean captionAsHtml) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public boolean isCaptionAsHtml() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public Resource getIcon() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setIcon(Resource icon) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public String getDescription() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setDescription(String description) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setDescription(String description, ContentMode mode) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public ErrorMessage getErrorMessage() { + return null; + } + + @Override + public ErrorMessage getComponentError() { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + + @Override + public void setComponentError(ErrorMessage componentError) { + throw new UnsupportedOperationException(COMPOSITE_HAS_NO_DOM_OR_WIDGET); + } + +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/StateGetDoesNotMarkDirtyTest.java b/server/src/test/java/com/vaadin/tests/server/component/StateGetDoesNotMarkDirtyTest.java index 3b4c2413f9..29dd6638fa 100644 --- a/server/src/test/java/com/vaadin/tests/server/component/StateGetDoesNotMarkDirtyTest.java +++ b/server/src/test/java/com/vaadin/tests/server/component/StateGetDoesNotMarkDirtyTest.java @@ -17,6 +17,8 @@ import org.mockito.Mockito; import com.vaadin.server.VaadinSession; import com.vaadin.tests.VaadinClasses; import com.vaadin.ui.Component; +import com.vaadin.ui.ComponentRootSetter; +import com.vaadin.ui.Composite; import com.vaadin.ui.ConnectorTracker; import com.vaadin.ui.Label; import com.vaadin.ui.UI; @@ -75,7 +77,16 @@ public class StateGetDoesNotMarkDirtyTest { } // just to make sure we can invoke it method.setAccessible(true); - method.invoke(newInstance); + try { + method.invoke(newInstance); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof UnsupportedOperationException) { + // Overridden getter which is not supposed to be + // called + } else { + throw e; + } + } } } catch (Exception e) { System.err.println("problem with method " + clazz.getName() @@ -116,6 +127,9 @@ public class StateGetDoesNotMarkDirtyTest { if (component instanceof UI) { return component; } + if (component instanceof Composite) { + ComponentRootSetter.setRoot(component, new Label()); + } emulateAttach(component); return component; } catch (NoSuchMethodException e) { |