Package org.springmodules.xt.model.specifications.composite

Source Code of org.springmodules.xt.model.specifications.composite.CompositeSpecificationImpl

/*
* Copyright 2006 the original author or authors.
*
* 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.springmodules.xt.model.specifications.composite;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.springmodules.xt.model.notification.Message;
import org.springmodules.xt.model.notification.Notification;
import org.springmodules.xt.model.specifications.Specification;
import org.springmodules.xt.model.specifications.composite.operator.BinaryOperator;
import org.springmodules.xt.model.specifications.composite.operator.OperatorFactory;
import org.springmodules.xt.model.specifications.AbstractSpecification;
import org.springmodules.xt.model.specifications.support.InverseSpecification;
import org.springmodules.xt.model.specifications.support.SpecificationGenericAdapter;
import org.springmodules.xt.model.specifications.support.SpecificationDescriptionException;
import org.springmodules.xt.model.specifications.support.SpecificationNotComposedException;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
* {@link CompositeSpecification} implementation.<br>
* You can use whatever specification class you implement, you have one only constraint: your specification method
* (i.e. "isSatisfiedBy" or "validate" or so) <b>must not</b> be overloaded.
*
* @author Sergio Bossa
*/
public class CompositeSpecificationImpl<S, O> extends AbstractSpecification<O> implements CompositeSpecification<S, O> {
   
    private Class<S> specificationClass;
    private Method specificationMethod;
   
    private OperatorFactory operatorFactory = OperatorFactory.getInstance();
    private List<Specification> specificationsList = new LinkedList<Specification>();
    private List<BinaryOperator> operatorsList = new LinkedList<BinaryOperator>();
   
    /**
     * Composite specification constructor: here you have to provide the <i>description</i> of the actual specification class
     * you want to compose.<br>
     *
     * @param specificationClass The class of the actual specification to compose.
     * @param specificationMethod The actual specification method to call for evaluation.
     */
    public CompositeSpecificationImpl(Class<S> specificationClass, String specificationMethod) {
        this.specificationClass = specificationClass;
       
        try {
            Method[] methods = this.specificationClass.getDeclaredMethods();
            for (Method m : methods) {
                if (m.getName().equals(specificationMethod)) {
                    this.specificationMethod = m;
                    break;
                }
            }
            if (this.specificationMethod == null) {
                throw new SpecificationDescriptionException("Wrong specification method: " + specificationMethod);
            }
        }
        catch(SecurityException ex) {
            throw new SpecificationDescriptionException("Wrong specification method: " + specificationMethod);
        }
    }
   
    /**
     * Start composing the specification.<br>
     * This is the first method to be called for composition.
     *
     * @param specification The actual specification to compose.
     */
    public CompositeSpecification compose(S specification) {
        this.specificationsList.clear();
        this.operatorsList.clear();
        this.specificationsList.add(this.adaptSpecification(specification));
        return this;
    }
   
    /**
     * Apply the logical <code>and</code> operator to this composite specification and the supplied one.
     * @param specification The supplied specification to compose.
     */
    public CompositeSpecification and(S specification) {
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        this.operatorsList.add(this.operatorFactory.getAndOperator());
        this.specificationsList.add(this.adaptSpecification(specification));
        return this;
    }
   
    /**
     * Apply the logical <code>or</code> operator to this composite specification and the supplied one.
     * @param specification The supplied specification to compose.
     */
    public CompositeSpecification or(S specification) {
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        this.operatorsList.add(this.operatorFactory.getOrOperator());
        this.specificationsList.add(this.adaptSpecification(specification));
        return this;
    }
   
    /**
     * Apply the logical <code>and</code> operator to this composite specification and another, supplied, composite specificaton.
     * @param specification The other composite specification.
     */
    public CompositeSpecification and(CompositeSpecification<S, O> specification) {
        if (!specification.equals(this)) {
            throw new IllegalArgumentException("You cannot compose specifications with different descriptions.");
        }
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        this.operatorsList.add(this.operatorFactory.getAndOperator());
        this.specificationsList.add(specification);
        return this;
    }
   
