Package org.apache.cocoon.forms.formmodel

Source Code of org.apache.cocoon.forms.formmodel.AggregateField

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.forms.formmodel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.excalibur.xml.sax.XMLizable;

import org.apache.cocoon.forms.FormContext;
import org.apache.cocoon.forms.FormsConstants;
import org.apache.cocoon.forms.formmodel.AggregateFieldDefinition.SplitMapping;
import org.apache.cocoon.forms.util.I18nMessage;
import org.apache.cocoon.forms.validation.ValidationError;

import org.apache.oro.text.regex.MatchResult;
import org.apache.oro.text.regex.PatternMatcher;
import org.apache.oro.text.regex.Perl5Matcher;
import org.outerj.expression.ExpressionException;

/**
* An aggregated field allows to represent one value as multiple input fields, or several values
* as one field. Hence this widget is a field and a container widget simultaneously.
*
* <p>Upon submit, it first attempts to read own value from the request, and splits over nested
* field widgets using a regular expression. If split fails, this will simply give a validation error.
* If own value was not submitted, it attempts to read values for nested field widgets, and combines
* theirs values using combine expression.
*
* <p>To validate this widget, both the validation rules of the nested widgets are
* checked, and those of the aggregated field themselves. The validation rules of the aggregated
* field can perform checks on the string as entered by the user (e.g. check its total length).
*
* <p>This field and nested fields can be of any supported type, as long as combine expression
* gives result of the correct type, and split regular expression can split string representation
* into parts which can be converted to the values of nested fields.
*
* @version $Id: AggregateField.java 462520 2006-10-10 19:39:14Z vgritsenko $
*/
public class AggregateField extends Field implements ContainerWidget {

    private static final String AGGREGATEFIELD_EL = "aggregatefield";

    /**
     * List of nested fields
     */
    private List fields = new ArrayList();

    /**
     * Map of nested fields
     */
    private Map fieldsById = new HashMap();


    public AggregateField(AggregateFieldDefinition definition) {
        super(definition);
    }

    public final AggregateFieldDefinition getAggregateFieldDefinition() {
        return (AggregateFieldDefinition)getDefinition();
    }

    public void initialize() {
        this.selectionList = getAggregateFieldDefinition().getSelectionList();

        Iterator it = this.getChildren();
        while(it.hasNext()) {
          ((Widget)it.next()).initialize();
        }
    }

    public void addChild(Widget widget) {
        if (!(widget instanceof Field)) {
            throw new IllegalArgumentException("AggregateField can only contain fields.");
        }
        addField((Field) widget);
    }

    protected void addField(Field field) {
        field.setParent(this);
        fields.add(field);
        fieldsById.put(field.getId(), field);
    }


    public boolean hasChild(String id) {
        return this.fieldsById.containsKey(id);
    }

    public Iterator getChildren() {
        return fields.iterator();
    }

    public void readFromRequest(FormContext formContext) {
        if (!getCombinedState().isAcceptingInputs()) {
            return;
        }

        String newEnteredValue = formContext.getRequest().getParameter(getRequestParameterName());
        if (newEnteredValue != null) {
            // There is one aggregated entered value. Read it and split it.
            super.readFromRequest(formContext);
            if (this.valueState == VALUE_UNPARSED) {
                setFieldsValues(enteredValue);
            }
        } else {
            // Check if there are multiple splitted values. Read them and aggregate them.
            for (Iterator i = fields.iterator(); i.hasNext();) {
                Field field = (Field)i.next();
                field.readFromRequest(formContext);
                if (field.valueState == VALUE_UNPARSED) {
                    this.valueState = VALUE_UNPARSED;
                }
            }
            if (this.valueState == VALUE_UNPARSED) {
                combineFields();
            }
        }
    }

    public void setValue(Object newValue) {
        super.setValue(newValue);
        if (this.valueState == VALUE_PARSED) {
            setFieldsValues(enteredValue);
        }
    }

    /**
     * Returns false if all fields have no value.
     */
    private boolean fieldsHaveValues() {
        for (Iterator i = fields.iterator(); i.hasNext();) {
            Field field = (Field)i.next();
            if (field.getValue() != null) {
                return true;
            }
        }
        return false;
    }

    /**
     * Splits passed value and sets values of all nested fields.
     * If split fails, resets all fields.
     */
    private void setFieldsValues(String value) {
        if (value == null) {
            resetFieldsValues();
        } else {
            PatternMatcher matcher = new Perl5Matcher();
            if (matcher.matches(value, getAggregateFieldDefinition().getSplitPattern())) {
                MatchResult matchResult = matcher.getMatch();
                Iterator iterator = getAggregateFieldDefinition().getSplitMappingsIterator();
                while (iterator.hasNext()) {
                    SplitMapping splitMapping = (SplitMapping)iterator.next();
                    String result = matchResult.group(splitMapping.getGroup());

                    // Fields can have a non-string datatype, going to the readFromRequest
                    Field field = (Field)fieldsById.get(splitMapping.getFieldId());
                    field.readFromRequest(result);
                }
            } else {
                resetFieldsValues();
            }
        }
    }

    public void combineFields() {
        try {
            Object value = getAggregateFieldDefinition().getCombineExpression().evaluate(new ExpressionContextImpl(this, true));
            super.setValue(value);
        } catch (CannotYetResolveWarning e) {
            super.setValue(null);
        } catch (ExpressionException e) {
            super.setValue(null);
        } catch (ClassCastException e) {
            super.setValue(null);
        }
    }

    /**
     * Sets values of all nested fields to null
     */
    private void resetFieldsValues() {
        for (Iterator i = fields.iterator(); i.hasNext();) {
            Field field = (Field)i.next();
            field.setValue(null);
        }
    }

    public boolean validate() {
        if (!getCombinedState().isValidatingValues()) {
            this.wasValid = true;
            return true;
        }

        if (enteredValue != null && !fieldsHaveValues()) {
            XMLizable failMessage = getAggregateFieldDefinition().getSplitFailMessage();
            if (failMessage != null) {
                validationError = new ValidationError(failMessage);
            } else {
                validationError = new ValidationError(new I18nMessage("aggregatedfield.split-failed",
                                                                      new String[] { getAggregateFieldDefinition().getSplitRegexp() },
                                                                      FormsConstants.I18N_CATALOGUE));
            }
            valueState = VALUE_DISPLAY_VALIDATION;
            this.wasValid = false;
            return false;
        }

        // Validate ALL my child fields
        boolean valid = true;
        for (Iterator i = fields.iterator(); i.hasNext();) {
            Field field = (Field)i.next();
            if (!field.validate()) {
                validationError = field.getValidationError();
                valid = false;
            }
        }
        if (!valid) {
            valueState = VALUE_DISPLAY_VALIDATION;
            this.wasValid = false;
            return false;
        }

        return super.validate();
    }

    /**
     * @return "aggregatefield"
     */
    public String getXMLElementName() {
        return AGGREGATEFIELD_EL;
    }

    public Widget getChild(String id) {
        return (Widget) fieldsById.get(id);
    }
}
TOP

Related Classes of org.apache.cocoon.forms.formmodel.AggregateField

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.