aboutsummaryrefslogtreecommitdiffstats
path: root/server/src/main/java/com/vaadin/ui/declarative/DesignContext.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/main/java/com/vaadin/ui/declarative/DesignContext.java')
-rw-r--r--server/src/main/java/com/vaadin/ui/declarative/DesignContext.java795
1 files changed, 795 insertions, 0 deletions
diff --git a/server/src/main/java/com/vaadin/ui/declarative/DesignContext.java b/server/src/main/java/com/vaadin/ui/declarative/DesignContext.java
new file mode 100644
index 0000000000..d72ec77d4a
--- /dev/null
+++ b/server/src/main/java/com/vaadin/ui/declarative/DesignContext.java
@@ -0,0 +1,795 @@
+/*
+ * 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.declarative;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jsoup.nodes.Attributes;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.nodes.Node;
+
+import com.vaadin.annotations.DesignRoot;
+import com.vaadin.server.Constants;
+import com.vaadin.server.DeploymentConfiguration;
+import com.vaadin.server.VaadinService;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.HasComponents;
+import com.vaadin.ui.declarative.Design.ComponentFactory;
+import com.vaadin.ui.declarative.Design.ComponentMapper;
+
+/**
+ * This class contains contextual information that is collected when a component
+ * tree is constructed based on HTML design template. This information includes
+ * mappings from local ids, global ids and captions to components , as well as a
+ * mapping between prefixes and package names (such as "vaadin" ->
+ * "com.vaadin.ui").
+ *
+ * Versions prior to 7.6 use "v" as the default prefix. Versions starting with
+ * 7.6 support reading designs with either "v" or "vaadin" as the prefix, but
+ * only write "vaadin" by default. Writing with the legacy prefix can be
+ * activated with the property or context parameter
+ * {@link Constants#SERVLET_PARAMETER_LEGACY_DESIGN_PREFIX}.
+ *
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public class DesignContext implements Serializable {
+
+ private static final String LEGACY_PREFIX = "v";
+ private static final String VAADIN_PREFIX = "vaadin";
+
+ private static final String VAADIN_UI_PACKAGE = "com.vaadin.ui";
+
+ // cache for object instances
+ private static Map<Class<?>, Component> instanceCache = new ConcurrentHashMap<Class<?>, Component>();
+
+ // The root component of the component hierarchy
+ private Component rootComponent = null;
+ // Attribute names for global id and caption and the prefix name for a local
+ // id
+ public static final String ID_ATTRIBUTE = "id";
+ public static final String CAPTION_ATTRIBUTE = "caption";
+ public static final String LOCAL_ID_ATTRIBUTE = "_id";
+ // Mappings from ids to components. Modified when reading from design.
+ private Map<String, Component> idToComponent = new HashMap<String, Component>();
+ private Map<String, Component> localIdToComponent = new HashMap<String, Component>();
+ private Map<String, Component> captionToComponent = new HashMap<String, Component>();
+ // Mapping from components to local ids. Accessed when writing to
+ // design. Modified when reading from design.
+ private Map<Component, String> componentToLocalId = new HashMap<Component, String>();
+ private Document doc; // required for calling createElement(String)
+ // namespace mappings
+ private Map<String, String> packageToPrefix = new HashMap<String, String>();
+ private Map<String, String> prefixToPackage = new HashMap<String, String>();
+
+ // component creation listeners
+ private List<ComponentCreationListener> listeners = new ArrayList<ComponentCreationListener>();
+
+ private ShouldWriteDataDelegate shouldWriteDataDelegate = ShouldWriteDataDelegate.DEFAULT;
+
+ // this cannot be static because of testability issues
+ private Boolean legacyDesignPrefix = null;
+
+ public DesignContext(Document doc) {
+ this.doc = doc;
+ // Initialize the mapping between prefixes and package names.
+ if (isLegacyPrefixEnabled()) {
+ addPackagePrefix(LEGACY_PREFIX, VAADIN_UI_PACKAGE);
+ prefixToPackage.put(VAADIN_PREFIX, VAADIN_UI_PACKAGE);
+ } else {
+ addPackagePrefix(VAADIN_PREFIX, VAADIN_UI_PACKAGE);
+ prefixToPackage.put(LEGACY_PREFIX, VAADIN_UI_PACKAGE);
+ }
+ }
+
+ public DesignContext() {
+ this(new Document(""));
+ }
+
+ /**
+ * Returns a component having the specified local id. If no component is
+ * found, returns null.
+ *
+ * @param localId
+ * The local id of the component
+ * @return a component whose local id equals localId
+ */
+ public Component getComponentByLocalId(String localId) {
+ return localIdToComponent.get(localId);
+ }
+
+ /**
+ * Returns a component having the specified global id. If no component is
+ * found, returns null.
+ *
+ * @param globalId
+ * The global id of the component
+ * @return a component whose global id equals globalId
+ */
+ public Component getComponentById(String globalId) {
+ return idToComponent.get(globalId);
+ }
+
+ /**
+ * Returns a component having the specified caption. If no component is
+ * found, returns null.
+ *
+ * @param caption
+ * The caption of the component
+ * @return a component whose caption equals the caption given as a parameter
+ */
+ public Component getComponentByCaption(String caption) {
+ return captionToComponent.get(caption);
+ }
+
+ /**
+ * Creates a mapping between the given global id and the component. Returns
+ * true if globalId was already mapped to some component. Otherwise returns
+ * false. Also sets the id of the component to globalId.
+ *
+ * If there is a mapping from the component to a global id (gid) different
+ * from globalId, the mapping from gid to component is removed.
+ *
+ * If the string was mapped to a component c different from the given
+ * component, the mapping from c to the string is removed. Similarly, if
+ * component was mapped to some string s different from globalId, the
+ * mapping from s to component is removed.
+ *
+ * @param globalId
+ * The new global id of the component.
+ * @param component
+ * The component whose global id is to be set.
+ * @return true, if there already was a global id mapping from the string to
+ * some component.
+ */
+ private boolean mapId(String globalId, Component component) {
+ Component oldComponent = idToComponent.get(globalId);
+ if (oldComponent != null && !oldComponent.equals(component)) {
+ oldComponent.setId(null);
+ }
+ String oldGID = component.getId();
+ if (oldGID != null && !oldGID.equals(globalId)) {
+ idToComponent.remove(oldGID);
+ }
+ component.setId(globalId);
+ idToComponent.put(globalId, component);
+ return oldComponent != null && !oldComponent.equals(component);
+ }
+
+ /**
+ * Creates a mapping between the given local id and the component. Returns
+ * true if localId was already mapped to some component or if component was
+ * mapped to some string. Otherwise returns false.
+ *
+ * If the string was mapped to a component c different from the given
+ * component, the mapping from c to the string is removed. Similarly, if
+ * component was mapped to some string s different from localId, the mapping
+ * from s to component is removed.
+ *
+ * @since 7.5.0
+ *
+ * @param component
+ * The component whose local id is to be set.
+ * @param localId
+ * The new local id of the component.
+ *
+ * @return true, if there already was a local id mapping from the string to
+ * some component or from the component to some string. Otherwise
+ * returns false.
+ */
+ public boolean setComponentLocalId(Component component, String localId) {
+ return twoWayMap(localId, component, localIdToComponent,
+ componentToLocalId);
+ }
+
+ /**
+ * Returns the local id for a component.
+ *
+ * @since 7.5.0
+ *
+ * @param component
+ * The component whose local id to get.
+ * @return the local id of the component, or null if the component has no
+ * local id assigned
+ */
+ public String getComponentLocalId(Component component) {
+ return componentToLocalId.get(component);
+ }
+
+ /**
+ * Creates a mapping between the given caption and the component. Returns
+ * true if caption was already mapped to some component.
+ *
+ * Note that unlike mapGlobalId, if some component already has the given
+ * caption, the caption is not cleared from the component. This allows
+ * non-unique captions. However, only one of the components corresponding to
+ * a given caption can be found using the map captionToComponent. Hence, any
+ * captions that are used to identify an object should be unique.
+ *
+ * @param caption
+ * The new caption of the component.
+ * @param component
+ * The component whose caption is to be set.
+ * @return true, if there already was a caption mapping from the string to
+ * some component.
+ */
+ private boolean mapCaption(String caption, Component component) {
+ return captionToComponent.put(caption, component) != null;
+ }
+
+ /**
+ * Creates a two-way mapping between key and value, i.e. adds key -> value
+ * to keyToValue and value -> key to valueToKey. If key was mapped to a
+ * value v different from the given value, the mapping from v to key is
+ * removed. Similarly, if value was mapped to some key k different from key,
+ * the mapping from k to value is removed.
+ *
+ * Returns true if there already was a mapping from key to some value v or
+ * if there was a mapping from value to some key k. Otherwise returns false.
+ *
+ * @param key
+ * The new key in keyToValue.
+ * @param value
+ * The new value in keyToValue.
+ * @param keyToValue
+ * A map from keys to values.
+ * @param valueToKey
+ * A map from values to keys.
+ * @return whether there already was some mapping from key to a value or
+ * from value to a key.
+ */
+ private <S, T> boolean twoWayMap(S key, T value, Map<S, T> keyToValue,
+ Map<T, S> valueToKey) {
+ T oldValue = keyToValue.put(key, value);
+ if (oldValue != null && !oldValue.equals(value)) {
+ valueToKey.remove(oldValue);
+ }
+ S oldKey = valueToKey.put(value, key);
+ if (oldKey != null && !oldKey.equals(key)) {
+ keyToValue.remove(oldKey);
+ }
+ return oldValue != null || oldKey != null;
+ }
+
+ /**
+ * Creates a two-way mapping between a prefix and a package name.
+ *
+ * Note that modifying the mapping for {@value #VAADIN_UI_PACKAGE} may
+ * invalidate the backwards compatibility mechanism supporting reading such
+ * components with either {@value #LEGACY_PREFIX} or {@value #VAADIN_PREFIX}
+ * as prefix.
+ *
+ * @param prefix
+ * the prefix name without an ending dash (for instance, "vaadin"
+ * is by default used for "com.vaadin.ui")
+ * @param packageName
+ * the name of the package corresponding to prefix
+ *
+ * @see #getPackagePrefixes()
+ * @see #getPackagePrefix(String)
+ * @see #getPackage(String)
+ * @since 7.5.0
+ */
+ public void addPackagePrefix(String prefix, String packageName) {
+ twoWayMap(prefix, packageName, prefixToPackage, packageToPrefix);
+ }
+
+ /**
+ * Gets the prefix mapping for a given package, or <code>null</code> if
+ * there is no mapping for the package.
+ *
+ * @see #addPackagePrefix(String, String)
+ * @see #getPackagePrefixes()
+ *
+ * @since 7.5.0
+ * @param packageName
+ * the package name to get a prefix for
+ * @return the prefix for the package, or <code>null</code> if no prefix is
+ * registered
+ */
+ public String getPackagePrefix(String packageName) {
+ if (VAADIN_UI_PACKAGE.equals(packageName)) {
+ return isLegacyPrefixEnabled() ? LEGACY_PREFIX : VAADIN_PREFIX;
+ } else {
+ return packageToPrefix.get(packageName);
+ }
+ }
+
+ /**
+ * Gets all registered package prefixes.
+ *
+ *
+ * @since 7.5.0
+ * @see #getPackage(String)
+ * @return a collection of package prefixes
+ */
+ public Collection<String> getPackagePrefixes() {
+ return Collections.unmodifiableCollection(prefixToPackage.keySet());
+ }
+
+ /**
+ * Gets the package corresponding to the give prefix, or <code>null</code>
+ * no package has been registered for the prefix
+ *
+ * @since 7.5.0
+ * @see #addPackagePrefix(String, String)
+ * @param prefix
+ * the prefix to find a package for
+ * @return the package prefix, or <code>null</code> if no package is
+ * registered for the provided prefix
+ */
+ public String getPackage(String prefix) {
+ return prefixToPackage.get(prefix);
+ }
+
+ /**
+ * Returns the default instance for the given class. The instance must not
+ * be modified by the caller.
+ *
+ * @param abstractComponent
+ * @return the default instance for the given class. The return value must
+ * not be modified by the caller
+ */
+ public <T> T getDefaultInstance(Component component) {
+ // If the root is a @DesignRoot component, it can't use itself as a
+ // reference or the written design will be empty
+
+ // If the root component in some other way initializes itself in the
+ // constructor
+ if (getRootComponent() == component
+ && component.getClass().isAnnotationPresent(DesignRoot.class)) {
+ return (T) getDefaultInstance((Class<? extends Component>) component
+ .getClass().getSuperclass());
+ }
+ return (T) getDefaultInstance(component.getClass());
+ }
+
+ private Component getDefaultInstance(
+ Class<? extends Component> componentClass) {
+ Component instance = instanceCache.get(componentClass);
+ if (instance == null) {
+ instance = instantiateClass(componentClass.getName());
+ instanceCache.put(componentClass, instance);
+ }
+ return instance;
+ }
+
+ /**
+ * Reads and stores the mappings from prefixes to package names from meta
+ * tags located under <head> in the html document.
+ */
+ protected void readPackageMappings(Document doc) {
+ Element head = doc.head();
+ if (head == null) {
+ return;
+ }
+ for (Node child : head.childNodes()) {
+ if (child instanceof Element) {
+ Element childElement = (Element) child;
+ if ("meta".equals(childElement.tagName())) {
+ Attributes attributes = childElement.attributes();
+ if (attributes.hasKey("name")
+ && attributes.hasKey("content")
+ && "package-mapping".equals(attributes.get("name"))) {
+ String contentString = attributes.get("content");
+ String[] parts = contentString.split(":");
+ if (parts.length != 2) {
+ throw new DesignException("The meta tag '"
+ + child.toString() + "' cannot be parsed.");
+ }
+ String prefixName = parts[0];
+ String packageName = parts[1];
+ addPackagePrefix(prefixName, packageName);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Writes the package mappings (prefix -> package name) of this object to
+ * the specified document.
+ * <p>
+ * The prefixes are stored as <meta> tags under <head> in the document.
+ *
+ * @param doc
+ * the Jsoup document tree where the package mappings are written
+ */
+ public void writePackageMappings(Document doc) {
+ Element head = doc.head();
+ for (String prefix : getPackagePrefixes()) {
+ // Only store the prefix-name mapping if it is not a default mapping
+ // (such as "vaadin" -> "com.vaadin.ui")
+ if (!VAADIN_PREFIX.equals(prefix) && !LEGACY_PREFIX.equals(prefix)) {
+ Node newNode = doc.createElement("meta");
+ newNode.attr("name", "package-mapping");
+ String prefixToPackageName = prefix + ":" + getPackage(prefix);
+ newNode.attr("content", prefixToPackageName);
+ head.appendChild(newNode);
+ }
+ }
+ }
+
+ /**
+ * Check whether the legacy prefix "v" or the default prefix "vaadin" should
+ * be used when writing designs. The property or context parameter
+ * {@link Constants#SERVLET_PARAMETER_LEGACY_DESIGN_PREFIX} can be used to
+ * switch to the legacy prefix.
+ *
+ * @since 7.5.7
+ * @return true to use the legacy prefix, false by default
+ */
+ protected boolean isLegacyPrefixEnabled() {
+ if (legacyDesignPrefix != null) {
+ return legacyDesignPrefix.booleanValue();
+ }
+ if (VaadinService.getCurrent() == null) {
+ // This will happen at least in JUnit tests.
+ return false;
+ }
+ DeploymentConfiguration configuration = VaadinService.getCurrent()
+ .getDeploymentConfiguration();
+ legacyDesignPrefix = configuration.getApplicationOrSystemProperty(
+ Constants.SERVLET_PARAMETER_LEGACY_DESIGN_PREFIX, "false")
+ .equals("true");
+ return legacyDesignPrefix.booleanValue();
+ }
+
+ /**
+ * Creates an html tree node corresponding to the given element. Also
+ * initializes its attributes by calling writeDesign. As a result of the
+ * writeDesign() call, this method creates the entire subtree rooted at the
+ * returned Node.
+ *
+ * @param childComponent
+ * The component with state that is written in to the node
+ * @return An html tree node corresponding to the given component. The tag
+ * name of the created node is derived from the class name of
+ * childComponent.
+ */
+ public Element createElement(Component childComponent) {
+ ComponentMapper componentMapper = Design.getComponentMapper();
+
+ String tagName = componentMapper.componentToTag(childComponent, this);
+
+ Element newElement = doc.createElement(tagName);
+ childComponent.writeDesign(newElement, this);
+ // Handle the local id. Global id and caption should have been taken
+ // care of by writeDesign.
+ String localId = componentToLocalId.get(childComponent);
+ if (localId != null) {
+ newElement.attr(LOCAL_ID_ATTRIBUTE, localId);
+ }
+ return newElement;
+ }
+
+ /**
+ * Reads the given design node and creates the corresponding component tree
+ *
+ * @param componentDesign
+ * The design element containing the description of the component
+ * to be created.
+ * @return the root component of component tree
+ */
+ public Component readDesign(Element componentDesign) {
+ // Create the component.
+ Component component = instantiateComponent(componentDesign);
+ readDesign(componentDesign, component);
+ fireComponentCreatedEvent(componentToLocalId.get(component), component);
+ return component;
+ }
+
+ /**
+ *
+ * Reads the given design node and populates the given component with the
+ * corresponding component tree
+ * <p>
+ * Additionally registers the component id, local id and caption of the
+ * given component and all its children in the context
+ *
+ * @param componentDesign
+ * The design element containing the description of the component
+ * to be created
+ * @param component
+ * The component which corresponds to the design element
+ */
+ public void readDesign(Element componentDesign, Component component) {
+ component.readDesign(componentDesign, this);
+ // Get the ids and the caption of the component and store them in the
+ // maps of this design context.
+ org.jsoup.nodes.Attributes attributes = componentDesign.attributes();
+ // global id: only update the mapping, the id has already been set for
+ // the component
+ String id = component.getId();
+ if (id != null && id.length() > 0) {
+ boolean mappingExists = mapId(id, component);
+ if (mappingExists) {
+ throw new DesignException(
+ "The following global id is not unique: " + id);
+ }
+ }
+ // local id: this is not a property of a component, so need to fetch it
+ // from the attributes of componentDesign
+ if (attributes.hasKey(LOCAL_ID_ATTRIBUTE)) {
+ String localId = attributes.get(LOCAL_ID_ATTRIBUTE);
+ boolean mappingExists = setComponentLocalId(component, localId);
+ if (mappingExists) {
+ throw new DesignException(
+ "the following local id is not unique: " + localId);
+ }
+ }
+ // caption: a property of a component, possibly not unique
+ String caption = component.getCaption();
+ if (caption != null) {
+ mapCaption(caption, component);
+ }
+ }
+
+ /**
+ * Creates a Component corresponding to the given node. Does not set the
+ * attributes for the created object.
+ *
+ * @param node
+ * a node of an html tree
+ * @return a Component corresponding to node, with no attributes set.
+ */
+ private Component instantiateComponent(Node node) {
+ String tag = node.nodeName();
+
+ ComponentMapper componentMapper = Design.getComponentMapper();
+ Component component = componentMapper.tagToComponent(tag,
+ Design.getComponentFactory(), this);
+
+ assert tagEquals(tag, componentMapper.componentToTag(component, this));
+
+ return component;
+ }
+
+ private boolean tagEquals(String tag1, String tag2) {
+ return tag1.equals(tag2)
+ || (hasVaadinPrefix(tag1) && hasVaadinPrefix(tag2));
+ }
+
+ private boolean hasVaadinPrefix(String tag) {
+ return tag.startsWith(LEGACY_PREFIX + "-")
+ || tag.startsWith(VAADIN_PREFIX + "-");
+ }
+
+ /**
+ * Instantiates given class via ComponentFactory.
+ *
+ * @param qualifiedClassName
+ * class name to instantiate
+ * @return instance of a given class
+ */
+ private Component instantiateClass(String qualifiedClassName) {
+ ComponentFactory factory = Design.getComponentFactory();
+ Component component = factory.createComponent(qualifiedClassName, this);
+
+ if (component == null) {
+ throw new DesignException("Got unexpected null component from "
+ + factory.getClass().getName() + " for class "
+ + qualifiedClassName);
+ }
+
+ return component;
+ }
+
+ /**
+ * Returns the root component of a created component hierarchy.
+ *
+ * @return the root component of the hierarchy
+ */
+ public Component getRootComponent() {
+ return rootComponent;
+ }
+
+ /**
+ * Sets the root component of a created component hierarchy.
+ *
+ * @param rootComponent
+ * the root component of the hierarchy
+ */
+ public void setRootComponent(Component rootComponent) {
+ this.rootComponent = rootComponent;
+ }
+
+ /**
+ * Adds a component creation listener. The listener will be notified when
+ * components are created while parsing a design template
+ *
+ * @param listener
+ * the component creation listener to be added
+ */
+ public void addComponentCreationListener(ComponentCreationListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes a component creation listener.
+ *
+ * @param listener
+ * the component creation listener to be removed
+ */
+ public void removeComponentCreationListener(
+ ComponentCreationListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Fires component creation event
+ *
+ * @param localId
+ * localId of the component
+ * @param component
+ * the component that was created
+ */
+ private void fireComponentCreatedEvent(String localId, Component component) {
+ ComponentCreatedEvent event = new ComponentCreatedEvent(localId,
+ component);
+ for (ComponentCreationListener listener : listeners) {
+ listener.componentCreated(event);
+ }
+ }
+
+ /**
+ * Interface to be implemented by component creation listeners
+ *
+ * @author Vaadin Ltd
+ */
+ public interface ComponentCreationListener extends Serializable {
+
+ /**
+ * Called when component has been created in the design context
+ *
+ * @param event
+ * the component creation event containing information on the
+ * created component
+ */
+ public void componentCreated(ComponentCreatedEvent event);
+ }
+
+ /**
+ * Component creation event that is fired when a component is created in the
+ * context
+ *
+ * @author Vaadin Ltd
+ */
+ public class ComponentCreatedEvent implements Serializable {
+ private String localId;
+ private Component component;
+ private DesignContext context;
+
+ /**
+ * Creates a new instance of ComponentCreatedEvent
+ *
+ * @param localId
+ * the local id of the created component
+ * @param component
+ * the created component
+ */
+ private ComponentCreatedEvent(String localId, Component component) {
+ this.localId = localId;
+ this.component = component;
+ context = DesignContext.this;
+ }
+
+ /**
+ * Returns the local id of the created component or null if not exist
+ *
+ * @return the localId
+ */
+ public String getLocalId() {
+ return localId;
+ }
+
+ /**
+ * Returns the created component
+ *
+ * @return the component
+ */
+ public Component getComponent() {
+ return component;
+ }
+ }
+
+ /**
+ * Helper method for component write implementors to determine whether their
+ * children should be written out or not
+ *
+ * @param c
+ * The component being written
+ * @param defaultC
+ * The default instance for the component
+ * @return whether the children of c should be written
+ */
+ public boolean shouldWriteChildren(Component c, Component defaultC) {
+ if (c == getRootComponent()) {
+ // The root component should always write its children - otherwise
+ // the result is empty
+ return true;
+ }
+
+ if (defaultC instanceof HasComponents
+ && ((HasComponents) defaultC).iterator().hasNext()) {
+ // Easy version which assumes that this is a custom component if the
+ // constructor adds children
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Determines whether the container data of a component should be written
+ * out by delegating to a {@link ShouldWriteDataDelegate}. The default
+ * delegate assumes that all component data is provided by a data source
+ * connected to a back end system and that the data should thus not be
+ * written.
+ *
+ * @since 7.5.0
+ * @see #setShouldWriteDataDelegate(ShouldWriteDataDelegate)
+ * @param component
+ * the component to check
+ * @return <code>true</code> if container data should be written out for the
+ * provided component; otherwise <code>false</code>.
+ */
+ public boolean shouldWriteData(Component component) {
+ return getShouldWriteDataDelegate().shouldWriteData(component);
+ }
+
+ /**
+ * Sets the delegate that determines whether the container data of a
+ * component should be written out.
+ *
+ * @since 7.5.0
+ * @see #shouldWriteChildren(Component, Component)
+ * @see #getShouldWriteDataDelegate()
+ * @param shouldWriteDataDelegate
+ * the delegate to set, not <code>null</code>
+ * @throws IllegalArgumentException
+ * if the provided delegate is <code>null</code>
+ */
+ public void setShouldWriteDataDelegate(
+ ShouldWriteDataDelegate shouldWriteDataDelegate) {
+ if (shouldWriteDataDelegate == null) {
+ throw new IllegalArgumentException("Delegate cannot be null");
+ }
+ this.shouldWriteDataDelegate = shouldWriteDataDelegate;
+ }
+
+ /**
+ * Gets the delegate that determines whether the container data of a
+ * component should be written out.
+ *
+ * @since 7.5.0
+ * @see #setShouldWriteDataDelegate(ShouldWriteDataDelegate)
+ * @see #shouldWriteChildren(Component, Component)
+ * @return the shouldWriteDataDelegate the currently use delegate
+ */
+ public ShouldWriteDataDelegate getShouldWriteDataDelegate() {
+ return shouldWriteDataDelegate;
+ }
+
+}