| /* |
| * 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; |
| } |
| } |