org.richfaces.component.UIOrderingBaseComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.richfaces.component.UIOrderingBaseComponent.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */
package org.richfaces.component;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.EvaluationException;
import javax.faces.el.MethodBinding;
import javax.faces.model.ArrayDataModel;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

import org.ajax4jsf.component.UIDataAdaptor;
import org.ajax4jsf.model.DataComponentState;
import org.ajax4jsf.model.RepeatState;
import org.ajax4jsf.util.ELUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.iterators.EmptyIterator;
import org.apache.commons.collections.iterators.FilterIterator;
import org.richfaces.component.util.MessageUtil;

/**
 * @author Nick Belaevski
 *         mailto:nbelaevski@exadel.com
 *         created 16.11.2007
 * @since 3.2
 */
public abstract class UIOrderingBaseComponent extends UIDataAdaptor implements EditableValueHolder {

    private boolean localValueSet;

    private List validators = null;
    private MethodBinding validator;

    public static final Predicate isColumn = new Predicate() {
        public boolean evaluate(Object input) {
            return (input instanceof javax.faces.component.UIColumn || input instanceof Column)
                    && ((UIComponent) input).isRendered();
        }
    };

    protected boolean isSuitableValue(Object value, Object restoredObject) {
        if (value instanceof Object[]) {
            Object[] objects = (Object[]) value;
            for (int i = 0; i < objects.length; i++) {
                Object object = objects[i];

                if (object != null && object.equals(restoredObject)) {
                    return true;
                }
            }

            return false;
        } else {
            if (value != null) {
                return ((Collection) value).contains(restoredObject);
            } else {
                return false;
            }
        }
    }

    public abstract boolean isOrderControlsVisible();

    public abstract void setOrderControlsVisible(boolean visible);

    public abstract boolean isFastOrderControlsVisible();

    public abstract void setFastOrderControlsVisible(boolean visible);

    public abstract String getRequiredMessage();

    public abstract void setRequiredMessage(String requiredMessage);

    public Object saveState(FacesContext faces) {
        Object[] state = new Object[4];

        state[0] = super.saveState(faces);

        state[1] = saveAttachedState(faces, validators);
        state[2] = saveAttachedState(faces, validator);

        state[3] = localValueSet ? Boolean.TRUE : Boolean.FALSE;

        return state;
    }

    public void restoreState(FacesContext faces, Object object) {
        Object[] state = (Object[]) object;

        super.restoreState(faces, state[0]);

        validators = (List) restoreAttachedState(faces, state[1]);
        validator = (MethodBinding) restoreAttachedState(faces, state[2]);

        localValueSet = ((Boolean) state[3]).booleanValue();
    }

    protected DataComponentState createComponentState() {
        return new RepeatState();
    }

    public Iterator columns() {
        return new FilterIterator(getChildren().iterator(), isColumn);
    }

    protected Iterator dataChildren() {
        if (getChildCount() != 0) {
            return columns();
        } else {
            return EmptyIterator.INSTANCE;
        }
    }

    protected Iterator fixedChildren() {
        Map facets = getFacets();
        if (facets != null) {
            return facets.values().iterator();
        } else {
            return EmptyIterator.INSTANCE;
        }
    }

    //validators
    public MethodBinding getValidator() {
        return validator;
    }

    public void setValidator(MethodBinding validatorBinding) {
        this.validator = validatorBinding;
    }

    public Validator[] getValidators() {

        if (validators == null) {
            return new Validator[0];
        } else {
            return (Validator[]) validators.toArray(new Validator[validators.size()]);
        }
    }

    public void addValidator(Validator validator) {
        if (validator == null) {
            throw new NullPointerException();
        }

        if (validators == null) {
            validators = new ArrayList();
        }

        validators.add(validator);
    }

    public void removeValidator(Validator validator) {
        if (validators != null) {
            validators.remove(validator);
        }
    }
    //validators end

    public boolean isLocalValueSet() {
        return localValueSet;
    }

    public void setLocalValueSet(boolean localValueSet) {
        this.localValueSet = localValueSet;
    }

    protected DataModel createDataModel(Object value) {
        DataModel dataModel;

        if (value == null) {
            dataModel = new ListDataModel(Collections.EMPTY_LIST);
        } else if (value instanceof List) {
            dataModel = new ListDataModel((List) value);
        } else if (Object[].class.isAssignableFrom(value.getClass())) {
            dataModel = new ArrayDataModel((Object[]) value);
        } else {
            throw new IllegalArgumentException();
        }

        return dataModel;
    }

