# Specifications.java

    /*
     * Copyright (c) 2010, Rickard Öberg. All Rights Reserved.
     *
     * 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.qi4j.functional;

    /**
     * Common generic specification expressions
     */
    public class Specifications
    {
        public static <T> Specification<T> TRUE()
        {
            return new Specification<T>()
            {
                @Override
                public boolean satisfiedBy( T instance )
                {
                    return true;
                }
            };
        }

        public static <T> Specification<T> not( final Specification<T> specification )
        {
            return new Specification<T>()
            {
                @Override
                public boolean satisfiedBy( T instance )
                {
                    return !specification.satisfiedBy( instance );
                }
            };
        }

        @SafeVarargs
        public static <T> AndSpecification<T> and( final Specification<T>... specifications )
        {
            return and( Iterables.iterable( specifications ) );
        }

        public static <T> AndSpecification<T> and( final Iterable<Specification<T>> specifications )
        {
            return new AndSpecification<>( specifications );
        }

        @SafeVarargs
        public static <T> OrSpecification<T> or( final Specification<T>... specifications )
        {
            return or( Iterables.iterable( specifications ) );
        }

        public static <T> OrSpecification<T> or( final Iterable<Specification<T>> specifications )
        {
            return new OrSpecification<>( specifications );
        }

        @SafeVarargs
        public static <T> Specification<T> in( final T... allowed )
        {
            return in( Iterables.iterable( allowed ) );
        }

        public static <T> Specification<T> in( final Iterable<T> allowed )
        {
            return new Specification<T>()
            {
                @Override
                public boolean satisfiedBy( T item )
                {
                    for( T allow : allowed )
                    {
                        if( allow.equals( item ) )
                        {
                            return true;
                        }
                    }
                    return false;
                }
            };
        }

        public static <T> Specification<T> notNull()
        {
            return new Specification<T>()
            {
                @Override
                public boolean satisfiedBy( T item )
                {
                    return item != null;
                }
            };
        }

        public static <FROM, TO> Specification<FROM> translate( final Function<FROM, TO> function,
                                                                final Specification<? super TO> specification
        )
        {
            return new Specification<FROM>()
            {
                @Override
                public boolean satisfiedBy( FROM item )
                {
                    return specification.satisfiedBy( function.map( item ) );
                }
            };
        }

        /**
         * AND Specification.
         */
        public static class AndSpecification<T>
            implements Specification<T>
        {
            private final Iterable<Specification<T>> specifications;

            private AndSpecification( Iterable<Specification<T>> specifications )
            {
                this.specifications = specifications;
            }

            @Override
            public boolean satisfiedBy( T instance )
            {
                for( Specification<T> specification : specifications )
                {
                    if( !specification.satisfiedBy( instance ) )
                    {
                        return false;
                    }
                }

                return true;
            }

            @SafeVarargs
            public final AndSpecification<T> and( Specification<T>... specifications )
            {
                Iterable<Specification<T>> iterable = Iterables.iterable( specifications );
                Iterable<Specification<T>> flatten = Iterables.flatten( this.specifications, iterable );
                return Specifications.and( flatten );
            }

            @SafeVarargs
            public final OrSpecification<T> or( Specification<T>... specifications )
            {
                return Specifications.or( Iterables.prepend( this, Iterables.iterable( specifications ) ) );
            }
        }

        /**
         * OR Specification.
         */
        public static class OrSpecification<T>
            implements Specification<T>
        {
            private final Iterable<Specification<T>> specifications;

            private OrSpecification( Iterable<Specification<T>> specifications )
            {
                this.specifications = specifications;
            }

            @Override
            public boolean satisfiedBy( T instance )
            {
                for( Specification<T> specification : specifications )
                {
                    if( specification.satisfiedBy( instance ) )
                    {
                        return true;
                    }
                }

                return false;
            }

            @SafeVarargs
            public final AndSpecification<T> and( Specification<T>... specifications )
            {
                return Specifications.and( Iterables.prepend( this, Iterables.iterable( specifications ) ) );
            }

            @SafeVarargs
            public final OrSpecification<T> or( Specification<T>... specifications )
            {
                Iterable<Specification<T>> iterable = Iterables.iterable( specifications );
                Iterable<Specification<T>> flatten = Iterables.flatten( this.specifications, iterable );
                return Specifications.or( flatten );
            }
        }

        private Specifications()
        {
        }

    }