    /**
     * Apply the logical <code>or</code> operator to this composite specification and another, supplied, composite specificaton.
     * @param specification The other composite specification.
     */
    public CompositeSpecification or(CompositeSpecification<S, O> specification) {
        if (!specification.equals(this)) {
            throw new IllegalArgumentException("You cannot compose specifications with different descriptions.");
        }
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        this.operatorsList.add(this.operatorFactory.getOrOperator());
        this.specificationsList.add(specification);
        return this;
    }
   
    /**
     * Apply the logical, negated, <code>and</code> operator to this composite specification and the supplied one.
     * @param specification The supplied specification to compose.
     */
    public CompositeSpecification andNot(S specification) {
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        this.operatorsList.add(this.operatorFactory.getAndOperator());
        this.specificationsList.add(new InverseSpecification(this.adaptSpecification(specification)));
        return this;
    }
   
    /**
     * Apply the logical, negated, <code>or</code> operator to this composite specification and the supplied one.
     * @param specification The actual specification to compose.
     */
    public CompositeSpecification orNot(S specification) {
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        this.operatorsList.add(this.operatorFactory.getOrOperator());
        this.specificationsList.add(new InverseSpecification(this.adaptSpecification(specification)));
        return this;
    }
   
    /**
     * Apply the logical, negated, <code>and</code> operator to this composite specification and another, supplied, composite specificaton.
     * @param specification The other composite specification.
     */
    public CompositeSpecification andNot(CompositeSpecification<S, O> specification) {
        if (!specification.equals(this)) {
            throw new IllegalArgumentException("You cannot compose specifications with different descriptions.");
        }
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        this.operatorsList.add(this.operatorFactory.getAndOperator());
        this.specificationsList.add(new InverseSpecification(specification));
        return this;
    }
   
    /**
     * Apply the logical, negated, <code>or</code> operator to this composite specification and another, supplied, composite specificaton.
     * @param specification The other composite specification.
     */
    public CompositeSpecification orNot(CompositeSpecification<S, O> specification) {
        if (!specification.equals(this)) {
            throw new IllegalArgumentException("You cannot compose specifications with different descriptions.");
        }
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        this.operatorsList.add(this.operatorFactory.getOrOperator());
        this.specificationsList.add(new InverseSpecification(specification));
        return this;
    }
   
    public CompositeSpecification withMessage(Message message, boolean whenSatisfied) {
        Specification lastSpecification = this.specificationsList.get(this.specificationsList.size() - 1);
        lastSpecification.addMessage(message, whenSatisfied);
        return this;
    }
   
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (! (obj instanceof CompositeSpecificationImpl)) {
            return false;
        }
        else {
            CompositeSpecificationImpl otherSpec = (CompositeSpecificationImpl) obj;
            EqualsBuilder builder = new EqualsBuilder();
            return builder.append(this.specificationClass, otherSpec.specificationClass)
                                  .append(this.specificationMethod, otherSpec.specificationMethod)
                                  .isEquals();
        }
    }

    public int hashCode() {
        HashCodeBuilder builder = new HashCodeBuilder();
        return builder.append(this.specificationClass)
                              .append(this.specificationMethod)
                              .toHashCode();
    }
   
    protected boolean internalEvaluate(O object, Notification notification) {
        if (this.specificationsList.isEmpty()) {
            throw new SpecificationNotComposedException("You have to compose your specification first.");
        }
        Iterator<Specification> specificationsIt = this.specificationsList.iterator();
        boolean result = specificationsIt.next().evaluate(object, notification);
        for (BinaryOperator op : operatorsList) {
            result = op.evaluate(specificationsIt.next(), result, object, notification);
        }
        return result;
    }

    private Specification adaptSpecification(S specification) {
        return new SpecificationGenericAdapter(specification, this.specificationMethod);
    }
}
TOP

Related Classes of org.springmodules.xt.model.specifications.composite.CompositeSpecificationImpl

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.