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(); 134 } 135 } 136 // The boundLists set contains bound lists with their target NamedParameters 137 for (final NamedParameterNode<List<?>> np : builder.boundLists.keySet()) { 138 bindList(np.getFullName(), builder.boundLists.get(np)); 139 } 140 } 141 142 @Override 143 public ClassHierarchy getClassHierarchy() { 144 return namespace; 145 } 146 147 @Override 148 public void registerLegacyConstructor(final ClassNode<?> c, 149 final ConstructorArg... args) throws BindException { 150 final String[] cn = new String[args.length]; 151 for (int i = 0; i < args.length; i++) { 152 cn[i] = args[i].getType(); 153 } 154 registerLegacyConstructor(c.getFullName(), cn); 155 } 156 157 @Override 158 public void registerLegacyConstructor(final String s, final String... args) 159 throws BindException { 160 final ClassNode<?> cn = (ClassNode<?>) namespace.getNode(s); 161 final ClassNode<?>[] cnArgs = new ClassNode[args.length]; 162 for (int i = 0; i < args.length; i++) { 163 cnArgs[i] = (ClassNode<?>) namespace.getNode(args[i]); 164 } 165 registerLegacyConstructor(cn, cnArgs); 166 } 167 168 @Override 169 public void registerLegacyConstructor(final ClassNode<?> cn, 170 final ClassNode<?>... args) throws BindException { 171 legacyConstructors.put(cn, cn.getConstructorDef(args)); 172 } 173 174 @Override 175 public <T> void bind(final String key, final String value) throws BindException { 176 final Node n = namespace.getNode(key); 177 if (n instanceof NamedParameterNode) { 178 bindParameter((NamedParameterNode<?>) n, value); 179 } else if (n instanceof ClassNode) { 180 final Node m = namespace.getNode(value); 181 bind((ClassNode<?>) n, (ClassNode<?>) m); 182 } else { 183 throw new IllegalStateException("getNode() returned " + n + 184 " which is neither a ClassNode nor a NamedParameterNode"); 185 } 186 } 187 188 @SuppressWarnings({"unchecked", "rawtypes"}) 189 public void bind(final Node key, final Node value) throws BindException { 190 if (key instanceof NamedParameterNode) { 191 bindParameter((NamedParameterNode<?>) key, value.getFullName()); 192 } else if (key instanceof ClassNode) { 193 final ClassNode<?> k = (ClassNode<?>) key; 194 if (value instanceof ClassNode) { 195 final ClassNode<?> val = (ClassNode<?>) value; 196 if (val.isExternalConstructor() && !k.isExternalConstructor()) { 197 bindConstructor(k, (ClassNode) val); 198 } else { 199 bindImplementation(k, (ClassNode) val); 200 } 201 } 202 } 203 } 204 205 public <T> void bindImplementation(final ClassNode<T> n, final ClassNode<? extends T> m) 206 throws BindException { 207 if (namespace.isImplementation(n, m)) { 208 boundImpls.put(n, m); 209 } else { 210 throw new IllegalArgumentException("Class" + m + " does not extend " + n); 211 } 212 } 213 214 @SuppressWarnings({"unchecked", "rawtypes"}) 215 public <T> void bindParameter(final NamedParameterNode<T> name, final String value) 216 throws BindException { 217 /* Parse and discard value; this is just for type checking */ 218 if (namespace instanceof JavaClassHierarchy) { 219 ((JavaClassHierarchy) namespace).parse(name, value); 220 } 221 if (name.isSet()) { 222 bindSetEntry((NamedParameterNode) name, value); 223 } else { 224 namedParameters.put(name, value); 225 } 226 } 227 228 @SuppressWarnings("unchecked") 229 @Override 230 public void bindSetEntry(final String iface, final String impl) 231 throws BindException { 232 boundSetEntries.put((NamedParameterNode<Set<?>>) namespace.getNode(iface), impl); 233 } 234 235 @SuppressWarnings("unchecked") 236 @Override 237 public void bindSetEntry(final String iface, final Node impl) 238 throws BindException { 239 boundSetEntries.put((NamedParameterNode<Set<?>>) namespace.getNode(iface), impl); 240 } 241 242 @SuppressWarnings("unchecked") 243 @Override 244 public <T> void bindSetEntry(final NamedParameterNode<Set<T>> iface, final String impl) 245 throws BindException { 246 if (namespace instanceof ClassHierarchyImpl) { 247 final JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace; 248 try { 249 javanamespace.parse(iface, impl); 250 } catch (final ParseException e) { 251 throw new IllegalStateException("Could not parse " + impl + " which was passed to " + iface, e); 252 } 253 } 254 boundSetEntries.put((NamedParameterNode<Set<?>>) (NamedParameterNode<?>) iface, impl); 255 } 256 257 @SuppressWarnings("unchecked") 258 @Override 259 public <T> void bindSetEntry(final NamedParameterNode<Set<T>> iface, final Node impl) 260 throws BindException { 261 boundSetEntries.put((NamedParameterNode<Set<?>>) (NamedParameterNode<?>) iface, impl); 262 } 263 264 @SuppressWarnings("unchecked") 265 @Override 266 public <T> void bindList(final NamedParameterNode<List<T>> iface, final List implList) { 267 // Check parsability of list items 268 for (final Object item : implList) { 269 if (item instanceof String) { 270 final JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace; 271 try { 272 // Just for parsability checking. 273 javanamespace.parse(iface, (String) item); 274 } catch (final ParseException e) { 275 throw new IllegalStateException("Could not parse " + item + " which was passed to " + iface, e); 276 } 277 } 278 } 279 boundLists.put((NamedParameterNode<List<?>>) (NamedParameterNode<?>) iface, implList); 280 } 281 282 @SuppressWarnings("unchecked") 283 @Override 284 public void bindList(final String iface, final List implList) { 285 final NamedParameterNode<List<?>> ifaceNode = (NamedParameterNode<List<?>>) namespace.getNode(iface); 286 // Check parsability of list items 287 for (final Object item : implList) { 288 if (item instanceof String) { 289 final JavaClassHierarchy javanamespace = (ClassHierarchyImpl) namespace; 290 try { 291 // Just for parsability checking. 292 javanamespace.parse(ifaceNode, (String) item); 293 } catch (final ParseException e) { 294 throw new IllegalStateException("Could not parse " + item + " which was passed to " + iface, e); 295 } 296 } 297 } 298 boundLists.put(ifaceNode, implList); 299 } 300 301 @Override 302 public <T> void bindConstructor(final ClassNode<T> k, 303 final ClassNode<? extends ExternalConstructor<? extends T>> v) { 304 boundConstructors.put(k, v); 305 } 306 307 @Override 308 public ConfigurationImpl build() { 309 return new ConfigurationImpl(new ConfigurationBuilderImpl(this)); 310 } 311 312 @Override 313 public String classPrettyDefaultString(final String longName) throws NameResolutionException { 314 final NamedParameterNode<?> param = (NamedParameterNode<?>) namespace 315 .getNode(longName); 316 return param.getSimpleArgName() + "=" + join(",", param.getDefaultInstanceAsStrings()); 317 } 318 319 private String join(final String sep, final String[] s) { 320 if (s.length == 0) { 321 return null; 322 } else { 323 final StringBuilder sb = new StringBuilder(s[0]); 324 for (int i = 1; i < s.length; i++) { 325 sb.append(sep); 326 sb.append(s[i]); 327 } 328 return sb.toString(); 329 } 330 } 331 332 @Override 333 public String classPrettyDescriptionString(final String fullName) 334 throws NameResolutionException { 335 final NamedParameterNode<?> param = (NamedParameterNode<?>) namespace 336 .getNode(fullName); 337 return param.getDocumentation() + "\n" + param.getFullName(); 338 } 339 340 @Override 341 public boolean equals(final Object o) { 342 if (this == o) { 343 return true; 344 } 345 if (o == null || getClass() != o.getClass()) { 346 return false; 347 } 348 349 final ConfigurationBuilderImpl that = (ConfigurationBuilderImpl) o; 350 351 if (boundConstructors != null ? 352 !boundConstructors.equals(that.boundConstructors) : that.boundConstructors != null) { 353 return false; 354 } 355 if (boundImpls != null ? !boundImpls.equals(that.boundImpls) : that.boundImpls != null) { 356 return false; 357 } 358 if (boundSetEntries != null ? !boundSetEntries.equals(that.boundSetEntries) : that.boundSetEntries != null) { 359 return false; 360 } 361 if (boundLists != null ? !boundLists.equals(that.boundLists) : that.boundLists != null) { 362 return false; 363 } 364 if (legacyConstructors != null ? 365 !legacyConstructors.equals(that.legacyConstructors) : that.legacyConstructors != null) { 366 return false; 367 } 368 if (namedParameters != null ? !namedParameters.equals(that.namedParameters) : that.namedParameters != null) { 369 return false; 370 } 371 372 return true; 373 } 374 375 @Override 376 public int hashCode() { 377 int result = boundImpls != null ? boundImpls.hashCode() : 0; 378 result = 31 * result + (boundConstructors != null ? boundConstructors.hashCode() : 0); 379 result = 31 * result + (namedParameters != null ? namedParameters.hashCode() : 0); 380 result = 31 * result + (legacyConstructors != null ? legacyConstructors.hashCode() : 0); 381 result = 31 * result + (boundSetEntries != null ? boundSetEntries.hashCode() : 0); 382 result = 31 * result + (boundLists != null ? boundLists.hashCode() : 0); 383 return result; 384 } 385}