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(); 036 } 037 this.selectedIndex = selectedIndex; 038 if (selectedIndex != -1) { 039 this.numAlternatives = alternatives[selectedIndex].getNumAlternatives(); 040 } else { 041 int numAlternativesSum = 0; 042 for (final InjectionPlan<? extends T> a : alternatives) { 043 numAlternativesSum += a.getNumAlternatives(); 044 } 045 this.numAlternatives = numAlternativesSum; 046 } 047 } 048 049 @SafeVarargs 050 public Subplan(final Node node, final InjectionPlan<T>... alternatives) { 051 this(node, -1, alternatives); 052 } 053 054 /** 055 * Get child elements of the injection plan tree. 056 * TODO: use ArrayList internally (and maybe for input, too). 057 * 058 * @return A list of injection sub-plans. 059 */ 060 @SuppressWarnings({"unchecked", "rawtypes"}) 061 @Override 062 public Collection<InjectionPlan<?>> getChildren() { 063 return (Collection) Collections.unmodifiableCollection(Arrays.asList(this.alternatives)); 064 } 065 066 @Override 067 public int getNumAlternatives() { 068 return this.numAlternatives; 069 } 070 071 /** 072 * Even if there is only one sub-plan, it was registered as a default plan, 073 * and is therefore ambiguous. 074 */ 075 @Override 076 public boolean isAmbiguous() { 077 if (selectedIndex == -1) { 078 return true; 079 } 080 return alternatives[selectedIndex].isAmbiguous(); 081 } 082 083 @Override 084 public boolean isInjectable() { 085 if (selectedIndex == -1) { 086 return false; 087 } else { 088 return alternatives[selectedIndex].isInjectable(); 089 } 090 } 091 092 @Override 093 public String toString() { 094 if (alternatives.length == 1) { 095 return getNode().getName() + " = " + alternatives[0]; 096 } else if (alternatives.length == 0) { 097 return getNode().getName() + ": no injectable constructors"; 098 } 099 final StringBuilder sb = new StringBuilder("["); 100 sb.append(getNode().getName() + " = " + alternatives[0]); 101 for (int i = 1; i < alternatives.length; i++) { 102 sb.append(" | " + alternatives[i]); 103 } 104 sb.append("]"); 105 return sb.toString(); 106 } 107 108 @Override 109 public String toShallowString() { 110 if (alternatives.length == 1) { 111 return getNode().getName() + " = " + alternatives[0].toShallowString(); 112 } else if (alternatives.length == 0) { 113 return getNode().getName() + ": no injectable constructors"; 114 } 115 final StringBuilder sb = new StringBuilder("["); 116 sb.append(getNode().getName() + " = " + alternatives[0].toShallowString()); 117 for (int i = 1; i < alternatives.length; i++) { 118 sb.append(" | " + alternatives[i].toShallowString()); 119 } 120 sb.append("]"); 121 return sb.toString(); 122 } 123 124 public int getSelectedIndex() { 125 return selectedIndex; 126 } 127 128 public InjectionPlan<? extends T> getDelegatedPlan() { 129 if (selectedIndex == -1) { 130 throw new IllegalStateException(); 131 } else { 132 return alternatives[selectedIndex]; 133 } 134 } 135 136 @Override 137 protected String toAmbiguousInjectString() { 138 if (alternatives.length == 1) { 139 return alternatives[0].toAmbiguousInjectString(); 140 } else if (selectedIndex != -1) { 141 return alternatives[selectedIndex].toAmbiguousInjectString(); 142 } else { 143 final List<InjectionPlan<?>> alts = new ArrayList<>(); 144 final List<InjectionPlan<?>> ambig = new ArrayList<>(); 145 for (final InjectionPlan<?> alt : alternatives) { 146 if (alt.isFeasible()) { 147 alts.add(alt); 148 } 149 if (alt.isAmbiguous()) { 150 ambig.add(alt); 151 } 152 } 153 final StringBuffer sb = new StringBuffer("Ambiguous subplan " + getNode().getFullName()); 154 for (final InjectionPlan<?> alt : alts) { 155 sb.append("\n " + alt.toShallowString() + " "); 156 } 157 for (final InjectionPlan<?> alt : ambig) { 158 sb.append("\n " + alt.toShallowString() + " "); 159 } 160 sb.append("\n]"); 161 return sb.toString(); 162 } 163 } 164 165 @Override 166 protected String toInfeasibleInjectString() { 167 if (alternatives.length == 1) { 168 return alternatives[0].toInfeasibleInjectString(); 169 } else if (alternatives.length == 0) { 170 return "No known implementations / injectable constructors for " 171 + this.getNode().getFullName(); 172 } else if (selectedIndex != -1) { 173 return alternatives[selectedIndex].toInfeasibleInjectString(); 174 } else { 175 return "Multiple infeasible plans: " + toPrettyString(); 176 } 177 } 178 179 @Override 180 protected boolean isInfeasibleLeaf() { 181 return false; 182 } 183 184 public InjectionPlan<?>[] getPlans() { 185 return Arrays.copyOf(alternatives, alternatives.length); 186 } 187 188}