cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jheym...@apache.org
Subject svn commit: r330548 [94/132] - in /cocoon/whiteboard/maven2/cocoon-flat-layout: ./ cocoon-ajax-block/ cocoon-ajax-block/api/ cocoon-ajax-block/api/src/ cocoon-ajax-block/api/src/main/ cocoon-ajax-block/api/src/main/java/ cocoon-ajax-block/api/src/main/...
Date Thu, 03 Nov 2005 14:00:48 GMT
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,786 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * 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.apache.cocoon.forms.flow.javascript.v3;
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cocoon.forms.datatype.Datatype;
+import org.apache.cocoon.forms.datatype.SelectionList;
+import org.apache.cocoon.forms.event.ActionEvent;
+import org.apache.cocoon.forms.event.ActionListener;
+import org.apache.cocoon.forms.event.ActionListenerEnabled;
+import org.apache.cocoon.forms.event.ValueChangedEvent;
+import org.apache.cocoon.forms.event.ValueChangedListener;
+import org.apache.cocoon.forms.event.ValueChangedListenerEnabled;
+import org.apache.cocoon.forms.formmodel.Action;
+import org.apache.cocoon.forms.formmodel.AggregateField;
+import org.apache.cocoon.forms.formmodel.BooleanField;
+import org.apache.cocoon.forms.formmodel.ContainerWidget;
+import org.apache.cocoon.forms.formmodel.DataWidget;
+import org.apache.cocoon.forms.formmodel.Field;
+import org.apache.cocoon.forms.formmodel.Form;
+import org.apache.cocoon.forms.formmodel.MultiValueField;
+import org.apache.cocoon.forms.formmodel.Output;
+import org.apache.cocoon.forms.formmodel.Repeater;
+import org.apache.cocoon.forms.formmodel.SelectableWidget;
+import org.apache.cocoon.forms.formmodel.Submit;
+import org.apache.cocoon.forms.formmodel.Upload;
+import org.apache.cocoon.forms.formmodel.Widget;
+import org.apache.cocoon.forms.formmodel.WidgetState;
+import org.apache.cocoon.forms.validation.ValidationError;
+import org.apache.cocoon.forms.validation.ValidationErrorAware;
+import org.apache.cocoon.forms.validation.WidgetValidator;
+import org.apache.commons.lang.BooleanUtils;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.NativeArray;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.Wrapper;
+
+/**
+ * @version $Id: ScriptableWidget.java 161264 2005-04-14 12:32:26Z sylvain $
+ * 
+ */
+public class ScriptableWidget extends ScriptableObject implements ValueChangedListener, ActionListener, WidgetValidator {
+
+    final static String WIDGETS_PROPERTY = "__widgets__";
+
+    Widget delegate;
+    ScriptableWidget formWidget;
+    private Function onChange;
+    private Function onActivate;
+    private Function validator;
+
+    public String getClassName() {
+        return "Widget";
+    }
+
+    public ScriptableWidget() {
+    }
+
+    public ScriptableWidget(Object widget) {
+        this.delegate = (Widget)unwrap(widget);
+        if (delegate instanceof Form) {
+            formWidget = this;
+            Map widgetMap = new HashMap();
+            widgetMap.put(delegate, this);
+            defineProperty(WIDGETS_PROPERTY, widgetMap, DONTENUM|PERMANENT);
+        }
+    }
+
+    static private Object unwrap(Object obj) {
+        if (obj == Undefined.instance) {
+            return null;
+        }
+        if (obj instanceof Wrapper) {
+            return ((Wrapper)obj).unwrap();
+        }
+        return obj;
+    }
+
+    private void deleteWrapper(Widget w) {
+        if (delegate instanceof Form) {
+            Map widgetMap = (Map)super.get(WIDGETS_PROPERTY, this);
+            widgetMap.remove(w);
+        }
+    }
+
+    private ScriptableWidget wrap(Widget w) {
+        if (w == null) return null;
+        if (delegate instanceof Form) {
+            Map widgetMap = (Map)super.get(WIDGETS_PROPERTY, this);
+            ScriptableWidget result = null;
+            result = (ScriptableWidget)widgetMap.get(w);
+            if (result == null) {
+                result = new ScriptableWidget(w);
+                result.formWidget = this;
+                result.setPrototype(getClassPrototype(this, getClassName()));
+                result.setParentScope(getParentScope());
+                widgetMap.put(w, result);
+            }
+            return result;
+        } else {
+            return formWidget.wrap(w);
+        }
+    }
+
+    public boolean has(String id, Scriptable start) {
+        if (delegate != null && delegate instanceof ContainerWidget) {
+            Widget sub = ((ContainerWidget)delegate).getChild(id);
+            if (sub != null) {
+                return true;
+            }
+        } 
+        return super.has(id, start);
+    }
+
+    public boolean has(int index, Scriptable start) {
+        if (super.has(index, start)) {
+            return true;
+        }
+        if (delegate instanceof Repeater) {
+            Repeater repeater = (Repeater)delegate;
+            return index >= 0 && index < repeater.getSize();
+        }
+        if (delegate instanceof MultiValueField) {
+            Object[] values = (Object[])delegate.getValue();
+            return index >= 0 && index < values.length;
+        }
+        return false;
+    }
+
+    public Object get(String id, Scriptable start) {
+        Object result = super.get(id, start);
+        if (result != NOT_FOUND) {
+            return result;
+        }
+        if (delegate != null && delegate instanceof ContainerWidget) {
+            Widget sub = ((ContainerWidget)delegate).getChild(id);
+            if (sub != null) {
+                return wrap(sub);
+            }
+        }
+        return NOT_FOUND;
+    }
+
+    public Object get(int index, Scriptable start) {
+        Object result = super.get(index, start);
+        if (result != NOT_FOUND) {
+            return result;
+        }
+        if (delegate instanceof Repeater) {
+            Repeater repeater = (Repeater)delegate;
+            if (index >= 0) {
+                int count = index + 1 - repeater.getSize();
+                if (count > 0) {
+                    ScriptableWidget[] rows = new ScriptableWidget[count];
+                    for (int i = 0; i < count; i++) {
+                        rows[i] = wrap(repeater.addRow());
+                    }
+                    for (int i = 0; i < count; i++) {
+                        rows[i].notifyAddRow();
+                    }
+                }
+                return wrap(repeater.getRow(index));
+            }
+        } else if (delegate instanceof MultiValueField) {
+            Object[] values = (Object[])delegate.getValue();
+            if (index >= 0 && index < values.length) {
+                return values[index];
+            }
+        }
+        return NOT_FOUND;
+    }
+
+    public Object[] getAllIds() {
+        Object[] result = super.getAllIds();
+        return addWidgetIds(result);
+    }
+
+    public Object[] getIds() {
+        Object[] result = super.getIds();
+        return addWidgetIds(result);
+    }
+
+    private Object[] addWidgetIds(Object[] result) {
+        if (delegate instanceof ContainerWidget) {
+            Iterator iter = ((ContainerWidget)delegate).getChildren();
+            List list = new LinkedList();
+            for (int i = 0; i < result.length; i++) {
+                list.add(result[i]);
+            }
+            while (iter.hasNext()) {
+                Widget widget = (Widget)iter.next();
+                list.add(widget.getId());
+            }
+            result = list.toArray();
+        }
+        return result;
+    }
+
+    private void deleteRow(Repeater repeater, int index) {
+        Widget row = repeater.getRow(index);
+        ScriptableWidget s = wrap(row);
+        s.notifyRemoveRow();
+        formWidget.deleteWrapper(row);
+        repeater.removeRow(index);
+    }
+
+    private void notifyAddRow() {
+        ScriptableWidget repeater = wrap(delegate.getParent());
+        Object prop = getProperty(repeater, "onAddRow");
+        if (prop instanceof Function) {
+            try {
+                Function fun = (Function)prop;
+                Object[] args = new Object[1];
+                Scriptable scope = getTopLevelScope(this);
+                Scriptable thisObj = scope;
+                Context cx = Context.getCurrentContext();
+                args[0] = this;
+                fun.call(cx, scope, thisObj, args);
+            } catch (Exception exc) {
+                throw Context.reportRuntimeError(exc.getMessage());
+            }
+        }
+    }
+
+    private void notifyRemoveRow() {
+        ScriptableWidget repeater = wrap(delegate.getParent());
+        Object prop = getProperty(repeater, "onRemoveRow");
+        if (prop instanceof Function) {
+            try {
+                Function fun = (Function)prop;
+                Object[] args = new Object[1];
+                Scriptable scope = getTopLevelScope(this);
+                Scriptable thisObj = scope;
+                Context cx = Context.getCurrentContext();
+                args[0] = this;
+                fun.call(cx, scope, thisObj, args);
+            } catch (Exception exc) {
+                throw Context.reportRuntimeError(exc.getMessage());
+            }
+        }
+    }
+
+    public void delete(int index) {
+        if (delegate instanceof Repeater) {
+            Repeater repeater = (Repeater)delegate;
+            if (index >= 0 && index < repeater.getSize()) {
+                deleteRow(repeater, index);
+                return;
+            }
+        } else if (delegate instanceof MultiValueField) {
+            MultiValueField field = (MultiValueField)delegate;
+            Object[] values = (Object[])field.getValue();
+            if (values != null && values.length > index) {
+                Object[] newValues = new Object[values.length-1];
+                int i;
+                for (i = 0; i < index; i++) {
+                    newValues[i] = values[i];
+                }
+                i++;
+                for (;i < values.length; i++) {
+                    newValues[i-1] = values[i];
+                }
+                field.setValues(newValues);
+            }
+            return;
+        }
+        super.delete(index);
+    }
+
+    public Object jsGet_value() {
+        return delegate.getValue();
+    }
+
+    public Object jsFunction_getValue() {
+        return jsGet_value();
+    }
+
+    public void jsFunction_setValue(Object value) throws JavaScriptException {
+        jsSet_value(value);
+    }
+
+    public void jsSet_length(int len) {
+        if (delegate instanceof Repeater) {
+            Repeater repeater = (Repeater)delegate;
+            int size = repeater.getSize();
+            if (size > len) {
+                while (repeater.getSize() > len) {
+                    deleteRow(repeater, repeater.getSize() - 1);
+                }
+            } else {
+                for (int i = size; i < len; ++i) {
+                    wrap(repeater.addRow()).notifyAddRow();
+                }
+            }
+        }
+    }
+
+    public Object jsGet_length() {
+        if (delegate instanceof Repeater) {
+            Repeater repeater = (Repeater)delegate;
+            return new Integer(repeater.getSize());
+        }
+        return Undefined.instance;
+    }
+
+    public void jsSet_value(Object value) throws JavaScriptException {
+        if (delegate instanceof AggregateField) {
+            AggregateField aggregateField = (AggregateField)delegate;
+            if (value instanceof Scriptable) {
+                Scriptable obj = (Scriptable)value;
+                Object[] ids = obj.getIds();
+                for (int i = 0; i < ids.length; i++) {
+                    String id = String.valueOf(ids[i]);
+                    Object val = getProperty(obj, id);
+                    ScriptableWidget wid = wrap(aggregateField.getChild(id));
+                    if (wid == null) {
+                        throw new JavaScriptException("No field \"" + id + "\" in widget \"" + aggregateField.getId() + "\"");
+                    }
+                    if (wid.delegate instanceof Field || 
+                        wid.delegate instanceof BooleanField ||
+                        wid.delegate instanceof Output) {
+                        if (val instanceof Scriptable) {
+                            Scriptable s = (Scriptable)val;
+                            if (s.has("value", s)) {
+                                wid.jsSet_value(s.get("value", s));
+                            }
+                        }
+                    } else {
+                        wid.jsSet_value(val);
+                    }
+                }
+                aggregateField.combineFields();
+                return;
+            }
+            // fall through
+        }
+        if (delegate instanceof DataWidget) {
+            value = unwrap(value);
+            if (value != null) {
+                // Coerce values
+                Datatype datatype = ((DataWidget)delegate).getDatatype();
+                Class typeClass = datatype.getTypeClass();
+                if (typeClass == String.class) {
+                    value = Context.toString(value);
+                } else if (typeClass == boolean.class || 
+                           typeClass == Boolean.class) {
+                    value = Context.toBoolean(value) ? Boolean.TRUE : Boolean.FALSE;
+                } else {
+                    if (value instanceof Double) {
+                        // make cform accept a JS Number
+                        if (typeClass == long.class || typeClass == Long.class) {
+                            value = new Long(((Number)value).longValue());
+                        } else if (typeClass == int.class || 
+                                   typeClass == Integer.class) {
+                            value = new Integer(((Number)value).intValue());
+                        } else if (typeClass == float.class || 
+                                   typeClass == Float.class) {
+                            value = new Float(((Number)value).floatValue());
+                        } else if (typeClass == short.class || 
+                                   typeClass == Short.class) {
+                            value = new Short(((Number)value).shortValue());
+                        } else if (typeClass == BigDecimal.class) {
+                            value = new BigDecimal(((Number)value).doubleValue());
+                        }
+                    } 
+                }
+            }
+            delegate.setValue(value);
+        } else if (delegate instanceof BooleanField) {
+            BooleanField field = (BooleanField)delegate;
+            field.setValue(BooleanUtils.toBooleanObject(Context.toBoolean(value)));
+        } else if (delegate instanceof Repeater) {
+            Repeater repeater = (Repeater)delegate;
+            if (value instanceof NativeArray) {
+                NativeArray arr = (NativeArray)value;
+                Object length = getProperty(arr, "length");
+                int len = ((Number)length).intValue();
+                for (int i = repeater.getSize(); i >= len; --i) {
+                    deleteRow(repeater, i);
+                }
+                for (int i = 0; i < len; i++) {
+                    Object elemValue = getProperty(arr, i);
+                    ScriptableWidget wid = wrap(repeater.getRow(i));
+                    wid.jsSet_value(elemValue);
+                }
+            }
+        } else if (delegate instanceof Repeater.RepeaterRow) {
+            Repeater.RepeaterRow row = (Repeater.RepeaterRow)delegate;
+            if (value instanceof Scriptable) {
+                Scriptable obj = (Scriptable)value;
+                Object[] ids = obj.getIds();
+                for (int i = 0; i < ids.length; i++) {
+                    String id = String.valueOf(ids[i]);
+                    Object val = getProperty(obj, id);
+                    ScriptableWidget wid = wrap(row.getChild(id));
+                    if (wid == null) {
+                        throw new JavaScriptException("No field \"" + id + "\" in row " + i + " of repeater \"" + row.getParent().getId() + "\"");
+                    }
+                    if (wid.delegate instanceof Field || 
+                        wid.delegate instanceof BooleanField ||
+                        wid.delegate instanceof Output) {
+                        if (val instanceof Scriptable) {
+                            Scriptable s = (Scriptable)val;
+                            if (s.has("value", s)) {
+                                wid.jsSet_value(s.get("value", s));
+                            }
+                        }
+                    } else {
+                        wid.jsSet_value(val);
+                    }
+                }
+            } else {
+                throw new JavaScriptException("Expected an object instead of: " + Context.toString(value));
+            }
+        } else if (delegate instanceof MultiValueField) {
+            MultiValueField field = (MultiValueField)delegate;
+            Object[] values = null;
+            if (value instanceof NativeArray) {
+                NativeArray arr = (NativeArray)value;
+                Object length = getProperty(arr, "length");
+                int len = ((Number)length).intValue();
+                values = new Object[len];
+                for (int i = 0; i < len; i++) {
+                    Object elemValue = getProperty(arr, i);
+                    values[i] = unwrap(elemValue);
+                }
+            } else if (value instanceof Object[]) {
+                values = (Object[])value;
+            }
+            field.setValues(values);
+        } else {
+            delegate.setValue(value);
+        }
+    }
+
+    public String jsFunction_getId() {
+        return delegate.getId();
+    }
+
+    public WidgetState jsGet_state() {
+        return delegate.getState();
+    }
+
+    public void jsSet_state(Object stateObj) {
+        Object obj = unwrap(stateObj);
+        WidgetState state = null;
+
+        if (obj instanceof String) {
+            state = WidgetState.stateForName((String)obj);
+        } else if (obj instanceof WidgetState) {
+            state = (WidgetState)obj;
+        }
+
+        if (state == null) {
+            throw new IllegalArgumentException("Invalid value for widgetState " + stateObj);
+        }
+
+        delegate.setState(state);
+    }
+
+    public ScriptableWidget jsFunction_getSubmitWidget() {
+        return wrap(delegate.getForm().getSubmitWidget());
+    }
+
+    public String jsFunction_getRequestParameterName() {
+        return delegate.getRequestParameterName();
+    }
+
+//    public String jsFunction_getNamespace() {
+//        return delegate.getNamespace();
+//    }
+
+    public Object jsFunction_getParent() {
+        if (delegate != null) {
+            return wrap(delegate.getParent());
+        }
+        return Undefined.instance;
+    }
+
+    public boolean jsFunction_isRequired() {
+        return delegate.isRequired();
+    }
+    
+    public ScriptableWidget jsFunction_getForm() {
+        return formWidget;
+    }
+    
+    public boolean jsFunction_equals(Object other) {
+        if (other instanceof ScriptableWidget) {
+            ScriptableWidget otherWidget = (ScriptableWidget)other;
+            return delegate.equals(otherWidget.delegate);
+        }
+        return false;
+    }
+
+    public ScriptableWidget jsFunction_lookupWidget(String id) {
+        Widget sub = null;
+        sub = delegate.lookupWidget(id);
+        return wrap(sub);
+    }
+
+    public void jsFunction_setValidationError(Object message /* null to clear error */, 
+                                              Object parameters) {
+        if (delegate instanceof ValidationErrorAware) {
+            String[] parms = null;
+            if (parameters != null && parameters != Undefined.instance) {
+                Scriptable obj = Context.toObject(parameters, this);
+                int len = (int)
+                    Context.toNumber(getProperty(obj, "length"));
+                parms = new String[len];
+                for (int i = 0; i < len; i++) {
+                    parms[i] = Context.toString(getProperty(obj, i));
+                }
+            }
+            ValidationError validationError = null;
+            if (message != null) {
+                if (parms != null && parms.length > 0) {
+                    validationError = 
+                        new ValidationError(Context.toString(message), parms);
+                } else {
+                    validationError = 
+                        new ValidationError(Context.toString(message), parms != null);
+                }
+            }
+            ((ValidationErrorAware)delegate).setValidationError(validationError);
+        }
+    }
+
+    public void jsFunction_setAttribute(String name, Object value) {
+        delegate.setAttribute(name, value);
+    }
+
+    public Object jsFunction_getAttribute(String jsname) {
+        return delegate.getAttribute(jsname);
+    }
+
+    public void jsFunction_removeAttribute(String name) {
+        delegate.removeAttribute(name);
+    }
+
+    public Widget jsFunction_unwrap() {
+        return delegate;
+    }
+
+    public ScriptableWidget jsFunction_addRow() {
+        ScriptableWidget result = null;
+        if (delegate instanceof Repeater) {
+            result = wrap(((Repeater)delegate).addRow());
+            result.notifyAddRow();
+        }
+        return result;
+    }
+
+    public ScriptableObject jsFunction_getRow(int index) {
+        if (delegate instanceof Repeater) {
+            return wrap(((Repeater)delegate).getRow(index));
+        }
+        return null;
+    }
+
+    public void jsFunction_removeRow(Object obj) throws JavaScriptException {
+        if (delegate instanceof Repeater) {
+            Repeater repeater = (Repeater)delegate;
+            if (obj instanceof Function) {
+                Function fun = (Function)obj;
+                int len = repeater.getSize();
+                boolean[] index = new boolean[len];
+                Object[] args = new Object[1];
+                Scriptable scope = getTopLevelScope(this);
+                Scriptable thisObj = scope;
+                Context cx = Context.getCurrentContext();
+                for (int i = 0; i < len; i++) {
+                    ScriptableWidget row = wrap(repeater.getRow(i));
+                    args[0] = row;
+                    Object result = fun.call(cx, scope, thisObj, args);
+                    index[i] = Context.toBoolean(result);
+                }    
+                for (int i = len-1; i >= 0; --i) {
+                    if (index[i]) {
+                        deleteRow(repeater, i);
+                    }
+                }
+            } else {
+                int index = (int)Context.toNumber(obj);
+                if (index >= 0 && index < repeater.getSize()) {
+                    deleteRow(repeater, index);
+                }
+            } 
+        }
+    }
+
+    public Object jsGet_onChange() {
+        if (onChange != null)
+            return onChange;
+        else
+            return Undefined.instance;
+    }
+
+    public Object jsGet_onActivate() {
+        if (onActivate != null)
+            return onActivate;
+        else
+            return Undefined.instance;
+    }
+
+    public void jsSet_onChange(Object value) {
+        value = unwrap(value);
+        if (value == null) {
+            this.onChange = null;
+            if (delegate instanceof ValueChangedListenerEnabled) {
+                ((ValueChangedListenerEnabled)delegate).removeValueChangedListener(this);
+            }
+            return;
+        }
+
+        if (value instanceof Function) {
+            if (delegate instanceof ValueChangedListenerEnabled) {
+                if (this.onChange == null)
+                    ((ValueChangedListenerEnabled)delegate).addValueChangedListener(this);
+                this.onChange = (Function)value;
+            }
+        }
+
+    }
+
+    public void jsSet_onActivate(Object value) {
+        value = unwrap(value);
+        if (value == null) {
+            this.onActivate = null;
+            if (delegate instanceof ActionListenerEnabled) {
+                ((ActionListenerEnabled)delegate).removeActionListener(this);
+            }
+            return;
+        }
+
+        if (value instanceof Function) {
+            if (delegate instanceof ActionListenerEnabled) {
+                if (this.onActivate == null)
+                    ((ActionListenerEnabled)delegate).addActionListener(this);
+                this.onActivate = (Function)value;
+            }
+        }
+    }
+
+    public void valueChanged(ValueChangedEvent event) {
+        if (onChange != null) {
+            try {
+                Object[] args = new Object[2];
+                Scriptable scope = getTopLevelScope(this);
+                Scriptable thisObj = scope;
+                Context cx = Context.getCurrentContext();
+                args[0] = event.getOldValue();
+                args[1] = event.getNewValue();
+                onChange.call(cx, scope, thisObj, args);
+            } catch (Exception exc) {
+                throw Context.reportRuntimeError(exc.getMessage());
+            }
+        }
+    }
+
+    public void actionPerformed(ActionEvent event) {
+        if (onActivate != null) {
+            try {
+                Object[] args = new Object[1];
+                Scriptable scope = getTopLevelScope(this);
+                Scriptable thisObj = scope;
+                Context cx = Context.getCurrentContext();
+                args[0] = event.getActionCommand();
+                onActivate.call(cx, scope, thisObj, args);
+            } catch (Exception exc) {
+                throw Context.reportRuntimeError(exc.getMessage());
+            }
+        }
+    }
+
+    public Object jsGet_validator() {
+        if (validator != null)
+            return validator;
+        else
+            return Undefined.instance;
+    }
+
+    public void jsSet_validator(Object value) {
+        value = unwrap(value);
+        if (value == null) {
+            this.validator = null;
+            delegate.removeValidator(this);
+            return;
+        }
+
+        if (value instanceof Function) {
+            if (this.validator == null)
+                delegate.addValidator(this);
+            this.validator = (Function)value;
+        }
+
+    }
+
+    public boolean validate(Widget widget) {
+        try {
+            Object[] args = new Object[1];
+            Scriptable scope = getTopLevelScope(this);
+            Scriptable thisObj = scope;
+            Context cx = Context.getCurrentContext();
+            args[0] = this;
+            Object result = validator.call(cx, scope, thisObj, args);
+            return Context.toBoolean(result);
+        } catch (Exception exc) {
+            throw Context.reportRuntimeError(exc.getMessage());
+        }
+    }
+
+    public void jsFunction_setSelectionList(Object arg,
+                                            Object valuePathArg, 
+                                            Object labelPathArg) 
+        throws Exception {
+        if (delegate instanceof SelectableWidget) {
+            arg = unwrap(arg);
+            if (valuePathArg != Undefined.instance && labelPathArg != Undefined.instance) {
+                String valuePath = Context.toString(valuePathArg);
+                String labelPath = Context.toString(labelPathArg);
+                ((SelectableWidget)delegate).setSelectionList(arg, valuePath, labelPath);
+            } else {
+                if (arg instanceof SelectionList) {
+                    SelectionList selectionList = (SelectionList)arg;
+                    ((SelectableWidget)delegate).setSelectionList(selectionList);
+                } else {
+                    String str = Context.toString(arg);
+                    ((SelectableWidget)delegate).setSelectionList(str);
+                }
+            }
+        }
+    }
+
+    static final Object[] WIDGET_CLASS_MAP = {
+        Form.class, "Form",
+        Field.class, "Field",
+        Action.class, "Action",
+        Repeater.class, "Repeater",
+        Repeater.RepeaterRow.class, "RepeaterRow",
+        AggregateField.class, "AggregateField",
+        BooleanField.class, "BooleanField",
+        MultiValueField.class, "MultiValueField",
+        Output.class, "Output",
+        Submit.class, "Submit",
+        Upload.class, "Upload"
+    };
+
+    public String jsFunction_getWidgetClass() {
+        for (int i = 0; i < WIDGET_CLASS_MAP.length; i += 2) {
+            Class c = (Class)WIDGET_CLASS_MAP[i];
+            if (c.isAssignableFrom(delegate.getClass())) {
+                return (String)WIDGET_CLASS_MAP[i + 1];
+            }
+        }
+        return "<unknown>";
+    }
+
+    public String jsFunction_toString() {
+        return "[object Widget (" + jsFunction_getWidgetClass() + ")]";
+    }
+
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/flow/javascript/v3/ScriptableWidget.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinition.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinition.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinition.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinition.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * 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.apache.cocoon.forms.formmodel;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The {@link AbstractContainerDefinition} corresponding to an {@link AbstractContainerWidget}.
+ *
+ * @version $Id: AbstractContainerDefinition.java 289538 2005-09-16 13:46:22Z sylvain $
+ */
+public abstract class AbstractContainerDefinition
+        extends AbstractWidgetDefinition implements ContainerDefinition {
+    protected WidgetDefinitionList definitions;
+
+    public AbstractContainerDefinition() {
+        definitions = new WidgetDefinitionList(this);
+    }
+
+    public void createWidget(Widget parent, String id) {
+        definitions.createWidget(parent, id);
+    }
+
+    public void createWidgets(Widget parent) {
+        definitions.createWidgets(parent);
+    }
+    
+    /**
+     * initialize this definition with the other, sort of like a copy constructor
+     */
+    public void initializeFrom(WidgetDefinition definition) throws Exception {
+    	super.initializeFrom(definition);
+    	
+    	if(definition instanceof AbstractContainerDefinition) {
+    		AbstractContainerDefinition other = (AbstractContainerDefinition)definition;
+    		
+    		Iterator otherwidgets = other.definitions.getWidgetDefinitions().iterator();
+    		while(otherwidgets.hasNext()) {
+    			try {
+    				WidgetDefinition def = (WidgetDefinition)otherwidgets.next();
+    				this.definitions.addWidgetDefinition(def);
+    			} catch(DuplicateIdException ignore) {}
+    		}
+    	} else {
+    		throw new Exception("Definition to inherit from is not of the right type! (at "+getLocation()+")");
+    	}
+    }
+    
+    /**
+     * checks completeness of this definition
+     */
+    public void checkCompleteness() throws IncompletenessException {
+        super.checkCompleteness();
+        this.definitions.checkCompleteness();
+    }
+
+    public void addWidgetDefinition(WidgetDefinition definition) throws Exception, DuplicateIdException {
+        //FIXME: cannot enforce this check here as more children are added in the resolve() phase
+        //checkMutable();
+        definition.setParent(this);
+        definitions.addWidgetDefinition(definition);
+    }
+
+    public void resolve(List parents, WidgetDefinition parent) throws Exception {
+        definitions.resolve(parents, parent);
+    }
+
+    public boolean hasWidget(String id) {
+        return definitions.hasWidget(id);
+    }
+
+    public WidgetDefinition getWidgetDefinition(String id) {
+        return definitions.getWidgetDefinition(id);
+    }
+
+    public Collection getWidgetDefinitions() {
+        return definitions.getWidgetDefinitions();
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinition.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinitionBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinitionBuilder.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinitionBuilder.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinitionBuilder.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ * 
+ * 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.apache.cocoon.forms.formmodel;
+
+import org.apache.cocoon.forms.FormsConstants;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.w3c.dom.Element;
+
+/**
+ * Abstract base class for container widget builders.
+ * 
+ * @version $Id: AbstractContainerDefinitionBuilder.java 327169 2005-10-21 13:04:31Z sylvain $
+ */
+
+public abstract class AbstractContainerDefinitionBuilder extends AbstractWidgetDefinitionBuilder {
+
+    protected void setupContainer(Element element, String widgetsElementName, AbstractContainerDefinition definition) throws Exception {
+
+        Element widgetsElement = DomHelper.getChildElement(element, FormsConstants.DEFINITION_NS, widgetsElementName, false);
+
+        // if its not there, ignore it. Just means that there are no new widgets
+        if (widgetsElement == null)
+            return;
+
+        // All child elements of the widgets element are widgets
+        Element[] widgetElements = DomHelper.getChildElements(widgetsElement, FormsConstants.DEFINITION_NS);
+        WidgetDefinitionBuilderContext oldContext = this.context;
+
+        for (int i = 0; i < widgetElements.length; i++) {
+            Element widgetElement = widgetElements[i];
+
+            this.context = new WidgetDefinitionBuilderContext(oldContext);
+            this.context.setSuperDefinition(null);
+
+            String newId = DomHelper.getAttribute(widgetElement, "extends", null);
+            WidgetDefinition def = null;
+            if (newId != null) {
+                if ((def = this.context.getLocalLibrary().getDefinition(newId)) != null)
+                    this.context.setSuperDefinition(def);
+                else if ((def = definition.getWidgetDefinition(newId)) != null)
+                    this.context.setSuperDefinition(def);
+                // throw new Exception("Widget to inherit from ("+newId+") not
+                // found! (at "+DomHelper.getLocation(element)+")");
+            }
+
+            WidgetDefinition widgetDefinition = buildAnotherWidgetDefinition(widgetElement);
+
+            if (widgetDefinition != null)
+                definition.addWidgetDefinition(widgetDefinition);
+
+        }
+
+        this.context = oldContext;
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerWidget.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerWidget.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerWidget.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerWidget.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,139 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * 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.apache.cocoon.forms.formmodel;
+
+import java.util.Iterator;
+import java.util.Locale;
+
+import org.apache.cocoon.forms.FormContext;
+import org.apache.cocoon.forms.validation.ValidationError;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * A general-purpose abstract Widget which can hold zero or more widgets.
+ *
+ * @version $Id: AbstractContainerWidget.java 293356 2005-10-03 14:10:45Z sylvain $
+ */
+public abstract class AbstractContainerWidget extends AbstractWidget implements ContainerWidget {
+
+    /**
+     * List of contained widgets.
+     */
+    protected WidgetList widgets;
+    
+    /**
+     * validation errors on container widgets
+     */
+    protected ValidationError validationError;    
+
+    /**
+     * Constructs AbstractContainerWidget
+     */
+    public AbstractContainerWidget(AbstractContainerDefinition definition) {
+        super(definition);
+        widgets = new WidgetList();
+    }
+
+    /**
+     * Called after widget's environment has been setup,
+     * to allow for any contextual initalization such as
+     * looking up case widgets for union widgets.
+     */
+    public void initialize() {
+        Iterator it = this.getChildren();
+        while(it.hasNext()) {
+          ((Widget)it.next()).initialize();
+        }
+        
+        super.initialize();
+    }
+
+    public void addChild(Widget widget) {
+        // order is important
+        widgets.addWidget(widget);
+        widget.setParent(this);
+    }
+
+    public boolean hasChild(String id) {
+        return widgets.hasWidget(id);
+    }
+
+    public Widget getChild(String id) {
+        return widgets.getWidget(id);
+    }
+
+    public Iterator getChildren() {
+        return widgets.iterator();
+    }
+    
+    public int getSize() {
+        return widgets.getWidgetList().size();
+    }
+    
+    /**
+     * Delegates the readFromRequest() down to the contained child-widgets.
+     *
+     * When overriding one should call <code>super.readFromRequest()</code>
+     * to allow child-widgets to process the request.
+     *
+     * Overide only to add possible request-reading statements on the containment level.
+     *
+     * @param formContext to be passed to the {@link Widget#readFromRequest(FormContext)}
+     *                    of the contained widgets.
+     */
+    public void readFromRequest(FormContext formContext) {
+        if (getCombinedState().isAcceptingInputs()) {
+            widgets.readFromRequest(formContext);
+        }
+    }
+
+    /**
+     * Delegates the <code>validate()</code> down to the contained child-widgets,
+     * and validates the extra rules on this containment level regardless of
+     * children widget's validities.
+     *
+     * <p>When overriding one should call <code>super.validate()</code> as the first
+     * statement to keep in sync with this behaviour.</p>
+     *
+     * @return <code>true</code> only if all contained widgets are valid and the
+     *         extra validation rules on this containment level are ok.
+     */
+    public boolean validate() {
+        if (!getCombinedState().isValidatingValues()) {
+            this.wasValid = true;
+            return true;
+        }
+        // Validate children first, then always validate self. Return combined result.
+        final boolean valid = widgets.validate();
+        this.wasValid = super.validate() && valid;
+        return this.wasValid;
+    }
+
+    /**
+     * Subclass container widgets can call this super.generateItemSaxFragment(..)
+     * to just insert the child-widget content wrapped in a @lt;fi:widgets@gt;
+     *
+     * @param contentHandler where the SAX is sent to via {@link Widget#generateSaxFragment(ContentHandler, Locale)}
+     * @param locale
+     * @throws SAXException
+     */
+    public void generateItemSaxFragment(ContentHandler contentHandler, Locale locale) throws SAXException {
+        if (getCombinedState().isDisplayingValues()) {
+            widgets.generateSaxFragment(contentHandler, locale);
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractContainerWidget.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinition.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinition.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinition.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinition.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * 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.apache.cocoon.forms.formmodel;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.cocoon.forms.datatype.Datatype;
+import org.apache.cocoon.forms.datatype.DynamicSelectionList;
+import org.apache.cocoon.forms.datatype.FlowJXPathSelectionList;
+import org.apache.cocoon.forms.datatype.SelectionList;
+import org.apache.cocoon.forms.event.ValueChangedEvent;
+import org.apache.cocoon.forms.event.ValueChangedListener;
+import org.apache.cocoon.forms.event.WidgetEventMulticaster;
+
+/**
+ * Base class for WidgetDefinitions that use a Datatype and SelectionList.
+ * 
+ * @version $Id: AbstractDatatypeWidgetDefinition.java 289538 2005-09-16 13:46:22Z sylvain $
+ */
+public abstract class AbstractDatatypeWidgetDefinition extends AbstractWidgetDefinition implements Serviceable {
+    private Datatype datatype;
+    private Object initialValue;
+    private SelectionList selectionList;
+    private ValueChangedListener listener;
+    private ServiceManager manager;
+
+    public void service(ServiceManager manager) throws ServiceException {
+        checkMutable();
+        this.manager = manager;
+    }
+    
+    /**
+     * checks 
+     * @return
+     */
+    public void checkCompleteness() throws IncompletenessException {
+    	super.checkCompleteness();
+    	
+    	if(this.datatype==null)
+    		throw new IncompletenessException("A datatype element is required!",this);
+    	
+    }
+    
+    /**
+     * initialize this definition with the other, sort of like a copy constructor
+     */
+    public void initializeFrom(WidgetDefinition definition) throws Exception {
+    	super.initializeFrom(definition);
+    	
+    	if(definition instanceof AbstractDatatypeWidgetDefinition) {
+    		AbstractDatatypeWidgetDefinition other = (AbstractDatatypeWidgetDefinition)definition;
+    		
+    		this.datatype = other.datatype;
+    		this.initialValue = other.initialValue;
+    		this.selectionList = other.selectionList;
+    		this.listener = other.listener;
+    		
+    	} else {
+    		throw new Exception("Definition to inherit from is not of the right type! (at "+getLocation()+")");
+    	}
+    }
+
+    public Datatype getDatatype() {
+        return datatype;
+    }
+    
+    public Object getInitialValue() {
+        return this.initialValue;
+    }
+
+    public void setDatatype(Datatype datatype, Object initialValue) {
+        checkMutable();
+        this.datatype = datatype;
+        this.initialValue = initialValue;
+    }
+
+    public void setSelectionList(SelectionList selectionList) {
+        checkMutable();
+        if (selectionList != null && selectionList.getDatatype() != getDatatype())
+            throw new RuntimeException("Tried to assign a SelectionList that is not associated with this widget's datatype.");
+        this.selectionList = selectionList;
+    }
+
+    public SelectionList getSelectionList() {
+        return selectionList;
+    }
+    
+    /**
+     * Builds a dynamic selection list from a source. This is a helper method for widget instances whose selection
+     * list source has to be changed dynamically, and it does not modify this definition's selection list,
+     * if any.
+     * @param uri The URI of the source. 
+     */
+    public SelectionList buildSelectionList(String uri) {
+        return new DynamicSelectionList(datatype, uri, this.manager);
+    }
+    
+    /**
+     * Builds a dynamic selection list from an in-memory collection.
+     * This is a helper method for widget instances whose selection
+     * list has to be changed dynamically, and it does not modify this definition's selection list,
+     * if any.
+     * @see org.apache.cocoon.forms.formmodel.Field#setSelectionList(Object model, String valuePath, String labelPath)
+     * @param model The collection used as a model for the selection list. 
+     * @param valuePath An XPath expression referring to the attribute used
+     * to populate the values of the list's items. 
+     * @param labelPath An XPath expression referring to the attribute used
+     * to populate the labels of the list's items.
+     */
+    public SelectionList buildSelectionListFromModel(Object model, String valuePath, String labelPath) {
+        return new FlowJXPathSelectionList(model, valuePath, labelPath, datatype);
+    }
+    
+    public void addValueChangedListener(ValueChangedListener listener) {
+        checkMutable();
+        this.listener = WidgetEventMulticaster.add(this.listener, listener);
+    }
+    
+    public void fireValueChangedEvent(ValueChangedEvent event) {
+        if (this.listener != null) {
+            this.listener.valueChanged(event);
+        }
+    }
+    
+    public boolean hasValueChangedListeners() {
+        return this.listener != null;
+    }
+
+    public ValueChangedListener getValueChangedListener() {
+        return this.listener;
+    }
+
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinition.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinitionBuilder.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinitionBuilder.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinitionBuilder.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinitionBuilder.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * 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.apache.cocoon.forms.formmodel;
+
+import java.util.Iterator;
+import java.util.Locale;
+
+import org.apache.avalon.framework.service.ServiceSelector;
+import org.apache.cocoon.forms.FormsConstants;
+import org.apache.cocoon.forms.datatype.Datatype;
+import org.apache.cocoon.forms.datatype.SelectionList;
+import org.apache.cocoon.forms.datatype.SelectionListBuilder;
+import org.apache.cocoon.forms.datatype.convertor.ConversionResult;
+import org.apache.cocoon.forms.event.ValueChangedListener;
+import org.apache.cocoon.forms.util.DomHelper;
+import org.apache.cocoon.i18n.I18nUtils;
+import org.w3c.dom.Element;
+
+/**
+ * Abstract base class for WidgetDefinitionBuilders that build widgets that have datatypes/selection lists.
+ * 
+ * @version $Id: AbstractDatatypeWidgetDefinitionBuilder.java 326838 2005-10-20 06:26:53Z sylvain $
+ */
+public abstract class AbstractDatatypeWidgetDefinitionBuilder extends AbstractWidgetDefinitionBuilder {
+    
+    protected void setupDefinition(Element widgetElement, AbstractDatatypeWidgetDefinition definition) throws Exception {
+        super.setupDefinition(widgetElement, definition);
+        // parse "label", "hint", etc.
+        setDisplayData(widgetElement, definition);
+
+        // parse "on-value-changed"
+        Iterator iter = buildEventListeners(widgetElement, "on-value-changed", ValueChangedListener.class).iterator();
+        while (iter.hasNext()) {
+            definition.addValueChangedListener((ValueChangedListener)iter.next());
+        }
+        
+        //---- parse "datatype"
+        Element datatypeElement = DomHelper.getChildElement(widgetElement, FormsConstants.DEFINITION_NS, "datatype");
+        if (datatypeElement != null) {
+        	Datatype datatype = datatypeManager.createDatatype(datatypeElement, false);
+        	
+        	// ---- parse "initial-value"
+            Object initialValue = null;
+            Element initialValueElement = DomHelper.getChildElement(widgetElement, FormsConstants.DEFINITION_NS, "initial-value", false);
+            if (initialValueElement != null) {
+                String localeValue = DomHelper.getAttribute(initialValueElement, "locale", null);
+                Locale locale = localeValue == null ? null : I18nUtils.parseLocale(localeValue);
+                String value = DomHelper.getElementText(initialValueElement);
+                ConversionResult result = datatype.convertFromString(value, locale);
+                if (!result.isSuccessful()) {
+                    throw new Exception("Cannot parse initial value '" + value + "' at " +
+                            DomHelper.getLocation(initialValueElement));
+                }
+                initialValue = result.getResult();
+            }
+            
+            definition.setDatatype(datatype, initialValue);
+        }
+        
+        
+        //---- parse "selection-list"
+        // FIXME: pass the manager to the definition as a side effect. Should be removed
+        // when definition are managed like components.
+        definition.service(this.serviceManager);
+
+        SelectionList list = buildSelectionList(widgetElement, definition, "selection-list");
+        if (list != null) {
+            definition.setSelectionList(list);
+        }
+    }
+    
+    protected SelectionList buildSelectionList(
+            Element widgetElement, AbstractDatatypeWidgetDefinition definition, String name) throws Exception {
+        Element selectionListElement = DomHelper.getChildElement(widgetElement, FormsConstants.DEFINITION_NS, name);
+        
+        if(selectionListElement != null && definition.getDatatype() == null)
+            throw new Exception("A widget with a selection list always requires a datatype as well! (at "+DomHelper.getLocation(selectionListElement)+" )");
+        
+        if (selectionListElement == null)
+            return null;
+
+        // Get an appropriate list builder
+        ServiceSelector builderSelector = (ServiceSelector)this.serviceManager.lookup(SelectionListBuilder.ROLE + "Selector");
+        SelectionListBuilder builder = null;
+        try {
+            // listType can be null, meaning we will use the default selection list
+            String listType = selectionListElement.getAttribute("type");
+            if ("".equals(listType)) {
+                listType = null;
+            }
+
+            builder = (SelectionListBuilder)builderSelector.select(listType);
+            return builder.build(selectionListElement, definition.getDatatype());
+
+        } finally {
+            if (builder != null) {
+                builderSelector.release(builder);
+            }
+            this.serviceManager.release(builderSelector);
+        }
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractDatatypeWidgetDefinitionBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,550 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * 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.apache.cocoon.forms.formmodel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.cocoon.forms.FormsConstants;
+import org.apache.cocoon.forms.event.CreateEvent;
+import org.apache.cocoon.forms.event.ValueChangedListenerEnabled;
+import org.apache.cocoon.forms.event.WidgetEvent;
+import org.apache.cocoon.forms.validation.ValidationErrorAware;
+import org.apache.cocoon.forms.validation.WidgetValidator;
+import org.apache.cocoon.util.location.Location;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Abstract base class for Widget implementations. Provides functionality
+ * common to many widgets.
+ *
+ * @version $Id: AbstractWidget.java 326930 2005-10-20 16:03:19Z sylvain $
+ */
+public abstract class AbstractWidget implements Widget {
+
+    /**
+     * Containing parent-widget to this widget.
+     * NOTE: avoid directly accessing this member since subclasses can mask this
+     * property through own implemented getParent()
+     */
+    private Widget parent;
+
+    /**
+     * The widget's own state
+     */
+    private WidgetState state = WidgetState.ACTIVE;
+
+    /**
+     * Lazy loaded reference to the top-level form.
+     */
+    private Form form;
+
+    /**
+     * Validation-rules local to the widget instance
+     */
+    private List validators;
+
+    /**
+     * Storage for the widget allocated attributes
+     */
+    private Map attributes;
+
+    /**
+     * The result of the last call to {@link #validate()}.
+     */
+    protected boolean wasValid = true;
+
+
+    protected AbstractWidget(AbstractWidgetDefinition definition) {
+        this.state = definition.getState();
+    }
+
+    /**
+     * Called after widget's environment has been setup,
+     * to allow for any contextual initalization, such as
+     * looking up case widgets for union widgets.
+     */
+    public void initialize() {
+        ((AbstractWidgetDefinition)getDefinition()).widgetCreated(this);
+    }
+
+    /**
+     * Gets the id of this widget.
+     */
+    public String getId() {
+        return getDefinition().getId();
+    }
+    
+    public String getName() {
+        return getId();
+    }
+
+    /**
+     * Concrete subclasses should allow access to their underlaying Definition
+     * through this method.
+     *
+     * If subclasses decide to return <code>null</code> they should also organize
+     * own implementations of {@link #getId()}, {@link #getLocation()},
+     * {@link #validate()}, {@link #generateLabel(ContentHandler)} and
+     * {@link #generateDisplayData(ContentHandler)} to avoid NPE's.
+     *
+     * @return the widgetDefinition from which this widget was instantiated.
+     *        (@link WidgetDefinition#createInstance()}
+     */
+    public abstract WidgetDefinition getDefinition();
+
+    /**
+     * @return the location-information (file, line and column) where this widget was
+     * configured.
+     */
+    public Location getLocation() {
+        return getDefinition().getLocation();
+    }
+
+    /**
+     * @return The parent-widget of this widget.
+     */
+    // This method is final in order for other methods in this class to use this.parent
+    public final Widget getParent() {
+        return this.parent;
+    }
+
+    /**
+     * Sets the parent-widget of this widget.
+     * This is a write-once property.
+     *
+     * @param widget the parent-widget of this one.
+     * @throws IllegalStateException when the parent had already been set.
+     */
+    public void setParent(Widget widget) {
+        if (this.parent != null) {
+            throw new IllegalStateException("The parent of widget " + getRequestParameterName() + " should only be set once.");
+        }
+        this.parent = widget;
+    }
+
+    /**
+     * @return the form where this widget belongs to.
+     */
+    public Form getForm() {
+        if (this.form == null) {
+            Widget myParent = getParent();
+            if (myParent == null) {
+                this.form = (Form)this;
+            } else {
+                this.form = myParent.getForm();
+            }
+        }
+        return this.form;
+    }
+
+    public WidgetState getState() {
+        return this.state;
+    }
+
+    public void setState(WidgetState state) {
+        if (state == null) {
+            throw new IllegalArgumentException("A widget state cannot be set to null");
+        }
+        this.state = state;
+
+        // Update the browser
+        getForm().addWidgetUpdate(this);
+    }
+
+    public WidgetState getCombinedState() {
+        if (this.parent == null) {
+            return this.state;
+        }
+        return WidgetState.strictest(this.state, this.parent.getCombinedState());
+    }
+
+    // Cached param names, used to speed up execution of the method below while
+    // still allowing ids to change (e.g. repeater rows when they are reordered).
+    private String cachedParentParamName;
+    private String cachedParamName;
+    
+    /**
+     * Should be called when a widget's own name has changed, in order to clear
+     * internal caches used to compute request parameters.
+     */
+    protected void widgetNameChanged() {
+        this.cachedParentParamName = null;
+        this.cachedParamName = null;
+    }
+    
+    public String getFullName() {
+        return getRequestParameterName();
+    }
+    
+    public String getRequestParameterName() {
+
+        if (this.parent == null) {
+            return getId();
+        }
+
+        String parentParamName = parent.getRequestParameterName();
+        if (parentParamName == this.cachedParentParamName) {
+            // Parent name hasn't changed, so ours hasn't changed too
+            return this.cachedParamName;
+        }
+
+        // Compute our name and cache it
+        this.cachedParentParamName = parentParamName;
+        if (this.cachedParentParamName.length() == 0) {
+            // the top level form returns an id == ""
+            this.cachedParamName = getId();
+        } else {
+            this.cachedParamName = this.cachedParentParamName + "." + getId();
+        }
+
+        return this.cachedParamName;
+    }
+
+    public Widget lookupWidget(String path) {
+
+        if (path == null || path.equals("")) {
+            return this;
+        }
+
+        Widget relativeWidget;
+        String relativePath = null;
+        int sepPosition = path.indexOf("" + Widget.PATH_SEPARATOR);
+
+        if (sepPosition < 0) {
+            //last step
+            if (path.startsWith("..")) return getParent();
+            return getChild(path);
+        } else if (sepPosition == 0) {
+            //absolute path
+            relativeWidget = getForm();
+            relativePath = path.substring(1);
+        } else {
+        	if (path.startsWith(".." + Widget.PATH_SEPARATOR))  {
+        		relativeWidget = getParent();
+                relativePath = path.substring(3);
+            } else {
+                String childId = path.substring(0, sepPosition );
+                relativeWidget = getChild(childId);
+                relativePath = path.substring(sepPosition+1);
+            }
+        }
+
+        if (relativeWidget == null) return null;
+        return relativeWidget.lookupWidget(relativePath);
+    }
+
+    /**
+     * Concrete widgets that contain actual child widgets should override to
+     * return the actual child-widget.
+     *
+     * @param id of the child-widget
+     * @return <code>null</code> if not overriden.
+     */
+    protected Widget getChild(String id) {
+    	return null;
+    }
+
+    public Widget getWidget(String id) {
+        throw new UnsupportedOperationException("getWidget(id) got deprecated from the API. \n" +
+                "Consider using getChild(id) or even lookupWidget(path) instead.");
+    }
+
+    public Object getValue() {
+        throw new UnsupportedOperationException("Widget " + toString() + " has no value, at " + getLocation());
+    }
+
+    public void setValue(Object object) {
+        throw new UnsupportedOperationException("Widget " + toString() + " has no value, at " + getLocation());
+    }
+
+    public boolean isRequired() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Abstract implementation throws a {@link UnsupportedOperationException}.
+     * Concrete subclass widgets need to override when supporting event broadcasting.
+     */
+    public void broadcastEvent(WidgetEvent event) {
+        if (event instanceof CreateEvent) {
+            ((AbstractWidgetDefinition)getDefinition()).fireCreateEvent((CreateEvent)event);
+        } else {
+            throw new UnsupportedOperationException("Widget " + this.getRequestParameterName() + " doesn't handle events.");
+        }
+    }
+
+    /**
+     * Add a validator to this widget instance.
+     *
+     * @param validator
+     */
+    public void addValidator(WidgetValidator validator) {
+        if (this.validators == null) {
+            this.validators = new ArrayList();
+        }
+        this.validators.add(validator);
+    }
+
+    /**
+     * Remove a validator from this widget instance
+     *
+     * @param validator
+     * @return <code>true</code> if the validator was found.
+     */
+    public boolean removeValidator(WidgetValidator validator) {
+        if (this.validators != null) {
+            return this.validators.remove(validator);
+        }
+        return false;
+    }
+
+    /**
+     * @see org.apache.cocoon.forms.formmodel.Widget#validate()
+     */
+    public boolean validate() {
+        // Consider widget valid if it is not validating values.
+        if (!getCombinedState().isValidatingValues()) {
+            this.wasValid = true;
+            return true;
+        }
+
+        // Test validators from the widget definition
+        if (!getDefinition().validate(this)) {
+            // Failed
+            this.wasValid = false;
+            return false;
+        }
+        // Definition successful, test local validators
+        if (this.validators != null) {
+            Iterator iter = this.validators.iterator();
+            while(iter.hasNext()) {
+                WidgetValidator validator = (WidgetValidator)iter.next();
+                if (!validator.validate(this)) {
+                    this.wasValid = false;
+                    return false;
+                }
+            }
+        }
+
+        // Successful validation
+        
+        if (this instanceof ValidationErrorAware) {
+            // Clear validation error if any
+            ((ValidationErrorAware)this).setValidationError(null);
+        }
+
+        this.wasValid = true;
+        return true;
+    }
+
+    /**
+     * @see org.apache.cocoon.forms.formmodel.Widget#isValid()
+     */
+    public boolean isValid() {
+        return this.wasValid;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Delegates to the {@link #getDefinition()} to generate the 'label' part of
+     * the display-data of this widget.
+     *
+     * Subclasses should override if the getDefinition can return <code>null</code>
+     * to avoid NPE's
+     *
+     * @param contentHandler
+     * @throws SAXException
+     */
+    public void generateLabel(ContentHandler contentHandler) throws SAXException {
+        if (getCombinedState().isDisplayingValues()) {
+            getDefinition().generateDisplayData("label", contentHandler);
+        }
+    }
+
+    /**
+     * Generates nested additional content nested inside the main element for this
+     * widget which is generated by {@link #generateSaxFragment(ContentHandler, Locale)}
+     *
+     * The implementation on the AbstractWidget level inserts no additional XML.
+     * Subclasses need to override to insert widget specific content.
+     *
+     * @param contentHandler to send the SAX events to
+     * @param locale in which context potential content needs to be put.
+     * @throws SAXException
+     */
+    protected void generateItemSaxFragment(ContentHandler contentHandler, Locale locale) throws SAXException {
+        // Do nothing
+    }
+
+    /**
+     * The XML element name used in {@link #generateSaxFragment(ContentHandler, Locale)}
+     * to produce the wrapping element for all the XML-instance-content of this Widget.
+     *
+     * @return the main elementname for this widget's sax-fragment.
+     */
+    protected abstract String getXMLElementName();
+
+    /**
+     * The XML attributes used in {@link #generateSaxFragment(ContentHandler, Locale)}
+     * to be placed on the wrapping element for all the XML-instance-content of this Widget.
+     *
+     * This automatically adds @id={@link #getRequestParameterName()} to that element.
+     * Concrete subclasses should call super.getXMLElementAttributes and possibly
+     * add additional attributes.
+     *
+     * Note: the @id is not added for those widgets who's getId() returns <code>null</code>
+     * (e.g. top-level container widgets like 'form').  The contract of returning a non-null
+     * {@link AttributesImpl} is however maintained.
+     *
+     * @return the attributes for the main element for this widget's sax-fragment.
+     */
+    protected AttributesImpl getXMLElementAttributes() {
+        AttributesImpl attrs = new AttributesImpl();
+        // top-level widget-containers like forms will have their id set to ""
+        // for those the @id should not be included.
+        if (getId().length() != 0) {
+        	attrs.addCDATAAttribute("id", getRequestParameterName());
+        }
+
+        // Add the "state" attribute if different from active (the default state)
+        WidgetState state = getCombinedState();
+        if (state != WidgetState.ACTIVE) {
+            attrs.addCDATAAttribute("state", getCombinedState().getName());
+        }
+        
+        // Add the "listening" attribute is the value has change listeners
+        if (this instanceof ValueChangedListenerEnabled &&
+            ((ValueChangedListenerEnabled)this).hasValueChangedListeners()) {
+            attrs.addCDATAAttribute("listening", "true");
+        }
+        return attrs;
+    }
+
+    /**
+     * Delegates to the {@link #getDefinition()} of this widget to generate a common
+     * set of 'display' data. (i.e. help, label, hint,...)
+     *
+     * Subclasses should override if the getDefinition can return <code>null</code>
+     * to avoid NPE's.
+     *
+     * @param contentHandler where to send the SAX events to.
+     * @throws SAXException
+     *
+     * @see WidgetDefinition#generateDisplayData(ContentHandler)
+     */
+    protected void generateDisplayData(ContentHandler contentHandler) throws SAXException {
+        getDefinition().generateDisplayData(contentHandler);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This will generate some standard XML consisting of a simple wrapper
+     * element (name provided by {@link #getXMLElementName()}) with attributes
+     * (provided by {@link #getXMLElementAttributes()} around anything injected
+     * in by both {@link #generateDisplayData(ContentHandler)} and
+     * {@link #generateItemSaxFragment(ContentHandler, Locale)}.
+     *
+     * <pre>
+     * &lt;fi:{@link #getXMLElementName()} {@link #getXMLElementAttributes()} &gt;
+     *   {@link #generateDisplayData(ContentHandler)} (i.e. help, label, ...)
+     *
+     *   {@link #generateItemSaxFragment(ContentHandler, Locale)}
+     * &lt;/fi:{@link #getXMLElementName()} &gt;
+     * </pre>
+     *
+     * @param contentHandler to send the SAX events to
+     * @param locale in which context potential content needs to be put.
+     * @throws SAXException
+     */
+    public void generateSaxFragment(ContentHandler contentHandler, Locale locale)
+    throws SAXException {
+
+        if (getCombinedState().isDisplayingValues()) {
+            // FIXME: we may want to strip out completely widgets that aren't updated when in AJAX mode
+            String element = this.getXMLElementName();
+            AttributesImpl attrs = getXMLElementAttributes();
+            contentHandler.startElement(FormsConstants.INSTANCE_NS, element, FormsConstants.INSTANCE_PREFIX_COLON + element, attrs);
+
+            generateDisplayData(contentHandler);
+
+            if (locale == null) {
+                locale = getForm().getLocale();
+            }
+
+            generateItemSaxFragment(contentHandler, locale);
+
+            contentHandler.endElement(FormsConstants.INSTANCE_NS, element, FormsConstants.INSTANCE_PREFIX_COLON + element);
+
+        } else {
+            // Generate a placeholder that can be used later by AJAX updates
+            AttributesImpl attrs = new AttributesImpl();
+            attrs.addCDATAAttribute("id", getRequestParameterName());
+            contentHandler.startElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder", attrs);
+            contentHandler.endElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder");
+        }
+    }
+
+	public Object getAttribute(String name) {
+        Object result = null;
+
+        // First check locally
+        if (this.attributes != null) {
+            result = this.attributes.get(name);
+        }
+
+        // Fall back to the definition's attributes
+        if (result == null) {
+            result = getDefinition().getAttribute(name);
+        }
+
+        return result;
+    }
+
+    public void setAttribute(String name, Object value) {
+        if (this.attributes == null) {
+            this.attributes = new HashMap();
+        }
+        this.attributes.put(name, value);
+    }
+
+    public void removeAttribute(String name) {
+        if (this.attributes != null) {
+            this.attributes.remove(name);
+        }
+    }
+
+    public String toString() {
+        String className = this.getClass().getName();
+        int last = className.lastIndexOf('.');
+        if (last != -1) {
+            className = className.substring(last+1);
+        }
+        
+        String name = getRequestParameterName();
+        return name.length() == 0 ? className : className + " '" + getRequestParameterName() + "'";
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidget.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidgetDefinition.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidgetDefinition.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidgetDefinition.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidgetDefinition.java Thu Nov  3 05:41:06 2005
@@ -0,0 +1,323 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * 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.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.cocoon.forms.FormsConstants;
+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.validation.WidgetValidator;
+import org.apache.cocoon.util.location.Location;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.excalibur.xml.sax.XMLizable;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Provides functionality that is common across many WidgetDefinition implementations.
+ * 
+ * @version $Id: AbstractWidgetDefinition.java 326838 2005-10-20 06:26:53Z sylvain $
+ */
+public abstract class AbstractWidgetDefinition implements WidgetDefinition {
+    private FormDefinition formDefinition;
+    protected WidgetDefinition parent;
+
+    //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 {
+                this.formDefinition = this.parent.getFormDefinition();
+            }
+        }
+        return this.formDefinition;
+    }
+    
+    /**
+     * initialize this definition with the other, sort of like a copy constructor
+     */
+    public void initializeFrom(WidgetDefinition definition) throws Exception {
+    	if(definition instanceof AbstractWidgetDefinition) {
+    		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();
+    	    	copyMap(attributes,other.attributes);
+    		}
+    		if(other.displayData!=null) {
+    			if(displayData==null)
+    				displayData = new HashMap();
+    	    	copyMap(displayData,other.displayData);
+    		}
+    	} else {
+    		throw new Exception("Definition to inherit from is not of the right type! (at "+getLocation()+")");
+    	}
+    }
+    
+    /**
+     * helper to copy a map
+     * 
+     * @param dest destination map
+     * @param src source map
+     */
+    protected void copyMap(Map dest, Map src) {
+    	dest.clear();
+    	Iterator it = src.entrySet().iterator();
+    	while(it.hasNext()) {
+    		Map.Entry entry = (Map.Entry)it.next();
+    		dest.put(entry.getKey(),entry.getValue());
+    	}
+    }
+    
+    /**
+     * 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 found without an ID! "+this,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;
+    }
+
+    protected void setId(String id) {
+        checkMutable();
+        this.id = id;
+    }
+
+    protected void setAttributes(Map attributes) {
+        checkMutable();
+        //this.attributes = attributes;
+        if(this.attributes==null) {
+        	this.attributes = attributes;
+        	return;
+        }
+        if(attributes==null)
+        	return;
+        
+        // merge attribute lists
+        Iterator entries = attributes.entrySet().iterator();
+        while(entries.hasNext()) {
+        	Map.Entry entry = (Map.Entry)entries.next();
+        	Object key = entry.getKey();
+        	Object value = entry.getValue();
+        	this.attributes.put(key,value);
+        }
+    }
+    
+    public Object getAttribute(String name) {
+        if (this.attributes != null) {
+            return this.attributes.get(name);
+        }
+        return null;
+    }
+
+    protected 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();
+        //this.displayData = displayData;
+        
+        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;
+    }
+}

Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-forms-block/impl/src/main/java/org/apache/cocoon/forms/formmodel/AbstractWidgetDefinition.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message