/*
 * Copyright 2002-2006,2009 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 com.opensymphony.xwork2.validator.validators;

import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.validator.DelegatingValidatorContext;
import com.opensymphony.xwork2.validator.ShortCircuitableValidator;
import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.Validator;
import com.opensymphony.xwork2.validator.ValidatorContext;
import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.List;


/**
 * Abstract implementation of the Validator interface suitable for subclassing.
 *
 * @author Jason Carreira
 * @author tm_jee
 * @author Martin Gilday
 */
public abstract class ValidatorSupport implements Validator, ShortCircuitableValidator {

    protected final Logger log = LoggerFactory.getLogger(this.getClass());
    protected String defaultMessage = "";
    protected String messageKey;
    private ValidatorContext validatorContext;
    private boolean shortCircuit;
    private boolean parse;
    private String type;
    private String[] messageParameters;
    private ValueStack stack;


    public void setValueStack(ValueStack stack) {
        this.stack = stack;
    }

    public void setDefaultMessage(String message) {
        if (StringUtils.isNotEmpty(message)) {
            this.defaultMessage = message;
        }
    }

    public String getDefaultMessage() {
        return defaultMessage;
    }

    public void setParse(boolean parse) {
        this.parse = parse;
    }

    public boolean getParse() {
        return parse;
    }

    public String getMessage(Object object) {
        String message;
        boolean pop = false;

        if (!stack.getRoot().contains(object)) {
            stack.push(object);
            pop = true;
        }

        stack.push(this);

        if (messageKey != null) {
            if ((defaultMessage == null) || ("".equals(defaultMessage.trim()))) {
                defaultMessage = messageKey;
            }
            if (validatorContext == null) {
                validatorContext = new DelegatingValidatorContext(object);
            }
            List<Object> parsedMessageParameters = null;
            if (messageParameters != null) {
                parsedMessageParameters = new ArrayList<Object>();
                for (String messageParameter : messageParameters) {
                    if (messageParameter != null) {
                        try {
                            Object val = stack.findValue(messageParameter);
                            parsedMessageParameters.add(val);
                        } catch (Exception e) {
                            // if there's an exception in parsing, we'll just treat the expression itself as the
                            // parameter
                            log.warn("exception while parsing message parameter [" + messageParameter + "]", e);
                            parsedMessageParameters.add(messageParameter);
                        }
                    }
                }
            }

            message = validatorContext.getText(messageKey, defaultMessage, parsedMessageParameters);

        } else {
            message = defaultMessage;
        }

        if (StringUtils.isNotBlank(message))
            message = TextParseUtil.translateVariables(message, stack);

        stack.pop();

        if (pop) {
            stack.pop();
        }

        return message;
    }

    public void setMessageKey(String key) {
        messageKey = key;
    }

    public String getMessageKey() {
        return messageKey;
    }

    public String[] getMessageParameters() {
        return this.messageParameters;
    }

    public void setMessageParameters(String[] messageParameters) {
        this.messageParameters = messageParameters;
    }

    public void setShortCircuit(boolean shortcircuit) {
        shortCircuit = shortcircuit;
    }

    public boolean isShortCircuit() {
        return shortCircuit;
    }

    public void setValidatorContext(ValidatorContext validatorContext) {
        this.validatorContext = validatorContext;
    }

    public ValidatorContext getValidatorContext() {
        return validatorContext;
    }

    public void setValidatorType(String type) {
        this.type = type;
    }

    public String getValidatorType() {
        return type;
    }

    /**
     * Parse <code>expression</code> passed in against value stack. Only parse
     * when 'parse' param is set to true, else just returns the expression unparsed.
     *
     * @param expression
     * @return Object
     */
    protected Object conditionalParse(String expression) {
        if (parse) {
            return TextParseUtil.translateVariables('$', expression, stack);
        }
        return expression;
    }

    /**
     * Return the field value named <code>name</code> from <code>object</code>,
     * <code>object</code> should have the appropriate getter/setter.
     *
     * @param name
     * @param object
     * @return Object as field value
     * @throws ValidationException
     */
    protected Object getFieldValue(String name, Object object) throws ValidationException {

        boolean pop = false;

        if (!stack.getRoot().contains(object)) {
            stack.push(object);
            pop = true;
        }

        Object retVal = stack.findValue(name);

        if (pop) {
            stack.pop();
        }

        return retVal;
    }

    protected void addActionError(Object object) {
        validatorContext.addActionError(getMessage(object));
    }

    protected void addFieldError(String propertyName, Object object) {
        validatorContext.addFieldError(propertyName, getMessage(object));
    }

}
