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