/*
 * 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.openjpa.validation;

import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.event.LifecycleEvent;
import org.apache.openjpa.event.LifecycleEventManager;
import org.apache.openjpa.lib.conf.Configurable;
import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.meta.ClassMetaData;

/**
 * An extension of LifecycleEventManager which adds validation capabilities for
 * specific lifecycle events.  Validation occurs after firing all lifecycle
 * events and callbacks.
 *
 */
public class ValidatingLifecycleEventManager extends LifecycleEventManager
    implements Configurable {

    private static final long serialVersionUID = 1L;
    private OpenJPAConfiguration _conf = null;
    private Validator _validator = null;
    protected boolean _validationEnabled = true;

    /**
     * Constructor which accepts a reference to the validator to use.  If null,
     * no validation will occur.
     */
    public ValidatingLifecycleEventManager() {
        super();
    }

    /* (non-Javadoc)
     * @see org.apache.openjpa.lib.conf.Configurable#endConfiguration()
     */
    @Override
    public void endConfiguration() {
        _validator = (Validator)_conf.getValidatorInstance();
    }

    /* (non-Javadoc)
     * @see org.apache.openjpa.lib.conf.Configurable#setConfiguration(
     *      org.apache.openjpa.lib.conf.Configuration)
     */
    @Override
    public void setConfiguration(Configuration conf) {
        if (conf instanceof OpenJPAConfiguration) {
            _conf = (OpenJPAConfiguration)conf;
        }
    }

    /* (non-Javadoc)
     * @see org.apache.openjpa.lib.conf.Configurable#startConfiguration()
     */
    @Override
    public void startConfiguration() {
    }

    @Override
    public boolean hasUpdateListeners(Object source, ClassMetaData meta) {
        if (_validator == null) {
            return super.hasUpdateListeners(source, meta);
        }
        return _validator.validating(source, LifecycleEvent.BEFORE_UPDATE) ||
            super.hasUpdateListeners(source, meta);
    }

    @Override
    public boolean hasPersistListeners(Object source, ClassMetaData meta) {
        if (_validator == null) {
            return super.hasPersistListeners(source, meta);
        }
        return _validator.validating(source, LifecycleEvent.BEFORE_PERSIST) ||
            super.hasPersistListeners(source, meta);
    }

    @Override
    public boolean hasDeleteListeners(Object source, ClassMetaData meta) {
        if (_validator == null) {
            return super.hasDeleteListeners(source, meta);
        }
        return _validator.validating(source, LifecycleEvent.BEFORE_DELETE) ||
            super.hasDeleteListeners(source, meta);
    }

    @Override
    public Exception[] fireEvent(Object source,
        ClassMetaData meta, int type) {

        return fireEvent(source, null, meta, type);
    }

    @Override
    public Exception[] fireEvent(Object source, Object related,
        ClassMetaData meta, int type) {

        // Fire all pre-validation events and handlers.
        Exception[] evx = super.fireEvent(source, related, meta, type);

        // If there are exceptions and the event manager is marked fail fast
        // do not validate
        if (evx != null && evx.length > 0 && isFailFast())
            return evx;

        // If a validator is provided and the source object should be validated,
        // validate it and return any exceptions
        if (_validationEnabled && _validator != null && _validator.validating(source, type)) {
            ValidationException vex = _validator.validate(source, type);
            if (vex != null) {
                if (evx == null || evx.length == 0) {
                    evx = new Exception[1];
                    evx[0] = vex;
                }
                else {
                    // resize the exception array and add the validation
                    // exception
                    Exception[] vevx = new Exception[evx.length +1];
                    System.arraycopy(vevx, 0, evx, 0, evx.length);
                    vevx[evx.length+1] = vex;
                    evx = vevx;
                }
            }
        }
        return evx;
    }

    /**
     * Whether this LifeCycleEventManager has had at least one listener or callback
     * registered.  Used for a quick test when firing events.
     * @return boolean
     */
    @Override
    public boolean isActive(ClassMetaData meta) {
        return isValidationEnabled() || super.isActive(meta);
    }

    public boolean isValidationEnabled() {
        return _validationEnabled;
    }

    public boolean setValidationEnabled(boolean enabled) {
        boolean val = _validationEnabled;
        _validationEnabled = enabled;
        return val;
    }
}
