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; 022import org.apache.reef.tang.types.Traversable; 023 024import java.util.Collection; 025import java.util.Collections; 026 027public abstract class InjectionPlan<T> implements Traversable<InjectionPlan<?>> { 028 029 protected final Node node; 030 031 public InjectionPlan(final Node node) { 032 this.node = node; 033 } 034 035 private static void newline(final StringBuffer pretty, final int indent) { 036 pretty.append('\n'); 037 for (int j = 0; j < indent * 2; j++) { 038 pretty.append(' '); 039 } 040 } 041 042 public Node getNode() { 043 return node; 044 } 045 046 /** 047 * Get child elements of the injection plan tree. 048 * By default, returns an empty list. 049 * 050 * @return An empty list. 051 */ 052 @SuppressWarnings("unchecked") 053 @Override 054 public Collection<InjectionPlan<?>> getChildren() { 055 return Collections.EMPTY_LIST; 056 } 057 058 public abstract int getNumAlternatives(); 059 060 public boolean isFeasible() { 061 return getNumAlternatives() > 0; 062 } 063 064 public abstract boolean isAmbiguous(); 065 066 public abstract boolean isInjectable(); 067 068 protected void pad(final StringBuffer sb, final int n) { 069 for (int i = 0; i < n; i++) { 070 sb.append(" "); 071 } 072 } 073 074 public String toPrettyString() { 075 final String ugly = node.getFullName() + ":\n" + toString(); 076 final StringBuffer pretty = new StringBuffer(); 077 int currentIndent = 1; 078 for (int i = 0; i < ugly.length(); i++) { 079 final char c = ugly.charAt(i); 080 if (c == '(') { 081 if (ugly.charAt(i + 1) == ')') { 082 pretty.append("()"); 083 i++; 084 } else { 085 newline(pretty, currentIndent); 086 currentIndent++; 087 pretty.append(c); 088 pretty.append(' '); 089 } 090 } else if (c == '[') { 091 if (ugly.charAt(i + 1) == ']') { 092 pretty.append("[]"); 093 i++; 094 } else { 095 newline(pretty, currentIndent); 096 currentIndent++; 097 pretty.append(c); 098 pretty.append(' '); 099 } 100 } else if (c == ')' || c == ']') { 101 currentIndent--; 102 newline(pretty, currentIndent); 103 pretty.append(c); 104 } else if (c == '|') { 105 newline(pretty, currentIndent); 106 pretty.append(c); 107 } else if (c == ',') { 108 currentIndent--; 109 newline(pretty, currentIndent); 110 pretty.append(c); 111 currentIndent++; 112 } else { 113 pretty.append(c); 114 } 115 } 116 return pretty.toString(); 117 } 118 119 /** 120 * Algorithm for generating cant inject string: 121 * <p> 122 * For infeasible plans: 123 * <p> 124 * Some node types are "leaves": 125 * <ul> 126 * <li>NamedParameterNode</li> 127 * <li>ClassNode with no @Inject constructors</li> 128 * </ul> 129 * We do a depth first search of the injection plan, starting with the left 130 * most constructor arguments. When we encounter a constructor whose arguments 131 * are all either injectable or non-injectable leaf nodes, we return the name 132 * of its parent, and the name of the non-injectable leaves. 133 * <p> 134 * For ambiguous plans: 135 * <p> 136 * We perform a depth first search of the ambiguous constructors, as above. We 137 * return the name of the first class that has multiple constructors that are 138 * feasible or ambiguous (as opposed to having a single constructor with an 139 * ambiguous argument, or a constructor with an infeasible argument and an 140 * ambiguous argument). 141 */ 142 public final String toCantInjectString() { 143 if (!isFeasible()) { 144 return toInfeasibleInjectString(); 145 } else if (isAmbiguous()) { 146 return toAmbiguousInjectString(); 147 } else { 148 throw new IllegalArgumentException( 149 "toCantInjectString() called on injectable constructor:" 150 + this.toPrettyString()); 151 } 152 } 153 154 protected abstract String toAmbiguousInjectString(); 155 156 protected abstract String toInfeasibleInjectString(); 157 158 protected abstract boolean isInfeasibleLeaf(); 159 160 public abstract String toShallowString(); 161}