/* 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 org.riotfamily.forms;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import org.riotfamily.forms.request.FormRequest;
import org.riotfamily.forms.ui.Dimension;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.util.Assert;
/**
* Abstract superclass for elements that consist of several child elements.
* Calls to <code>processRequest()</code> and <code>render()</code> are
* automatically delegated to the components. Additionally the components are
* initialized, i.e. <code>setParent()</code>, <code>setForm()</code> and
* <code>Form.registerElement()</code> are called.
*/
public abstract class CompositeElement extends AbstractEditorBase
implements BeanFactoryAware {
private List<Element> components;
private AutowireCapableBeanFactory beanFactory;
/**
* Empty default constructor.
*/
public CompositeElement() {
components = new ArrayList<Element>();
}
/**
* Empty default constructor.
*/
@SuppressWarnings("unchecked")
public CompositeElement(List<? extends Element> components) {
this.components = (List<Element>) components;
}
public void setBeanFactory(BeanFactory beanFactory) {
if (beanFactory instanceof AutowireCapableBeanFactory) {
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
}
}
protected List<Element> getComponents() {
return components;
}
/**
* Adds the given element to the list of components. If a form as already
* been set {@link #initComponent(Element)} is
* invoked, otherwise initialization is deferred until
* {@link #setForm(Form)} is called.
*
* @param element the element to add
*/
protected void addComponent(Element element) {
components.add(element);
element.setParent(this);
if (getForm() != null) {
initComponent(element);
}
}
/**
* Removes the given component. Note that the element is
* not unregistered from the form.
*/
protected void removeComponent(Element element) {
components.remove(element);
}
public boolean isEmpty() {
return components.isEmpty();
}
/**
* Invokes <code>initComponent(Element)</code> on all components and
* finally calls <code>initCompositeElement()</code>.
*/
@Override
protected final void afterFormSet() {
for (Element element : components) {
initComponent(element);
}
initCompositeElement();
}
/**
* Subclasses may override this method to perform initialization tasks.
* A reference to the form will be set at this point and all components
* will be initialized.
*
*The default implementation does nothing.
*/
protected void initCompositeElement() {
}
/**
* Calls <code>processRequestInternal()</code> and afterwards
* <code>processRequestComponents()</code> to process the components.
*/
@Override
public void processRequest(FormRequest request) {
processRequestInternal(request);
processRequestCompontents(request);
}
/**
* Processes the request for all the components
*/
protected void processRequestCompontents(FormRequest request) {
// Temporary list to allow concurrent modification
List<Element> tempList = new ArrayList<Element>(components);
for (Element component : tempList) {
if (component.isEnabled()) {
component.processRequest(request);
}
}
}
/**
* Sets a reference to the form and registers the element by calling
* {@link Form#registerElement(Element)}.
*
* @throws IllegalStateException if form is null
*/
protected void initComponent(Element element) {
Assert.state(getForm() != null, "The form must be set");
if (beanFactory != null) {
beanFactory.initializeBean(element, null);
}
getForm().registerElement(element);
}
/**
* Called before processRequest() is invoked on the contained elements.
* Subclasses can override this method to perform custom processing. The
* default implementation does nothing.
*/
protected void processRequestInternal(FormRequest request) {
}
@Override
protected void renderInternal(PrintWriter writer) {
for (Element component : components) {
component.render(writer);
}
}
public Dimension getDimension() {
Dimension d = getPadding();
for (Element component : components) {
d = d.addHeight(getComponentPadding(component).add(component.getDimension()));
}
return d;
}
protected Dimension getPadding() {
return new Dimension();
}
protected Dimension getComponentPadding(Element component) {
return new Dimension();
}
/**
* Delegates the call to the first component.
*/
@Override
public void focus() {
if (!components.isEmpty()) {
components.get(0).focus();
}
}
/**
* Helper method to check for composite elements in templates.
* Always returns <code>true</code>
*/
@Override
public boolean isCompositeElement() {
return true;
}
}