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.types; 020 021import org.apache.reef.tang.exceptions.ClassHierarchyException; 022import org.apache.reef.tang.types.ClassNode; 023import org.apache.reef.tang.types.ConstructorArg; 024import org.apache.reef.tang.types.ConstructorDef; 025 026import java.util.Arrays; 027import java.util.HashMap; 028 029public class ConstructorDefImpl<T> implements ConstructorDef<T> { 030 private final ConstructorArg[] args; 031 private final String className; 032 033 public ConstructorDefImpl(final String className, final ConstructorArg[] args, 034 final boolean injectable) throws ClassHierarchyException { 035 this.className = className; 036 this.args = args; 037 if (injectable) { 038 for (int i = 0; i < this.getArgs().length; i++) { 039 for (int j = i + 1; j < this.getArgs().length; j++) { 040 if (this.getArgs()[i].equals(this.getArgs()[j])) { 041 throw new ClassHierarchyException( 042 "Repeated constructor parameter detected. " 043 + "Cannot inject constructor " + toString()); 044 } 045 } 046 } 047 } 048 } 049 050 @Override 051 public ConstructorArg[] getArgs() { 052 return args; 053 } 054 055 @Override 056 public String getClassName() { 057 return className; 058 } 059 060 private String join(final String sep, final Object[] vals) { 061 if (vals.length != 0) { 062 final StringBuilder sb = new StringBuilder(vals[0].toString()); 063 for (int i = 1; i < vals.length; i++) { 064 sb.append(sep + vals[i]); 065 } 066 return sb.toString(); 067 } else { 068 return ""; 069 } 070 } 071 072 @Override 073 public String toString() { 074 final StringBuilder sb = new StringBuilder(className); 075 sb.append("("); 076 sb.append(join(",", args)); 077 sb.append(")"); 078 return sb.toString(); 079 } 080 081 @Override 082 public boolean takesParameters(final ClassNode<?>[] paramTypes) { 083 if (paramTypes.length != args.length) { 084 return false; 085 } 086 for (int i = 0; i < paramTypes.length; i++) { 087 if (!args[i].getType().equals(paramTypes[i].getFullName())) { 088 return false; 089 } 090 } 091 return true; 092 } 093 094 /** 095 * Check to see if two boundConstructors take indistinguishable arguments. If 096 * so (and they are in the same class), then this would lead to ambiguous 097 * injection targets, and we want to fail fast. 098 * 099 * @param def 100 * @return 101 */ 102 private boolean equalsIgnoreOrder(final ConstructorDef<?> def) { 103 HashMap map = new HashMap(); 104 for (ConstructorArg a : def.getArgs()) { 105 map.put(a.getName(), null); 106 } 107 for (ConstructorArg a : getArgs()) { 108 if (!map.containsKey(a.getName())) { 109 return false; 110 } 111 } 112 return true; 113 } 114 115 @Override 116 public boolean equals(final Object o) { 117 if (this == o) { 118 return true; 119 } 120 if (o == null || getClass() != o.getClass()) { 121 return false; 122 } 123 124 int length = getArgs().length; 125 ConstructorDef<?> def = (ConstructorDef<?>) o; 126 if (length != def.getArgs().length) { 127 return false; 128 } 129 if (length == 0) { 130 return true; 131 } 132 if (length == 1) { 133 return getArgs()[0].getName().equals(def.getArgs()[0].getName()); 134 } 135 return equalsIgnoreOrder(def); 136 } 137 138 @Override 139 public boolean isMoreSpecificThan(final ConstructorDef<?> def) { 140 // Return true if our list of args is a superset of those in def. 141 142 // Is everything in def also in this? 143 for (int i = 0; i < def.getArgs().length; i++) { 144 boolean found = false; 145 for (int j = 0; j < this.getArgs().length; j++) { 146 if (getArgs()[j].equals(def.getArgs()[i])) { 147 found = true; 148 break; 149 } 150 } 151 // If not, then argument j from def is not in our list. Return false. 152 if (!found) { 153 return false; 154 } 155 } 156 // Everything in def's arg list is in ours. Do we have at least one extra 157 // argument? 158 return getArgs().length > def.getArgs().length; 159 } 160 161 @Override 162 public int compareTo(final ConstructorDef<?> o) { 163 return toString().compareTo(o.toString()); 164 } 165 166 @Override 167 public int hashCode() { 168 final ConstructorArg[] argsSort = getArgs().clone(); 169 Arrays.sort(argsSort); 170 int result = Arrays.hashCode(argsSort); 171 result = 31 * result + (this.className == null ? 0 : this.className.hashCode()); 172 return result; 173 } 174}