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 }