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}