001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.reef.tang.implementation; 020 021import org.apache.reef.tang.types.Node; 022 023import java.util.*; 024 025public final class Subplan<T> extends InjectionPlan<T> { 026 final InjectionPlan<? extends T>[] alternatives; 027 final int numAlternatives; 028 final int selectedIndex; 029 030 @SafeVarargs 031 public Subplan(final Node node, final int selectedIndex, final InjectionPlan<T>... alternatives) { 032 super(node); 033 this.alternatives = alternatives; 034 if (selectedIndex < -1 || selectedIndex >= alternatives.length) { 035 throw new ArrayIndexOutOfBoundsException("Actual value of selectedIndex is " + selectedIndex 036 + ". The length of the 'alternatives' vararg is " + alternatives.length); 037 } 038 this.selectedIndex = selectedIndex; 039 if (selectedIndex != -1) { 040 this.numAlternatives = alternatives[selectedIndex].getNumAlternatives(); 041 } else { 042 int numAlternativesSum = 0; 043 for (final InjectionPlan<? extends T> a : alternatives) { 044 numAlternativesSum += a.getNumAlternatives(); 045 } 046 this.numAlternatives = numAlternativesSum; 047 } 048 } 049 050 @SafeVarargs 051 public Subplan(final Node node, final InjectionPlan<T>... alternatives) { 052 this(node, -1, alternatives); 053 } 054 055 /** 056 * Get child elements of the injection plan tree. 057 * TODO: use ArrayList internally (and maybe for input, too). 058 * 059 * @return A list of injection sub-plans. 060 */ 061 @SuppressWarnings({"unchecked", "rawtypes"}) 062 @Override 063 public Collection<InjectionPlan<?>> getChildren() { 064 return (Collection) Collections.unmodifiableCollection(Arrays.asList(this.alternatives)); 065 } 066 067 @Override 068 public int getNumAlternatives() { 069 return this.numAlternatives; 070 } 071 072 /** 073 * Even if there is only one sub-plan, it was registered as a default plan, 074 * and is therefore ambiguous. 075 */ 076 @Override 077 public boolean isAmbiguous() { 078 if (selectedIndex == -1) { 079 return true; 080 } 081 return alternatives[selectedIndex].isAmbiguous(); 082 } 083 084 @Override 085 public boolean isInjectable() { 086 if (selectedIndex == -1) { 087 return false; 088 } else { 089 return alternatives[selectedIndex].isInjectable(); 090 } 091 } 092 093 @Override 094 public String toString() { 095 if (alternatives.length == 1) { 096 return getNode().getName() + " = " + alternatives[0]; 097 } else if (alternatives.length == 0) { 098 return getNode().getName() + ": no injectable constructors"; 099 } 100 final StringBuilder sb = new StringBuilder("["); 101 sb.append(getNode().getName() + " = " + alternatives[0]); 102 for (int i = 1; i < alternatives.length; i++) { 103 sb.append(" | " + alternatives[i]); 104 } 105 sb.append("]"); 106 return sb.toString(); 107 } 108 109 @Override 110 public String toShallowString() { 111 if (alternatives.length == 1) { 112 return getNode().getName() + " = " + alternatives[0].toShallowString(); 113 } else if (alternatives.length == 0) { 114 return getNode().getName() + ": no injectable constructors"; 115 } 116 final StringBuilder sb = new StringBuilder("["); 117 sb.append(getNode().getName() + " = " + alternatives[0].toShallowString()); 118 for (int i = 1; i < alternatives.length; i++) { 119 sb.append(" | " + alternatives[i].toShallowString()); 120 } 121 sb.append("]"); 122 return sb.toString(); 123 } 124 125 public int getSelectedIndex() { 126 return selectedIndex; 127 } 128 129 public InjectionPlan<? extends T> getDelegatedPlan() { 130 if (selectedIndex == -1) { 131 throw new IllegalStateException("selectedIndex equals -1"); 132 } else { 133 return alternatives[selectedIndex]; 134 } 135 } 136 137 @Override 138 protected String toAmbiguousInjectString() { 139 if (alternatives.length == 1) { 140 return alternatives[0].toAmbiguousInjectString(); 141 } else if (selectedIndex != -1) { 142 return alternatives[selectedIndex].toAmbiguousInjectString(); 143 } else { 144 final List<InjectionPlan<?>> alts = new ArrayList<>(); 145 final List<InjectionPlan<?>> ambig = new ArrayList<>(); 146 for (final InjectionPlan<?> alt : alternatives) { 147 if (alt.isFeasible()) { 148 alts.add(alt); 149 } 150 if (alt.isAmbiguous()) { 151 ambig.add(alt); 152 } 153 } 154 final StringBuffer sb = new StringBuffer("Ambiguous subplan " + getNode().getFullName()); 155 for (final InjectionPlan<?> alt : alts) { 156 sb.append("\n " + alt.toShallowString() + " "); 157 } 158 for (final InjectionPlan<?> alt : ambig) { 159 sb.append("\n " + alt.toShallowString() + " "); 160 } 161 sb.append("\n]"); 162 return sb.toString(); 163 } 164 } 165 166 @Override 167 protected String toInfeasibleInjectString() { 168 if (alternatives.length == 1) { 169 return alternatives[0].toInfeasibleInjectString(); 170 } else if (alternatives.length == 0) { 171 return "No known implementations / injectable constructors for " 172 + this.getNode().getFullName(); 173 } else if (selectedIndex != -1) { 174 return alternatives[selectedIndex].toInfeasibleInjectString(); 175 } else { 176 return "Multiple infeasible plans: " + toPrettyString(); 177 } 178 } 179 180 @Override 181 protected boolean isInfeasibleLeaf() { 182 return false; 183 } 184 185 public InjectionPlan<?>[] getPlans() { 186 return Arrays.copyOf(alternatives, alternatives.length); 187 } 188 189}