    /**
     * @exception NullPointerException {@inheritDoc}     
     */
    public void decode(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // Force validity back to "true"
        setValid(true);
        super.decode(context);
    }

    /**
     * <p>Specialized decode behavior on top of that provided by the
     * superclass.  In addition to the standard
     * <code>processDecodes</code> behavior inherited from {@link
     * UIComponentBase}, calls <code>validate()</code> if the the
     * <code>immediate</code> property is true; if the component is
     * invalid afterwards or a <code>RuntimeException</code> is thrown,
     * calls {@link FacesContext#renderResponse}.  </p>
     * @exception NullPointerException {@inheritDoc}     
     */
    public void processDecodes(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // Skip processing if our rendered flag is false
        if (!isRendered()) {
            return;
        }

        super.processDecodes(context);

        if (isImmediate()) {
            executeValidate(context);
        }
    }

    /**
     * Executes validation logic.
     */
    protected void executeValidate(FacesContext context) {
        try {
            validate(context);
        } catch (RuntimeException e) {
            context.renderResponse();
            throw e;
        }

        if (!isValid()) {
            context.renderResponse();
        }
    }

    public abstract void validate(FacesContext context);

    /**
     * <p>In addition to the standard <code>processValidators</code> behavior
     * inherited from {@link UIComponentBase}, calls <code>validate()</code>
     * if the <code>immediate</code> property is false (which is the 
     * default);  if the component is invalid afterwards, calls
     * {@link FacesContext#renderResponse}.
     * If a <code>RuntimeException</code> is thrown during
     * validation processing, calls {@link FacesContext#renderResponse}
     * and re-throw the exception.
     * </p>
     * @exception NullPointerException {@inheritDoc}    
     */
    public void processValidators(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // Skip processing if our rendered flag is false
        if (!isRendered()) {
            return;
        }

        super.processValidators(context);
        if (!isImmediate()) {
            executeValidate(context);
        }
    }

    protected void requiredInvalidate(FacesContext context) {
        String requiredMessage = getRequiredMessage();
        FacesMessage message = null;

        if (requiredMessage != null) {
            message = new FacesMessage(FacesMessage.SEVERITY_ERROR, requiredMessage, requiredMessage);
        } else {
            message = MessageUtil.getMessage(context, UIInput.REQUIRED_MESSAGE_ID,
                    new Object[] { MessageUtil.getLabel(context, this) });
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
        }

        context.addMessage(getClientId(context), message);
        setValid(false);
    }

    protected void validateValue(FacesContext context, Object newValue) {
        Validator[] validators = getValidators();
        for (int i = 0; i < validators.length; i++) {
            Validator validator = (Validator) validators[i];
            try {
                validator.validate(context, this, newValue);
            } catch (ValidatorException ve) {
                // If the validator throws an exception, we're
                // invalid, and we need to add a message
                setValid(false);
                FacesMessage message = ve.getFacesMessage();
                if (message != null) {
                    message.setSeverity(FacesMessage.SEVERITY_ERROR);
                    context.addMessage(getClientId(context), message);
                }
            }
        }

        MethodBinding validator = getValidator();
        if (validator != null) {
            try {
                validator.invoke(context, new Object[] { context, this, newValue });
            } catch (EvaluationException ee) {
                if (ee.getCause() instanceof ValidatorException) {
                    ValidatorException ve = (ValidatorException) ee.getCause();

                    // If the validator throws an exception, we're
                    // invalid, and we need to add a message
                    setValid(false);
                    FacesMessage message = ve.getFacesMessage();
                    if (message != null) {
                        message.setSeverity(FacesMessage.SEVERITY_ERROR);
                        context.addMessage(getClientId(context), message);
                    }
                } else {
                    // Otherwise, rethrow the EvaluationException
                    throw ee;
                }
            }
        }
    }

    /**
     * <p>In addition to the standard <code>processUpdates</code> behavior
     * inherited from {@link UIComponentBase}, calls
     * <code>updateModel()</code>.
     * If the component is invalid afterwards, calls
     * {@link FacesContext#renderResponse}.
     * If a <code>RuntimeException</code> is thrown during
     * update processing, calls {@link FacesContext#renderResponse}
     * and re-throw the exception.
     * </p>
     * @exception NullPointerException {@inheritDoc}     
     */
    public void processUpdates(FacesContext context) {

        if (context == null) {
            throw new NullPointerException();
        }

        // Skip processing if our rendered flag is false
        if (!isRendered()) {
            return;
        }

        super.processUpdates(context);

        try {
            updateModel(context);
        } catch (RuntimeException e) {
            context.renderResponse();
            throw e;
        }

        if (!isValid()) {
            context.renderResponse();
        }
    }

