net.contextfw.web.application.component.Component Maven / Gradle / Ivy
Show all versions of web-application Show documentation
/**
* Copyright 2010 Marko Lavikainen
*
* 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 net.contextfw.web.application.component;
import java.util.HashSet;
import java.util.Set;
import net.contextfw.web.application.WebApplicationException;
import net.contextfw.web.application.internal.component.ComponentBuilder;
/**
* The base class of a component
*
*/
@Buildable
public abstract class Component {
private final Set partialUpdates = new HashSet();
private String partialUpdateName;
private RefreshMode refreshMode = RefreshMode.NONE;
private Component parent = null;
private Set children = null;
private Set waitingToRegister = null;
private boolean enabled = true;
private enum RefreshMode {
NONE, PASS, UPDATE, PARTIAL_UPDATE
};
@Attribute
private String id;
/**
* Assigns an id for the component
*
*
* Id is generated automatically by the web framework so setting an id is
* not necesssary. the id is of a form el[n] where
* [n] is an incrementing value.
*
*
* It is also possible to set a custom id, but to be sure that setting
* succceeds it must be don during initialization.
*
*/
public void setId(String id) {
if (this.id != null) {
throw new WebApplicationException("Component id can be set only once");
}
this.id = id;
}
/**
* Get's component id
*
*
* The id is null initially, but when component is
* registered to thes system id is generated automatically. This means that
* id may not be in use at initialization phase and developer
* should not rely on using id in class properties.
*
*/
public String getId() {
return id;
}
/**
* Adds a component to be a child of this component.
*
*
* Registering child components is a mandatory task so that the framework is
* able to register all components in the page and assign proper ids for
* them.
*
*
*
* Component registering is lazy. If parent component has not yet been
* registered then registering for child components is delayd untit parent
* is also registered.
*
*
* @param
* Component type
* @param el
* The Component
* @return The added component
*/
public T registerChild(T el) {
if (children == null) {
children = new HashSet();
waitingToRegister = new HashSet();
}
children.add(el);
el.parent = this;
if (bubbleRegisterUp(el)) {
el.registerChildren();
} else {
waitingToRegister.add(el);
}
return el;
}
private void registerChildren() {
if (waitingToRegister != null) {
for (Component comp : waitingToRegister) {
registerChild(comp);
}
waitingToRegister.clear();
}
}
/**
* For internal use only
*/
protected boolean bubbleRegisterUp(Component el) {
if (parent != null) {
return parent.bubbleRegisterUp(el);
} else {
return false;
}
}
/**
* For internal use only
*/
protected void bubbleUnregisterUp(Component el) {
if (parent != null) {
parent.bubbleUnregisterUp(el);
}
}
/**
* Removes component (this) from its parent, if parent exists.
*/
public void detach() {
if (parent != null) {
parent.unregisterChild(this);
}
}
/**
* Removes child component from the framework
*/
public void unregisterChild(Component el) {
if (children != null) {
children.remove(el);
bubbleUnregisterUp(el);
el.parent = null;
}
}
/**
* Refreshes component state to web client
*
*
* When component needs to update its state on web client, this method must
* be called. Framework recognizes the request and creates property update
* during rendering phase.
*
*
*
* Note! If paren component also requests update, then the update of
* this component is canceled, because in normal circumstances this
* component if fully redrawn by the parent component.
*
*/
public void refresh() {
refresh(RefreshMode.UPDATE);
}
private void refresh(RefreshMode mode) {
if (id != null) {
refreshMode = mode;
Component p = parent;
while (p != null) {
if (p.refreshMode == RefreshMode.NONE) {
p.refreshMode = RefreshMode.PASS;
p = p.parent;
} else {
p = null;
}
}
}
}
/**
* For internal use only
*/
public final void buildComponentUpdate(DOMBuilder domBuilder, ComponentBuilder builder) {
if (refreshMode == RefreshMode.UPDATE) {
builder.buildUpdate(domBuilder, this, "update");
} else if (refreshMode == RefreshMode.PARTIAL_UPDATE) {
builder.buildPartialUpdate(domBuilder, this, partialUpdateName, partialUpdates);
}
if ((refreshMode == RefreshMode.PASS || refreshMode == RefreshMode.PARTIAL_UPDATE) && children != null) {
for (Component child : children) {
child.buildComponentUpdate(domBuilder, builder);
}
}
clearCascadedUpdate();
}
/**
* Requests a partial update for component
*
*
* When there is a need to only partially update component, then this method
* is used
*
*
*
* @param buildName
* @param updates
*/
public void partialRefresh(String buildName, String... updates) {
if (partialUpdates != null && refreshMode != RefreshMode.UPDATE) {
this.partialUpdateName = buildName;
for (String partialUpdate : updates) {
partialUpdates.add(partialUpdate);
}
partialUpdates.add("id");
refresh(RefreshMode.PARTIAL_UPDATE);
}
}
/**
* For internal use only
*/
public void clearCascadedUpdate() {
if (refreshMode == RefreshMode.NONE) {
return;
}
refreshMode = RefreshMode.NONE;
if (children != null) {
for (Component child : children) {
child.clearCascadedUpdate();
}
}
partialUpdates.clear();
partialUpdateName = null;
}
/**
* Defines if this component is enabled or disabled.
*
*
* If component is disabled then it is not added to DOM-tree during
* rendering phase. Also disabled component does not listen remote calls.
*
*
* @param enabled
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
}