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.ClassNode; 022import org.apache.reef.tang.types.ConstructorDef; 023 024import java.util.*; 025 026public final class Constructor<T> extends InjectionPlan<T> { 027 028 private final ConstructorDef<T> constructor; 029 private final InjectionPlan<?>[] args; 030 private final int numAlternatives; 031 private final boolean isAmbiguous; 032 private final boolean isInjectable; 033 034 public Constructor(final ClassNode<T> classNode, 035 final ConstructorDef<T> constructor, final InjectionPlan<?>[] args) { 036 super(classNode); 037 this.constructor = constructor; 038 this.args = args; 039 int curAlternatives = 1; 040 boolean curAmbiguous = false; 041 boolean curInjectable = true; 042 for (final InjectionPlan<?> plan : args) { 043 curAlternatives *= plan.getNumAlternatives(); 044 curAmbiguous |= plan.isAmbiguous(); 045 curInjectable &= plan.isInjectable(); 046 } 047 this.numAlternatives = curAlternatives; 048 this.isAmbiguous = curAmbiguous; 049 this.isInjectable = curInjectable; 050 } 051 052 public InjectionPlan<?>[] getArgs() { 053 return args; 054 } 055 056 /** 057 * Get child elements of the injection plan tree. 058 * This method is inherited from the Traversable interface. 059 * TODO: use ArrayList internally (and maybe for input, too). 060 * 061 * @return A list of injection plans for the constructor's arguments. 062 */ 063 @Override 064 public Collection<InjectionPlan<?>> getChildren() { 065 return Collections.unmodifiableList(Arrays.asList(this.args)); 066 } 067 068 public ConstructorDef<T> getConstructorDef() { 069 return constructor; 070 } 071 072 @SuppressWarnings("unchecked") 073 @Override 074 public ClassNode<T> getNode() { 075 return (ClassNode<T>) node; 076 } 077 078 @Override 079 public int getNumAlternatives() { 080 return numAlternatives; 081 } 082 083 @Override 084 public boolean isAmbiguous() { 085 return isAmbiguous; 086 } 087 088 @Override 089 public boolean isInjectable() { 090 return isInjectable; 091 } 092 093 @Override 094 public String toString() { 095 final StringBuilder sb = new StringBuilder("new " + getNode().getName() + '('); 096 if (args.length > 0) { 097 sb.append(args[0]); 098 for (int i = 1; i < args.length; i++) { 099 sb.append(", " + args[i]); 100 } 101 } 102 sb.append(')'); 103 return sb.toString(); 104 } 105 106 private String shallowArgString(final InjectionPlan<?> arg) { 107 if (arg instanceof Constructor || arg instanceof Subplan) { 108 return arg.getClass().getName() + ": " + arg.getNode().getName(); 109 } else { 110 return arg.toShallowString(); 111 } 112 } 113 114 @Override 115 public String toShallowString() { 116 final StringBuilder sb = new StringBuilder("new " + getNode().getName() + '('); 117 if (args.length > 0) { 118 sb.append(shallowArgString(args[0])); 119 for (int i = 1; i < args.length; i++) { 120 sb.append(", " + shallowArgString(args[i])); 121 } 122 } 123 sb.append(')'); 124 return sb.toString(); 125 } 126 127 /** 128 * @return A string describing ambiguous constructor arguments. 129 * @throws IllegalArgumentException if constructor is not ambiguous. 130 */ 131 @Override 132 protected String toAmbiguousInjectString() { 133 134 if (!isAmbiguous) { 135 throw new IllegalArgumentException(getNode().getFullName() + " is NOT ambiguous."); 136 } 137 138 final StringBuilder sb = new StringBuilder( 139 getNode().getFullName() + " has ambiguous arguments: [ "); 140 141 for (final InjectionPlan<?> plan : args) { 142 if (plan.isAmbiguous()) { 143 sb.append(plan.toAmbiguousInjectString()); 144 } 145 } 146 147 sb.append(']'); 148 return sb.toString(); 149 } 150 151 @Override 152 protected String toInfeasibleInjectString() { 153 154 final List<InjectionPlan<?>> leaves = new ArrayList<>(); 155 156 for (final InjectionPlan<?> ip : args) { 157 if (!ip.isFeasible()) { 158 if (ip.isInfeasibleLeaf()) { 159 leaves.add(ip); 160 } else { 161 return ip.toInfeasibleInjectString(); 162 } 163 } 164 } 165 166 if (leaves.isEmpty()) { 167 throw new IllegalArgumentException(getNode().getFullName() + " has NO infeasible leaves."); 168 } 169 170 if (leaves.size() == 1) { 171 return getNode().getFullName() + " missing argument " + leaves.get(0).getNode().getFullName(); 172 } else { 173 final StringBuffer sb = new StringBuffer(getNode().getFullName() + " missing arguments: [\n\t"); 174 for (final InjectionPlan<?> leaf : leaves) { 175 sb.append(leaf.getNode().getFullName() + "\n\t"); 176 } 177 sb.append(']'); 178 return sb.toString(); 179 } 180 } 181 182 @Override 183 protected boolean isInfeasibleLeaf() { 184 return false; 185 } 186}