blob: 53f64d0430436606f35d61794ed0cee8955f10ca [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.cocoon.forms.formmodel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.excalibur.xml.sax.XMLizable;
import org.apache.cocoon.forms.FormsConstants;
import org.apache.cocoon.forms.FormsException;
import org.apache.cocoon.forms.event.CreateEvent;
import org.apache.cocoon.forms.event.CreateListener;
import org.apache.cocoon.forms.event.WidgetEventMulticaster;
import org.apache.cocoon.forms.formmodel.library.Library;
import org.apache.cocoon.forms.validation.WidgetValidator;
import org.apache.cocoon.util.location.Location;
import org.apache.cocoon.xml.XMLUtils;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
/**
* Provides functionality that is common across many WidgetDefinition implementations.
*
* @version $Id$
*/
public abstract class AbstractWidgetDefinition implements WidgetDefinition {
private FormDefinition formDefinition;
protected WidgetDefinition parent;
protected Library enclosingLibrary;
//TODO consider final on these
private Location location = Location.UNKNOWN;
private String id;
/** the definition is mutable when being built */
private boolean mutable = true;
/** The initial map of attributes (can be null) */
private Map attributes;
private Map displayData;
private List validators;
private WidgetState state = WidgetState.ACTIVE;
protected CreateListener createListener;
public FormDefinition getFormDefinition() {
if (this.formDefinition == null) {
if (this instanceof FormDefinition) {
this.formDefinition = (FormDefinition) this;
} else if(this.parent != null) {
this.formDefinition = this.parent.getFormDefinition();
} else {
// no form definition in this widget tree, must be in a library!
return null;
}
}
return this.formDefinition;
}
public Library getEnclosingLibrary() {
if (this.enclosingLibrary == null) {
this.enclosingLibrary = this.parent.getEnclosingLibrary();
}
return this.enclosingLibrary;
}
public void setEnclosingLibrary(Library library) {
enclosingLibrary = library;
}
/**
* initialize this definition with the other, sort of like a copy constructor
*/
public void initializeFrom(WidgetDefinition definition) throws Exception {
if (!(definition instanceof AbstractWidgetDefinition)) {
throw new FormsException("Ancestor definition " + definition.getClass().getName() + " is not an AbstractWidgetDefinition.",
getLocation());
}
AbstractWidgetDefinition other = (AbstractWidgetDefinition) definition;
this.state = other.state;
this.createListener = other.createListener; // this works, we don't really remove listeners, right?
this.validators = new ArrayList();
if (other.validators != null) {
for (int i = 0; i < other.validators.size(); i++) {
this.validators.add(other.validators.get(i));
}
}
if (other.attributes != null) {
if (attributes == null) {
attributes = new HashMap();
}
attributes.putAll(other.attributes);
}
if (other.displayData != null) {
if (displayData == null) {
displayData = new HashMap();
}
displayData.putAll(other.displayData);
}
}
/**
* Checks if this definition is complete or not.
*/
public void checkCompleteness()
throws IncompletenessException {
// FormDefinition is the only one allowed not to have an ID
if (id == null || "".equals(id) && !(this instanceof FormDefinition)) {
throw new IncompletenessException("Widget must have an id attribute.", this);
}
// TODO: don't know what else to check now
}
/**
* Locks this definition so that it becomes immutable.
*/
public void makeImmutable() {
this.mutable = false;
}
/**
* Check that this definition is mutable, i.e. is in setup phase. If not, throw an exception.
*/
protected void checkMutable() {
if (!this.mutable) {
throw new IllegalStateException("Attempt to modify an immutable WidgetDefinition");
}
}
/**
* Sets the parent of this definition
*/
public void setParent(WidgetDefinition definition) {
//FIXME(SW) calling checkMutable() here is not possible as NewDefinition.resolve()
// does some weird reorganization of the definition tree
this.parent = definition;
}
/**
* Gets the parent of this definition.
* This method returns null for the root definition.
*/
public WidgetDefinition getParent() {
return this.parent;
}
public WidgetState getState() {
return this.state;
}
public void setState(WidgetState state) {
checkMutable();
this.state = state;
}
public void setLocation(Location location) {
checkMutable();
this.location = location;
}
public Location getLocation() {
return location;
}
public String getId() {
return id;
}
public void setId(String id) {
checkMutable();
this.id = id;
}
public void setAttributes(Map attributes) {
checkMutable();
if (this.attributes == null) {
this.attributes = attributes;
} else if (attributes != null) {
// merge attribute lists
this.attributes.putAll(attributes);
}
}
public Object getAttribute(String name) {
if (this.attributes != null) {
return this.attributes.get(name);
}
return null;
}
public void addCreateListener(CreateListener listener) {
checkMutable();
// Event listener daisy-chain
this.createListener = WidgetEventMulticaster.add(this.createListener, listener);
}
public void widgetCreated(Widget widget) {
if (this.createListener != null) {
widget.getForm().addWidgetEvent(new CreateEvent(widget));
}
}
public void fireCreateEvent(CreateEvent event) {
// Check that this widget was created by the current definition
if (event.getSourceWidget().getDefinition() != this) {
throw new IllegalArgumentException("Widget was not created by this definition");
}
if (this.createListener != null) {
this.createListener.widgetCreated(event);
}
}
public void generateLabel(ContentHandler contentHandler) throws SAXException {
generateDisplayData("label", contentHandler);
}
/**
* Sets the various display data for this widget. This includes the label, hint and help.
* They must all be objects implementing the XMLizable interface. This approach
* allows to have mixed content in these data.
*
* @param displayData an association of {name, sax fragment}
*/
public void setDisplayData(Map displayData) {
checkMutable();
if (this.displayData == null) {
this.displayData = displayData;
return;
}
if (displayData == null) {
return;
}
// merge displayData lists
Iterator entries = displayData.entrySet().iterator();
while(entries.hasNext()) {
Map.Entry entry = (Map.Entry)entries.next();
Object key = entry.getKey();
Object value = entry.getValue();
if (value != null || !this.displayData.containsKey(key)) {
this.displayData.put(key, value);
}
}
}
public void addValidator(WidgetValidator validator) {
checkMutable();
if (this.validators == null) {
this.validators = new ArrayList();
}
this.validators.add(validator);
}
public void generateDisplayData(String name, ContentHandler contentHandler) throws SAXException {
Object data = this.displayData.get(name);
if (data != null) {
((XMLizable) data).toSAX(contentHandler);
} else if (!this.displayData.containsKey(name)) {
throw new IllegalArgumentException("Unknown display data name '" + name + "'");
}
}
public void generateDisplayData(ContentHandler contentHandler) throws SAXException {
// Output all non-null display data
Iterator iter = this.displayData.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
if (entry.getValue() != null) {
String name = (String) entry.getKey();
// Enclose the data into a "wi:{name}" element
contentHandler.startElement(FormsConstants.INSTANCE_NS, name, FormsConstants.INSTANCE_PREFIX_COLON + name, XMLUtils.EMPTY_ATTRIBUTES);
((XMLizable) entry.getValue()).toSAX(contentHandler);
contentHandler.endElement(FormsConstants.INSTANCE_NS, name, FormsConstants.INSTANCE_PREFIX_COLON + name);
}
}
}
public boolean validate(Widget widget) {
if (this.validators == null) {
// No validators
return true;
}
Iterator iter = this.validators.iterator();
while (iter.hasNext()) {
WidgetValidator validator = (WidgetValidator)iter.next();
if (!validator.validate(widget)) {
// Stop at the first validator that fails
return false;
}
}
// All validators were sucessful
return true;
}
}