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.*; 022import org.apache.reef.tang.exceptions.BindException; 023import org.apache.reef.tang.exceptions.NameResolutionException; 024import org.apache.reef.tang.exceptions.ParseException; 025import org.apache.reef.tang.implementation.java.ClassHierarchyImpl; 026import org.apache.reef.tang.types.*; 027import org.apache.reef.tang.util.MonotonicMultiMap; 028import org.apache.reef.tang.util.TracingMonotonicMap; 029import org.apache.reef.tang.util.TracingMonotonicTreeMap; 030 031import java.net.URL; 032import java.util.List; 033import java.util.Map; 034import java.util.Map.Entry; 035import java.util.Set; 036 037public class ConfigurationBuilderImpl implements ConfigurationBuilder { 038 public final static String IMPORT = "import"; 039 public final static String INIT = "<init>"; 040 final TracingMonotonicMap<ClassNode<?>, ClassNode<?>> boundImpls = new TracingMonotonicTreeMap<>(); 041 final TracingMonotonicMap<ClassNode<?>, ClassNode<? extends ExternalConstructor<?>>> boundConstructors = new TracingMonotonicTreeMap<>(); 042 final Map<NamedParameterNode<?>, String> namedParameters = new TracingMonotonicTreeMap<>(); 043 final Map<ClassNode<?>, ConstructorDef<?>> legacyConstructors = new TracingMonotonicTreeMap<>(); 044 final MonotonicMultiMap<NamedParameterNode<Set<?>>, Object> boundSetEntries = new MonotonicMultiMap<>(); 045 final TracingMonotonicMap<NamedParameterNode<List<?>>, List<Object>> boundLists = new TracingMonotonicTreeMap<>(); 046 // TODO: None of these should be public! - Move to configurationBuilder. Have 047 // that wrap itself 048 // in a sane Configuration interface... 049 // TODO: Should be final again! 050 public ClassHierarchy namespace; 051 052 protected ConfigurationBuilderImpl() { 053 this.namespace = Tang.Factory.getTang().getDefaultClassHierarchy(); 054 } 055 056 protected ConfigurationBuilderImpl(ClassHierarchy namespace) { 057 this.namespace = namespace; 058 } 059 060 protected ConfigurationBuilderImpl(URL[] jars, Configuration[] confs, Class<? extends ExternalConstructor<?>>[] parsers) 061 throws BindException { 062 this.namespace = Tang.Factory.getTang().getDefaultClassHierarchy(jars, parsers); 063 for (Configuration tc : confs) { 064 addConfiguration(((ConfigurationImpl) tc)); 065 } 066 } 067 068 protected ConfigurationBuilderImpl(ConfigurationBuilderImpl t) { 069 this.namespace = t.getClassHierarchy(); 070 try { 071 addConfiguration(t.getClassHierarchy(), t); 072 } catch (BindException e) { 073 throw new IllegalStateException("Could not copy builder", e); 074 } 075 } 076 077 @SuppressWarnings("unchecked") 078 protected ConfigurationBuilderImpl(URL... jars) throws BindException { 079 this(jars, new Configuration[0], new Class[0]); 080 } 081 082 @SuppressWarnings("unchecked") 083 protected ConfigurationBuilderImpl(Configuration... confs) throws BindException { 084 this(new URL[0], confs, new Class[0]); 085 } 086 087 @Override 088 public void addConfiguration(Configuration conf) throws BindException { 089 // XXX remove cast! 090 addConfiguration(conf.getClassHierarchy(), ((ConfigurationImpl) conf).builder); 091 } 092 093 @SuppressWarnings("unchecked") 094 private <T> void addConfiguration(ClassHierarchy ns, ConfigurationBuilderImpl builder) 095 throws BindException { 096 namespace = namespace.merge(ns); 097 if ((namespace instanceof ClassHierarchyImpl || builder.namespace instanceof ClassHierarchyImpl)) { 098 if ((namespace instanceof ClassHierarchyImpl && builder.namespace instanceof ClassHierarchyImpl)) { 099 ((ClassHierarchyImpl) namespace).parameterParser 100 .mergeIn(((ClassHierarchyImpl) builder.namespace).parameterParser); 101 } else { 102 throw new IllegalArgumentException("Attempt to merge Java and non-Java class hierarchy! Not supported."); 103 } 104 } 105 106 for (ClassNode<?> cn : builder.boundImpls.keySet()) { 107 bind(cn.getFullName(), builder.boundImpls.get(cn).getFullName()); 108 } 109 for (ClassNode<?> cn : builder.boundConstructors.keySet()) { 110 bind(cn.getFullName(), builder.boundConstructors.get(cn).getFullName()); 111 } 112 // The namedParameters set contains the strings that can be used to 113 // instantiate new 114 // named parameter instances. Create new ones where we can. 115 for (NamedParameterNode<?> np : builder.namedParameters.keySet()) { 116 bind(np.getFullName(), builder.namedParameters.get(np)); 117 } 118 for (ClassNode<?> cn : builder.legacyConstructors.keySet()) { 119 registerLegacyConstructor(cn, builder.legacyConstructors.get(cn) 120 .getArgs()); 121 } 122 for (Entry<NamedParameterNode<Set<?>>, Object> e : builder.boundSetEntries) { 123 String name = ((NamedParameterNode<Set<T>>) (NamedParameterNode<?>) e.getKey()).getFullName(); 124 if (e.getValue() instanceof Node) { 125 bindSetEntry(name, (Node) e.getValue()); 126 } else if (e.getValue() instanceof String) { 127 bindSetEntry(name, (String) e.getValue()); 128 } else { 129 throw new IllegalStateException(); 130 } 131 } 132 // The boundLists set contains bound lists with their target NamedParameters 133 for (NamedParameterNode<List<?>> np : builder.boundLists.keySet()) { 134 bindList(np.getFullName(), builder.boundLists.get(np)); 135 } 136 } 137 138 @Override 139 public ClassHierarchy getClassHierarchy() { 140 return namespace; 141 } 142 143 @Override 144 public void registerLegacyConstructor(ClassNode<?> c, 145 final ConstructorArg... args) throws BindException { 146 String cn[] = new String[args.length]; 147 for (int i = 0; i < args.length; i++) { 148 cn[i] = args[i].getType(); 149 } 150 registerLegacyConstructor(c.getFullName(), cn); 151 } 152 153 @Override 154 public void registerLegacyConstructor(String s, final String... args) 155 throws BindException { 156 ClassNode<?> cn = (ClassNode<?>) namespace.getNode(s); 157 ClassNode<?>[] cnArgs = new ClassNode[args.length]; 158 for (int i = 0; i < args.length; i++) { 159 cnArgs[i] = (ClassNode<?>) namespace.getNode(args[i]); 160 } 161 registerLegacyConstructor(cn, cnArgs); 162 } 163 164 @Override 165 public void registerLegacyConstructor(ClassNode<?> cn, 166 final ClassNode<?>... args) throws BindException { 167 legacyConstructors.put(cn, cn.getConstructorDef(args)); 168 } 169 170 @Override 171 public <T> void bind(String key, String value) throws BindException { 172 Node n = namespace.getNode(key); 173 if (n instanceof NamedParameterNode) { 174 bindParameter((NamedParameterNode<?>) n, value); 175 } else if (n instanceof ClassNode) { 176 Node m = namespace.getNode(value); 177 bind((ClassNode<?>) n, (ClassNode<?>) m); 178 } else { 179 throw new IllegalStateException("getNode() returned " + n + " which is neither a ClassNode nor a NamedParameterNode"); 180 } 181 } 182 183 @SuppressWarnings({"unchecked", "rawtypes"}) 184 public void bind(Node key, Node value) throws BindException { 185 if (key instanceof NamedParameterNode) { 186 bindParameter((NamedParameterNode<?>) key, value.getFullName()); 187 } else if (key instanceof ClassNode) { 188 ClassNode<?> k = (ClassNode<?>) key; 189 if (value instanceof ClassNode) { 190 ClassNode<?> val = (ClassNode<?>) value; 191 if (val.isExternalConstructor() && !k.isExternalConstructor()) { 192 bindConstructor(k, (ClassNode) val); 193 } else { 194 bindImplementation(k, (ClassNode) val); 195 } 196 } 197 } 198 } 199 200 public <T> void bindImplementation(ClassNode<T> n, ClassNode<? extends T> m) 201 throws BindException { 202 if (namespace.isImplementation(n, m)) { 203 boundImpls.put(n, m); 204 } else { 205 throw new IllegalArgumentException("Class" + m + " does not extend " + n); 206 } 207 } 208 209 @SuppressWarnings({"unchecked", "rawtypes"}) 210 public <T> void bindParameter(NamedParameterNode<T> name, String value) 211 throws BindException { 212 /* Parse and discard value; this is just for type checking */ 213 if (namespace instanceof JavaClassHierarchy) { 214 ((JavaClassHierarchy) namespace).parse(name, value); 215 } 216 if (name.isSet()) { 217 bindSetEntry((NamedParameterNode) name, value); 218 } else { 219 namedParameters.put(name, value); 220 } 221 } 222 223 @SuppressWarnings("unchecked") 224 @Override 225 public void bindSetEntry(String iface, String impl) 226 throws BindException { 227 boundSetEntries.put((NamedParameterNode<Set<?>>) namespace.getNode(iface), impl); 228 } 229 230 @SuppressWarnings("unchecked") 231 @Override 232 public void bindSetEntry(String iface, Node impl) 233 throws BindException { 234 boundSetEntries.put((NamedParameterNode<Set<?>>) namespace.getNode(iface), impl); 235 } 236 237 @SuppressWarnings("unchecked") 238 @Override 239 public <T> void bindSetEntry(NamedParameterNode<Set<T>> iface, String impl) 240 throws BindException { 241 if (namespace instanceof ClassHierarchyImpl) { 242 JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace; 243 try { 244 javanamespace.parse(iface, impl); 245 } catch (ParseException e) { 246 throw new IllegalStateException("Could not parse " + impl + " which was passed to " + iface); 247 } 248 } 249 boundSetEntries.put((NamedParameterNode<Set<?>>) (NamedParameterNode<?>) iface, impl); 250 } 251 252 @SuppressWarnings("unchecked") 253 @Override 254 public <T> void bindSetEntry(NamedParameterNode<Set<T>> iface, Node impl) 255 throws BindException { 256 boundSetEntries.put((NamedParameterNode<Set<?>>) (NamedParameterNode<?>) iface, impl); 257 } 258 259 @SuppressWarnings("unchecked") 260 @Override 261 public <T> void bindList(NamedParameterNode<List<T>> iface, List implList) { 262 // Check parsability of list items 263 for (Object item : implList) { 264 if (item instanceof String) { 265 JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace; 266 try { 267 // Just for parsability checking. 268 javanamespace.parse(iface, (String) item); 269 } catch (ParseException e) { 270 throw new IllegalStateException("Could not parse " + item + " which was passed to " + iface); 271 } 272 } 273 } 274 boundLists.put((NamedParameterNode<List<?>>) (NamedParameterNode<?>) iface, implList); 275 } 276 277 @SuppressWarnings("unchecked") 278 @Override 279 public void bindList(String iface, List implList) { 280 NamedParameterNode<List<?>> ifaceNode = (NamedParameterNode<List<?>>) namespace.getNode(iface); 281 // Check parsability of list items 282 for (Object item : implList) { 283 if (item instanceof String) { 284 JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace; 285 try { 286 // Just for parsability checking. 287 javanamespace.parse(ifaceNode, (String) item); 288 } catch (ParseException e) { 289 throw new IllegalStateException("Could not parse " + item + " which was passed to " + iface); 290 } 291 } 292 } 293 boundLists.put(ifaceNode, implList); 294 } 295 296 @Override 297 public <T> void bindConstructor(ClassNode<T> k, 298 ClassNode<? extends ExternalConstructor<? extends T>> v) { 299 boundConstructors.put(k, v); 300 } 301 302 @Override 303 public ConfigurationImpl build() { 304 return new ConfigurationImpl(new ConfigurationBuilderImpl(this)); 305 } 306 307 @Override 308 public String classPrettyDefaultString(String longName) throws NameResolutionException { 309 final NamedParameterNode<?> param = (NamedParameterNode<?>) namespace 310 .getNode(longName); 311 return param.getSimpleArgName() + "=" + join(",", param.getDefaultInstanceAsStrings()); 312 } 313 314 private String join(String sep, String[] s) { 315 if (s.length == 0) { 316 return null; 317 } else { 318 StringBuilder sb = new StringBuilder(s[0]); 319 for (int i = 1; i < s.length; i++) { 320 sb.append(sep); 321 sb.append(s[i]); 322 } 323 return sb.toString(); 324 } 325 } 326 327 @Override 328 public String classPrettyDescriptionString(String fullName) 329 throws NameResolutionException { 330 final NamedParameterNode<?> param = (NamedParameterNode<?>) namespace 331 .getNode(fullName); 332 return param.getDocumentation() + "\n" + param.getFullName(); 333 } 334 335 @Override 336 public boolean equals(final Object o) { 337 if (this == o) { 338 return true; 339 } 340 if (o == null || getClass() != o.getClass()) { 341 return false; 342 } 343 344 ConfigurationBuilderImpl that = (ConfigurationBuilderImpl) o; 345 346 if (boundConstructors != null ? !boundConstructors.equals(that.boundConstructors) : that.boundConstructors != null) { 347 return false; 348 } 349 if (boundImpls != null ? !boundImpls.equals(that.boundImpls) : that.boundImpls != null) { 350 return false; 351 } 352 if (boundSetEntries != null ? !boundSetEntries.equals(that.boundSetEntries) : that.boundSetEntries != null) { 353 return false; 354 } 355 if (boundLists != null ? !boundLists.equals(that.boundLists) : that.boundLists != null) { 356 return false; 357 } 358 if (legacyConstructors != null ? !legacyConstructors.equals(that.legacyConstructors) : that.legacyConstructors != null) { 359 return false; 360 } 361 if (namedParameters != null ? !namedParameters.equals(that.namedParameters) : that.namedParameters != null) { 362 return false; 363 } 364 365 return true; 366 } 367 368 @Override 369 public int hashCode() { 370 int result = boundImpls != null ? boundImpls.hashCode() : 0; 371 result = 31 * result + (boundConstructors != null ? boundConstructors.hashCode() : 0); 372 result = 31 * result + (namedParameters != null ? namedParameters.hashCode() : 0); 373 result = 31 * result + (legacyConstructors != null ? legacyConstructors.hashCode() : 0); 374 result = 31 * result + (boundSetEntries != null ? boundSetEntries.hashCode() : 0); 375 result = 31 * result + (boundLists != null ? boundLists.hashCode() : 0); 376 return result; 377 } 378}