001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.forms.formmodel;
018:
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: import org.apache.excalibur.xml.sax.XMLizable;
026:
027: import org.apache.cocoon.forms.FormsConstants;
028: import org.apache.cocoon.forms.FormsException;
029: import org.apache.cocoon.forms.event.CreateEvent;
030: import org.apache.cocoon.forms.event.CreateListener;
031: import org.apache.cocoon.forms.event.WidgetEventMulticaster;
032: import org.apache.cocoon.forms.formmodel.library.Library;
033: import org.apache.cocoon.forms.validation.WidgetValidator;
034: import org.apache.cocoon.util.location.Location;
035: import org.apache.cocoon.xml.XMLUtils;
036:
037: import org.xml.sax.ContentHandler;
038: import org.xml.sax.SAXException;
039:
040: /**
041: * Provides functionality that is common across many WidgetDefinition implementations.
042: *
043: * @version $Id: AbstractWidgetDefinition.java 449149 2006-09-23 03:58:05Z crossley $
044: */
045: public abstract class AbstractWidgetDefinition implements
046: WidgetDefinition {
047:
048: private FormDefinition formDefinition;
049: protected WidgetDefinition parent;
050: protected Library enclosingLibrary;
051:
052: //TODO consider final on these
053: private Location location = Location.UNKNOWN;
054: private String id;
055: /** the definition is mutable when being built */
056: private boolean mutable = true;
057: /** The initial map of attributes (can be null) */
058: private Map attributes;
059: private Map displayData;
060: private List validators;
061: private WidgetState state = WidgetState.ACTIVE;
062:
063: protected CreateListener createListener;
064:
065: public FormDefinition getFormDefinition() {
066: if (this .formDefinition == null) {
067: if (this instanceof FormDefinition) {
068: this .formDefinition = (FormDefinition) this ;
069: } else if (this .parent != null) {
070: this .formDefinition = this .parent.getFormDefinition();
071: } else {
072: // no form definition in this widget tree, must be in a library!
073: return null;
074: }
075: }
076: return this .formDefinition;
077: }
078:
079: public Library getEnclosingLibrary() {
080: if (this .enclosingLibrary == null) {
081: this .enclosingLibrary = this .parent.getEnclosingLibrary();
082: }
083: return this .enclosingLibrary;
084: }
085:
086: public void setEnclosingLibrary(Library library) {
087: enclosingLibrary = library;
088: }
089:
090: /**
091: * initialize this definition with the other, sort of like a copy constructor
092: */
093: public void initializeFrom(WidgetDefinition definition)
094: throws Exception {
095: if (!(definition instanceof AbstractWidgetDefinition)) {
096: throw new FormsException("Ancestor definition "
097: + definition.getClass().getName()
098: + " is not an AbstractWidgetDefinition.",
099: getLocation());
100: }
101:
102: AbstractWidgetDefinition other = (AbstractWidgetDefinition) definition;
103:
104: this .state = other.state;
105: this .createListener = other.createListener; // this works, we don't really remove listeners, right?
106:
107: this .validators = new ArrayList();
108: if (other.validators != null) {
109: for (int i = 0; i < other.validators.size(); i++) {
110: this .validators.add(other.validators.get(i));
111: }
112: }
113:
114: if (other.attributes != null) {
115: if (attributes == null) {
116: attributes = new HashMap();
117: }
118: attributes.putAll(other.attributes);
119: }
120: if (other.displayData != null) {
121: if (displayData == null) {
122: displayData = new HashMap();
123: }
124: displayData.putAll(other.displayData);
125: }
126: }
127:
128: /**
129: * Checks if this definition is complete or not.
130: */
131: public void checkCompleteness() throws IncompletenessException {
132: // FormDefinition is the only one allowed not to have an ID
133: if (id == null || "".equals(id)
134: && !(this instanceof FormDefinition)) {
135: throw new IncompletenessException(
136: "Widget must have an id attribute.", this );
137: }
138:
139: // TODO: don't know what else to check now
140: }
141:
142: /**
143: * Locks this definition so that it becomes immutable.
144: */
145: public void makeImmutable() {
146: this .mutable = false;
147: }
148:
149: /**
150: * Check that this definition is mutable, i.e. is in setup phase. If not, throw an exception.
151: */
152: protected void checkMutable() {
153: if (!this .mutable) {
154: throw new IllegalStateException(
155: "Attempt to modify an immutable WidgetDefinition");
156: }
157: }
158:
159: /**
160: * Sets the parent of this definition
161: */
162: public void setParent(WidgetDefinition definition) {
163: //FIXME(SW) calling checkMutable() here is not possible as NewDefinition.resolve()
164: // does some weird reorganization of the definition tree
165: this .parent = definition;
166: }
167:
168: /**
169: * Gets the parent of this definition.
170: * This method returns null for the root definition.
171: */
172: public WidgetDefinition getParent() {
173: return this .parent;
174: }
175:
176: public WidgetState getState() {
177: return this .state;
178: }
179:
180: public void setState(WidgetState state) {
181: checkMutable();
182: this .state = state;
183: }
184:
185: public void setLocation(Location location) {
186: checkMutable();
187: this .location = location;
188: }
189:
190: public Location getLocation() {
191: return location;
192: }
193:
194: public String getId() {
195: return id;
196: }
197:
198: public void setId(String id) {
199: checkMutable();
200: this .id = id;
201: }
202:
203: public void setAttributes(Map attributes) {
204: checkMutable();
205:
206: if (this .attributes == null) {
207: this .attributes = attributes;
208: } else if (attributes != null) {
209: // merge attribute lists
210: this .attributes.putAll(attributes);
211: }
212: }
213:
214: public Object getAttribute(String name) {
215: if (this .attributes != null) {
216: return this .attributes.get(name);
217: }
218: return null;
219: }
220:
221: public void addCreateListener(CreateListener listener) {
222: checkMutable();
223: // Event listener daisy-chain
224: this .createListener = WidgetEventMulticaster.add(
225: this .createListener, listener);
226: }
227:
228: public void widgetCreated(Widget widget) {
229: if (this .createListener != null) {
230: widget.getForm().addWidgetEvent(new CreateEvent(widget));
231: }
232: }
233:
234: public void fireCreateEvent(CreateEvent event) {
235: // Check that this widget was created by the current definition
236: if (event.getSourceWidget().getDefinition() != this ) {
237: throw new IllegalArgumentException(
238: "Widget was not created by this definition");
239: }
240: if (this .createListener != null) {
241: this .createListener.widgetCreated(event);
242: }
243: }
244:
245: public void generateLabel(ContentHandler contentHandler)
246: throws SAXException {
247: generateDisplayData("label", contentHandler);
248: }
249:
250: /**
251: * Sets the various display data for this widget. This includes the label, hint and help.
252: * They must all be objects implementing the XMLizable interface. This approach
253: * allows to have mixed content in these data.
254: *
255: * @param displayData an association of {name, sax fragment}
256: */
257: public void setDisplayData(Map displayData) {
258: checkMutable();
259:
260: if (this .displayData == null) {
261: this .displayData = displayData;
262: return;
263: }
264: if (displayData == null) {
265: return;
266: }
267:
268: // merge displayData lists
269: Iterator entries = displayData.entrySet().iterator();
270: while (entries.hasNext()) {
271: Map.Entry entry = (Map.Entry) entries.next();
272: Object key = entry.getKey();
273: Object value = entry.getValue();
274: if (value != null || !this .displayData.containsKey(key)) {
275: this .displayData.put(key, value);
276: }
277: }
278: }
279:
280: public void addValidator(WidgetValidator validator) {
281: checkMutable();
282: if (this .validators == null) {
283: this .validators = new ArrayList();
284: }
285:
286: this .validators.add(validator);
287: }
288:
289: public void generateDisplayData(String name,
290: ContentHandler contentHandler) throws SAXException {
291: Object data = this .displayData.get(name);
292: if (data != null) {
293: ((XMLizable) data).toSAX(contentHandler);
294: } else if (!this .displayData.containsKey(name)) {
295: throw new IllegalArgumentException(
296: "Unknown display data name '" + name + "'");
297: }
298: }
299:
300: public void generateDisplayData(ContentHandler contentHandler)
301: throws SAXException {
302: // Output all non-null display data
303: Iterator iter = this .displayData.entrySet().iterator();
304: while (iter.hasNext()) {
305: Map.Entry entry = (Map.Entry) iter.next();
306: if (entry.getValue() != null) {
307: String name = (String) entry.getKey();
308:
309: // Enclose the data into a "wi:{name}" element
310: contentHandler.startElement(FormsConstants.INSTANCE_NS,
311: name, FormsConstants.INSTANCE_PREFIX_COLON
312: + name, XMLUtils.EMPTY_ATTRIBUTES);
313:
314: ((XMLizable) entry.getValue()).toSAX(contentHandler);
315:
316: contentHandler.endElement(FormsConstants.INSTANCE_NS,
317: name, FormsConstants.INSTANCE_PREFIX_COLON
318: + name);
319: }
320: }
321: }
322:
323: public boolean validate(Widget widget) {
324: if (this .validators == null) {
325: // No validators
326: return true;
327:
328: }
329: Iterator iter = this .validators.iterator();
330: while (iter.hasNext()) {
331: WidgetValidator validator = (WidgetValidator) iter.next();
332: if (!validator.validate(widget)) {
333: // Stop at the first validator that fails
334: return false;
335: }
336: }
337: // All validators were sucessful
338: return true;
339: }
340: }
|