001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.pipeline.validation;
019    
020    import org.apache.commons.pipeline.Stage;
021    
022    /**
023     * A collection of utility methods used by the validation system.
024     *
025     */
026    public class ValidationUtils {
027        
028        /** Prevent instantiation */
029        private ValidationUtils() {  }
030        
031        /**
032         * Tests whether the specified downstream stage can succeed the specified
033         * upstream stage.
034         * @return true or false on definitive identification of compatibility or
035         * null if unable to determine compatibility due to missing metadata.
036         * @param upstream the upstream stage
037         * @param downstream the stage consuming data produced by the upstream stage
038         */
039        public static final Boolean canSucceed(Stage upstream, Stage downstream) {
040            if (upstream.getClass().isAnnotationPresent(ProducedTypes.class) &&
041                    downstream.getClass().isAnnotationPresent(ConsumedTypes.class)) {
042                ProducedTypes p = upstream.getClass().getAnnotation(ProducedTypes.class);
043                ConsumedTypes c = downstream.getClass().getAnnotation(ConsumedTypes.class);
044                return compatible(p.value(), c.value());
045            }
046            
047            return null;
048        }
049        
050        /**
051         * Tests whether the specified downstream stage can succeed the specified
052         * upstream stage on a branch pipeline identified by the given branch key.
053         * @return true or false on definitive identification of compatibility or
054         * null if unable to determine compatibility due to missing metadata.
055         * @param upstream the upstream stage
056         * @param downstreamBranchKey the key identifying the branch receiving data from the upstream stage
057         * @param downstream the stage consuming data produced by the upstream stage
058         */
059        public static final Boolean canSucceedOnBranch(Stage upstream, String downstreamBranchKey, Stage downstream) {
060            if (downstream.getClass().isAnnotationPresent(ConsumedTypes.class)) {
061                ConsumedTypes c = downstream.getClass().getAnnotation(ConsumedTypes.class);
062                if (upstream.getClass().isAnnotationPresent(ProductionOnBranch.class)) {
063                    ProductionOnBranch pob = upstream.getClass().getAnnotation(ProductionOnBranch.class);
064                    
065                    if (!downstreamBranchKey.equals(pob.branchKey())) {
066                        return false;
067                    } else {
068                        return compatible(pob.producedTypes(), c.value());
069                    }
070                } else if (upstream.getClass().isAnnotationPresent(Branches.class)) {
071                    Branches branches = upstream.getClass().getAnnotation(Branches.class);
072                    for (ProductionOnBranch pob : branches.productionOnBranches()) {
073                        if (downstreamBranchKey.equals(pob.branchKey())) {
074                            return compatible(pob.producedTypes(), c.value());
075                        }
076                    }
077                    
078                    return false;
079                }
080            }
081            
082            return null;
083        }
084        
085        /**
086         * Check if the specified production is compatible with the specified consumption.
087         */
088        private static Boolean compatible(Class<?>[] producedTypes, Class<?>[] consumedTypes) {
089            for (Class<?> consumed : consumedTypes) {
090                for (Class<?> produced : producedTypes) { //usually just one type
091                    if (consumed.isAssignableFrom(produced)) return true;
092                }
093            }
094            
095            //none of what is produced can be consumed
096            return false;
097        }    
098    }