    public abstract void updateModel(FacesContext context);

    protected interface UpdateModelCommand {
        public void execute(FacesContext context);
    }

    protected interface DataAdder {
        Object getContainer();

        void add(Object object);
    }

    protected void updateModel(FacesContext context, UpdateModelCommand command) {
        try {
            command.execute(context);
        } catch (EvaluationException e) {
            String messageStr = e.getMessage();
            FacesMessage message = null;
            if (null == messageStr) {
                message = MessageUtil.getMessage(context, UIInput.UPDATE_MESSAGE_ID,
                        new Object[] { MessageUtil.getLabel(context, this) });
            } else {
                message = new FacesMessage(messageStr);
            }
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            context.addMessage(getClientId(context), message);
            setValid(false);
        } catch (FacesException e) {
            FacesMessage message = MessageUtil.getMessage(context, UIInput.UPDATE_MESSAGE_ID,
                    new Object[] { MessageUtil.getLabel(context, this) });
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            context.addMessage(getClientId(context), message);
            setValid(false);
        } catch (IllegalArgumentException e) {
            FacesMessage message = MessageUtil.getMessage(context, UIInput.UPDATE_MESSAGE_ID,
                    new Object[] { MessageUtil.getLabel(context, this) });
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            context.addMessage(getClientId(context), message);
            setValid(false);
        } catch (Exception e) {
            FacesMessage message = MessageUtil.getMessage(context, UIInput.UPDATE_MESSAGE_ID,
                    new Object[] { MessageUtil.getLabel(context, this) });
            message.setSeverity(FacesMessage.SEVERITY_ERROR);
            context.addMessage(getClientId(context), message);
            setValid(false);
        }
    }

    /**
     * <p>Return <code>true</code> if the new value is different from the
     * previous value.</p>
     *
     * @param previous old value of this component (if any)
     * @param value new value of this component (if any)
     */
    protected boolean compareValues(Object previous, Object value) {

        if (previous == null) {
            return (value != null);
        } else if (value == null) {
            return (true);
        } else {
            if (previous instanceof Object[]) {
                return !Arrays.equals((Object[]) previous, (Object[]) value);
            } else {
                return (!(previous.equals(value)));
            }
        }

    }

    protected void addConversionErrorMessage(FacesContext context, ConverterException ce, Object value) {
        FacesMessage message = ce.getFacesMessage();
        if (message == null) {
            message = MessageUtil.getMessage(context, UIInput.CONVERSION_MESSAGE_ID,
                    new Object[] { MessageUtil.getLabel(context, this) });
            if (message.getDetail() == null) {
                message.setDetail(ce.getMessage());
            }
        }

        message.setSeverity(FacesMessage.SEVERITY_ERROR);
        context.addMessage(getClientId(context), message);
    }

    protected boolean isEmpty(Object value) {

        if (value == null) {
            return (true);
        } else if ((value instanceof String) && (((String) value).length() < 1)) {
            return (true);
        } else if (value.getClass().isArray()) {
            if (0 == java.lang.reflect.Array.getLength(value)) {
                return (true);
            }
        } else if (value instanceof List) {
            if (0 == ((List) value).size()) {
                return (true);
            }
        }
        return (false);
    }

    protected Object createContainer(ArrayList data, Object object) {
        if (object != null) {
            Class objectClass = object.getClass();
            Class componentType = objectClass.getComponentType();
            if (componentType != null) {
                return data.toArray((Object[]) Array.newInstance(componentType, data.size()));
            }
        }

        data.trimToSize();
        return data;
    }

    public abstract ItemState getItemState();

    public interface ItemState {
        public boolean isSelected();

        public boolean isActive();
    }

    private Converter getConverterForType(FacesContext context, Class type) {
        if (!Object.class.equals(type) && type != null) {
            Application application = context.getApplication();
            return application.createConverter(type);
        }

        return null;
    }

    private static final Converter noOpConverter = new Converter() {
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
            return value;
        }

        public String getAsString(FacesContext context, UIComponent component, Object value) {
            return (String) value;
        }
    };

    public Converter getConverterForValue(FacesContext context) {
        Converter converter = null;
        ValueExpression expression = this.getValueExpression("value");

        if (expression != null) {
            Class<?> containerClass = ELUtils.getContainerClass(context, expression);

            converter = getConverterForType(context, containerClass);
            if (converter == null && String.class.equals(containerClass)) {
                converter = noOpConverter;
            }
        }

        return converter;
    }
}