package org.gatein.api.composition;
import java.util.ArrayList;
import java.util.List;
import org.gatein.api.security.Permission;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.gatein.pc.portlet.state.InvalidStateIdException;
/**
* Provides the main implementation for the ContainerBuilder. Allows the caller to build a container, which
* might in turn hold other containers. Provides access to other specialized containers, like ColumnContainerBuilderImpl,
* as well as a CustomContainerBuilderImpl.
*
* An instance of this class holds a list of ContainerItem, a reference to the topBuilder (usually a PageBuilder), and
* the parent (another ContainerBuilder).
*
* @author <a href="mailto:jpkroehling+javadoc@redhat.com">Juraci Paixão Kröhling</a>
*/
public class ContainerBuilderImpl<T extends LayoutBuilder<T>> implements ContainerBuilder<T> {
private static final Logger log = LoggerFactory.getLogger(ContainerBuilderImpl.class);
private Permission accessPermission = Container.DEFAULT_ACCESS_PERMISSION;
private Permission moveAppsPermission = Container.DEFAULT_MOVE_APPS_PERMISSION;
private Permission moveContainersPermission = Container.DEFAULT_MOVE_CONTAINERS_PERMISSION;
/**
* A list of ContainerItems added to this container. For instance, this container might hold two columns and a row.
* Or just an application.
*/
private List<ContainerItem> children = new ArrayList<ContainerItem>();
/**
* Holds a reference to the container that created this container. Might be null if the container is at
* the top-level.
*/
private ContainerBuilderImpl<T> parent;
/**
* A reference to the top-level builder, usually a PageBuilder.
*/
private T topBuilder;
/**
* set to {@code true} when either {@link #buildToParentBuilder()} or {@link #buildToTopBuilder()}
* were called and then causes any subsequent call of any of the both to throw an {@link InvalidStateIdException}.
*/
private boolean builtToParentOrTopBuilder = false;
/**
* Basic constructor, receiving a reference to the top-level builder. Serves to return it whenever the caller has
* finished working with the builder, providing a fluent way to return to the main builder.
*
* @param topBuilder a reference to the top level builder, usually "this" at the PageBuilder level.
*/
public ContainerBuilderImpl(T topBuilder) {
this.topBuilder = topBuilder;
}
/**
* Basic constructor, receiving a reference to the top-level builder and a reference to the ContainerBuilder that
* is creating this ContainerBuilder.
*
* @param topBuilder a reference to the top level builder, usually "this" at the PageBuilder level.
* @param parent a reference to the container that is creating this container
*/
public ContainerBuilderImpl(T topBuilder, ContainerBuilderImpl<T> parent) {
this.topBuilder = topBuilder;
this.parent = parent;
}
/**
* Adds a new child to this container.
* @return itself , so that the caller can continue interacting with this builder
*/
@Override
public ContainerBuilder<T> child(ContainerItem containerItem) {
this.children.add(containerItem);
return this;
}
/**
* Adds the provided list of children to the existing list of children for this builder. If a null value is provided,
* the current list of children is cleared.
*
* @param children the list of {@link ContainerItem} to add to this container
* @return this builder
*/
@Override
public ContainerBuilder<T> children(List<ContainerItem> children) {
if (null == children) {
this.children.clear();
return this;
}
this.children.addAll(children);
return this;
}
/**
* Marks the end of the inclusion of children for this container and adds itself as a child of the container
* that has created this builder, returning the parent.
* If there's no parent (ie, is at the top level), then add itself as a child of the root container
* (usually at the Page level) and returns itself.
*
* @return the parent container or itself if this container is placed at the top level
*/
@Override
public ContainerBuilder<T> buildToParentBuilder() {
if (log.isTraceEnabled()) {
log.trace("Building container to parent: " + this);
}
if (builtToParentOrTopBuilder) {
throw new IllegalStateException("The result of this container builder was already submitted either to the parent container builder or to the top level container.");
}
if (null == parent) {
throw new IllegalStateException("Parent container builder cannot be null when calling buildToParentBuilder(). You may want to call either build() or buildToTopBuilder() in this situation.");
}
parent.child(build());
builtToParentOrTopBuilder = true;
// as we are done building, return our parent, so the caller can fluently add more containers to it
return parent;
}
/**
* Marks the end of the work on building containers.
*
* Adds itself to the list of children of the topBuilder and returns the topBuilder (usually a PageBuilder).
* @return the PageBuilder that started this ContainerBuilder.
*/
@Override
public T buildToTopBuilder() {
if (builtToParentOrTopBuilder) {
throw new IllegalStateException("The result of this container builder was already submitted either to the parent container builder or to the top level container.");
}
if (parent != null) {
buildToParentBuilder();
return parent.buildToTopBuilder();
} else {
if (null == topBuilder) {
throw new IllegalStateException("topBuilder cannot be null when calling buildToTopBuilder().");
}
topBuilder.child(build());
builtToParentOrTopBuilder = true;
return topBuilder;
}
}
/**
* @see org.gatein.api.composition.ContainerBuilder#build()
*/
@Override
public Container build() {
ArrayList<ContainerItem> childrenCopy = new ArrayList<ContainerItem>(this.children);
return createContainer(childrenCopy);
}
/**
* Starts a new builder, using the column template. Children added to this new builder will be rendered as
* columns on the screen.
*
* @return a newly created ColumnContainerBuilderImpl
*/
@Override
public ContainerBuilder<T> newColumnsBuilder() {
if (log.isTraceEnabled()) {
log.trace("Creating a new columns container");
}
return new ColumnContainerBuilderImpl<T>(topBuilder, this);
}
/**
* Starts a new builder, using the default template, which renders the children as rows in the screen (ie: block
* elements).
* @return a newly created ContainerBuilderImpl
*/
@Override
public ContainerBuilder<T> newRowsBuilder() {
if (log.isTraceEnabled()) {
log.trace("Creating a new row builder");
}
return new ContainerBuilderImpl<T>(topBuilder, this);
}
/**
* Starts a new builder, that builds on top of the provided Container. Useful when a custom container type is
* required.
*
* @param container the container to serve as base for the builder.
* @return a newly created CustomContainerBuilderImpl
*/
@Override
public ContainerBuilder<T> newCustomContainerBuilder(Container container) {
if (log.isTraceEnabled()) {
log.trace("Creating a new custom container builder");
}
return new CustomContainerBuilderImpl<T>(container, topBuilder, this);
}
/**
* Starts a new builder, that builds on top of the provided Container, using a generic container but with a
* specific template. Useful to prevent creating a specific container type just to change the template.
*
* @param template the template to use when rendering container created via this builder
* @return a newly created CustomContainerBuilderImpl
*/
@Override
public ContainerBuilder<T> newCustomContainerBuilder(String template) {
if (log.isTraceEnabled()) {
log.trace("Creating a new custom container builder");
}
Container container = new ContainerImpl(template, null);
return new CustomContainerBuilderImpl<T>(container, topBuilder, this);
}
@Override
public ContainerBuilder<T> accessPermission(Permission accessPermission) {
this.accessPermission = accessPermission;
return this;
}
@Override
public ContainerBuilder<T> moveAppsPermission(Permission moveAppsPermission) {
this.moveAppsPermission = moveAppsPermission;
return this;
}
@Override
public ContainerBuilder<T> moveContainersPermission(Permission moveContainersPermission) {
this.moveContainersPermission = moveContainersPermission;
return this;
}
/**
* Creates a Container representation based on the information provided to this builder. Specific implementations
* might override this method and might have different requirements and rules.
*
* @param children a list of containers belonging to this container, or null
* @return a representation of this container.
*/
protected Container createContainer(List<ContainerItem> children) {
if (log.isTraceEnabled()) {
log.trace("Creating a new generic container");
}
return completeContainer(new ContainerImpl(children));
}
/**
* Maps common properties to all containers from this builder, like the permissions, into the provided container.
*
* @param container the base container, to add the remaining properties to
* @return the complete container
*/
protected Container completeContainer(Container container) {
container.setAccessPermission(accessPermission);
container.setMoveContainersPermission(moveContainersPermission);
container.setMoveAppsPermission(moveAppsPermission);
return container;
}
}