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.protobuf; 020 021import org.apache.reef.tang.ClassHierarchy; 022import org.apache.reef.tang.annotations.NamedParameter; 023import org.apache.reef.tang.exceptions.NameResolutionException; 024import org.apache.reef.tang.implementation.types.*; 025import org.apache.reef.tang.proto.ClassHierarchyProto; 026import org.apache.reef.tang.types.*; 027 028import java.io.*; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.HashMap; 032import java.util.List; 033 034/** 035 * @deprecated in 0.12. Use AvroClassHierarchy instead 036 */ 037@Deprecated 038public class ProtocolBufferClassHierarchy implements ClassHierarchy { 039 040 private final PackageNode namespace; 041 private HashMap<String, Node> lookupTable = new HashMap<>(); 042 043 // ############## Serialize implementation ############## 044 045 // protoc doesn't believe in auto-generating constructors, so here are 046 // hand-generated ones. *sigh* 047 048 /** 049 * Deserialize a class hierarchy from a protocol buffer object. The resulting 050 * object is immutable, and does not make use of reflection to fill in any 051 * missing values. This allows it to represent non-native classes as well 052 * as snapshots of Java class hierarchies. 053 * @deprecated in 0.12. Use AvroClassHierarchy instead 054 */ 055 public ProtocolBufferClassHierarchy(final ClassHierarchyProto.Node root) { 056 namespace = new PackageNodeImpl(); 057 if (!root.hasPackageNode()) { 058 throw new IllegalArgumentException("Expected a package node. Got: " 059 + root); 060 } 061 // Register all the classes. 062 for (final ClassHierarchyProto.Node child : root.getChildrenList()) { 063 parseSubHierarchy(namespace, child); 064 } 065 buildLookupTable(namespace); 066 // Now, register the implementations 067 for (final ClassHierarchyProto.Node child : root.getChildrenList()) { 068 wireUpInheritanceRelationships(child); 069 } 070 } 071 072 private static ClassHierarchyProto.Node newClassNode( 073 final String name, final String fullName, final boolean isInjectionCandidate, 074 final boolean isExternalConstructor, final boolean isUnit, 075 final List<ClassHierarchyProto.ConstructorDef> injectableConstructors, 076 final List<ClassHierarchyProto.ConstructorDef> otherConstructors, final List<String> implFullNames, 077 final Iterable<ClassHierarchyProto.Node> children) { 078 return ClassHierarchyProto.Node 079 .newBuilder() 080 .setName(name) 081 .setFullName(fullName) 082 .setClassNode( 083 ClassHierarchyProto.ClassNode.newBuilder() 084 .setIsInjectionCandidate(isInjectionCandidate) 085 .setIsExternalConstructor(isExternalConstructor) 086 .setIsUnit(isUnit) 087 .addAllInjectableConstructors(injectableConstructors) 088 .addAllOtherConstructors(otherConstructors) 089 .addAllImplFullNames(implFullNames).build()) 090 .addAllChildren(children).build(); 091 } 092 093 private static ClassHierarchyProto.Node newNamedParameterNode(final String name, 094 final String fullName, 095 final String simpleArgClassName, 096 final String fullArgClassName, 097 final boolean isSet, 098 final boolean isList, 099 final String documentation, // can be null 100 final String shortName, // can be null 101 final String[] instanceDefault, // can be null 102 final Iterable<ClassHierarchyProto.Node> children) { 103 final ClassHierarchyProto.NamedParameterNode.Builder namedParameterNodeBuilder 104 = ClassHierarchyProto.NamedParameterNode.newBuilder() 105 .setSimpleArgClassName(simpleArgClassName) 106 .setFullArgClassName(fullArgClassName) 107 .setIsSet(isSet) 108 .setIsList(isList); 109 if (documentation != null) { 110 namedParameterNodeBuilder.setDocumentation(documentation); 111 } 112 if (shortName != null) { 113 namedParameterNodeBuilder.setShortName(shortName); 114 } 115 if (instanceDefault != null) { 116 namedParameterNodeBuilder.addAllInstanceDefault(Arrays.asList(instanceDefault)); 117 } 118 119 return ClassHierarchyProto.Node.newBuilder().setName(name) 120 .setFullName(fullName) 121 .setNamedParameterNode(namedParameterNodeBuilder.build()) 122 .addAllChildren(children).build(); 123 } 124 125 private static ClassHierarchyProto.Node newPackageNode(final String name, 126 final String fullName, 127 final Iterable<ClassHierarchyProto.Node> children) { 128 return ClassHierarchyProto.Node.newBuilder() 129 .setPackageNode(ClassHierarchyProto.PackageNode.newBuilder().build()) 130 .setName(name).setFullName(fullName).addAllChildren(children).build(); 131 } 132 133 private static ClassHierarchyProto.ConstructorDef newConstructorDef( 134 final String fullClassName, final List<ClassHierarchyProto.ConstructorArg> args) { 135 return ClassHierarchyProto.ConstructorDef.newBuilder() 136 .setFullClassName(fullClassName).addAllArgs(args).build(); 137 } 138 139 // these serialize...() methods copy a pieces of the class hierarchy into 140 // protobufs 141 142 private static ClassHierarchyProto.ConstructorArg newConstructorArg( 143 final String fullArgClassName, final String namedParameterName, final boolean isFuture) { 144 final ClassHierarchyProto.ConstructorArg.Builder builder = 145 ClassHierarchyProto.ConstructorArg.newBuilder() 146 .setFullArgClassName(fullArgClassName) 147 .setIsInjectionFuture(isFuture); 148 if (namedParameterName != null) { 149 builder.setNamedParameterName(namedParameterName).build(); 150 } 151 return builder.build(); 152 } 153 154 private static ClassHierarchyProto.ConstructorDef serializeConstructorDef( 155 final ConstructorDef<?> def) { 156 final List<ClassHierarchyProto.ConstructorArg> args = new ArrayList<>(); 157 for (final ConstructorArg arg : def.getArgs()) { 158 args.add(newConstructorArg(arg.getType(), arg.getNamedParameterName(), arg.isInjectionFuture())); 159 } 160 return newConstructorDef(def.getClassName(), args); 161 } 162 163 private static ClassHierarchyProto.Node serializeNode(final Node n) { 164 final List<ClassHierarchyProto.Node> children = new ArrayList<>(); 165 for (final Node child : n.getChildren()) { 166 children.add(serializeNode(child)); 167 } 168 if (n instanceof ClassNode) { 169 final ClassNode<?> cn = (ClassNode<?>) n; 170 final ConstructorDef<?>[] injectable = cn.getInjectableConstructors(); 171 final ConstructorDef<?>[] all = cn.getAllConstructors(); 172 final List<ConstructorDef<?>> others = new ArrayList<>(Arrays.asList(all)); 173 others.removeAll(Arrays.asList(injectable)); 174 175 final List<ClassHierarchyProto.ConstructorDef> injectableConstructors = new ArrayList<>(); 176 for (final ConstructorDef<?> inj : injectable) { 177 injectableConstructors.add(serializeConstructorDef(inj)); 178 } 179 final List<ClassHierarchyProto.ConstructorDef> otherConstructors = new ArrayList<>(); 180 for (final ConstructorDef<?> other : others) { 181 otherConstructors.add(serializeConstructorDef(other)); 182 } 183 final List<String> implFullNames = new ArrayList<>(); 184 for (final ClassNode<?> impl : cn.getKnownImplementations()) { 185 implFullNames.add(impl.getFullName()); 186 } 187 return newClassNode(cn.getName(), cn.getFullName(), 188 cn.isInjectionCandidate(), cn.isExternalConstructor(), cn.isUnit(), 189 injectableConstructors, otherConstructors, implFullNames, children); 190 } else if (n instanceof NamedParameterNode) { 191 final NamedParameterNode<?> np = (NamedParameterNode<?>) n; 192 return newNamedParameterNode(np.getName(), np.getFullName(), 193 np.getSimpleArgName(), np.getFullArgName(), np.isSet(), np.isList(), np.getDocumentation(), 194 np.getShortName(), np.getDefaultInstanceAsStrings(), children); 195 } else if (n instanceof PackageNode) { 196 return newPackageNode(n.getName(), n.getFullName(), children); 197 } else { 198 throw new IllegalStateException("Encountered unknown type of Node: " + n); 199 } 200 } 201 202 /** 203 * Serialize a class hierarchy into a protocol buffer object. 204 * 205 * @param classHierarchy 206 * @return 207 * @deprecated in 0.12. Use AvroClassHierarchySerializer instead 208 */ 209 public static ClassHierarchyProto.Node serialize(final ClassHierarchy classHierarchy) { 210 return serializeNode(classHierarchy.getNamespace()); 211 } 212 213 /** 214 * serialize a class hierarchy into a file. 215 * 216 * @param file 217 * @param classHierarchy 218 * @throws IOException 219 * @deprecated in 0.12. Use AvroClassHierarchySerializer instead 220 */ 221 public static void serialize(final File file, final ClassHierarchy classHierarchy) throws IOException { 222 final ClassHierarchyProto.Node node = serializeNode(classHierarchy.getNamespace()); 223 try (final FileOutputStream output = new FileOutputStream(file)) { 224 try (final DataOutputStream dos = new DataOutputStream(output)) { 225 node.writeTo(dos); 226 } 227 } 228 } 229 230 /** 231 * Deserialize a class hierarchy from a file. The file can be generated from either Java or C# 232 * 233 * @param file 234 * @return 235 * @throws IOException 236 * @deprecated in 0.12. Use AvroClassHierarchySerializer instead 237 */ 238 public static ClassHierarchy deserialize(final File file) throws IOException { 239 try (final InputStream stream = new FileInputStream(file)) { 240 final ClassHierarchyProto.Node root = ClassHierarchyProto.Node.parseFrom(stream); 241 return new ProtocolBufferClassHierarchy(root); 242 } 243 } 244 245 private static void parseSubHierarchy(final Node parent, final ClassHierarchyProto.Node n) { 246 final Node parsed; 247 if (n.hasPackageNode()) { 248 parsed = new PackageNodeImpl(parent, n.getName(), n.getFullName()); 249 } else if (n.hasNamedParameterNode()) { 250 final ClassHierarchyProto.NamedParameterNode np = n.getNamedParameterNode(); 251 parsed = new NamedParameterNodeImpl<Object>(parent, n.getName(), 252 n.getFullName(), np.getFullArgClassName(), np.getSimpleArgClassName(), 253 np.getIsSet(), np.getIsList(), np.getDocumentation(), np.getShortName(), 254 np.getInstanceDefaultList().toArray(new String[0])); 255 } else if (n.hasClassNode()) { 256 final ClassHierarchyProto.ClassNode cn = n.getClassNode(); 257 final List<ConstructorDef<?>> injectableConstructors = new ArrayList<>(); 258 final List<ConstructorDef<?>> allConstructors = new ArrayList<>(); 259 260 for (final ClassHierarchyProto.ConstructorDef injectable : cn 261 .getInjectableConstructorsList()) { 262 final ConstructorDef<?> def = parseConstructorDef(injectable, true); 263 injectableConstructors.add(def); 264 allConstructors.add(def); 265 } 266 for (final ClassHierarchyProto.ConstructorDef other : cn 267 .getOtherConstructorsList()) { 268 final ConstructorDef<?> def = parseConstructorDef(other, false); 269 allConstructors.add(def); 270 271 } 272 @SuppressWarnings("unchecked") final ConstructorDef<Object>[] dummy = new ConstructorDef[0]; 273 parsed = new ClassNodeImpl<>(parent, n.getName(), n.getFullName(), 274 cn.getIsUnit(), cn.getIsInjectionCandidate(), 275 cn.getIsExternalConstructor(), injectableConstructors.toArray(dummy), 276 allConstructors.toArray(dummy), cn.getDefaultImplementation()); 277 } else { 278 throw new IllegalStateException("Bad protocol buffer: got abstract node" 279 + n); 280 } 281 for (final ClassHierarchyProto.Node child : n.getChildrenList()) { 282 parseSubHierarchy(parsed, child); 283 } 284 } 285 286 private static ConstructorDef<?> parseConstructorDef( 287 final ClassHierarchyProto.ConstructorDef def, 288 final boolean isInjectable) { 289 final List<ConstructorArg> args = new ArrayList<>(); 290 for (final ClassHierarchyProto.ConstructorArg arg : def.getArgsList()) { 291 args.add(new ConstructorArgImpl(arg.getFullArgClassName(), arg 292 .getNamedParameterName(), arg.getIsInjectionFuture())); 293 } 294 return new ConstructorDefImpl<>(def.getFullClassName(), 295 args.toArray(new ConstructorArg[0]), isInjectable); 296 } 297 298 private static String getNthPrefix(final String str, final int n) { 299 int j = n; 300 j++; // want this function to be zero indexed... 301 for (int i = 0; i < str.length(); i++) { 302 final char c = str.charAt(i); 303 if (c == '.' || c == '$' || c == '+') { 304 j--; 305 } 306 if (j == 0) { 307 return str.substring(0, i); 308 } 309 } 310 if (j == 1) { 311 return str; 312 } else { 313 throw new ArrayIndexOutOfBoundsException(); 314 } 315 } 316 317 private void buildLookupTable(final Node n) { 318 for (final Node child : n.getChildren()) { 319 lookupTable.put(child.getFullName(), child); 320 buildLookupTable(child); 321 } 322 } 323 324 @SuppressWarnings({"rawtypes", "unchecked"}) 325 private void wireUpInheritanceRelationships(final ClassHierarchyProto.Node n) { 326 if (n.hasClassNode()) { 327 final ClassHierarchyProto.ClassNode cn = n.getClassNode(); 328 final ClassNode iface; 329 try { 330 iface = (ClassNode) getNode(n.getFullName()); 331 } catch (final NameResolutionException e) { 332 throw new IllegalStateException("When reading protocol buffer node " 333 + n.getFullName() + " does not exist. Full record is " + n, e); 334 } 335 for (final String impl : cn.getImplFullNamesList()) { 336 try { 337 iface.putImpl((ClassNode) getNode(impl)); 338 } catch (final NameResolutionException e) { 339 throw new IllegalStateException("When reading protocol buffer node " 340 + n + " refers to non-existent implementation:" + impl, e); 341 } catch (final ClassCastException e) { 342 try { 343 throw new IllegalStateException( 344 "When reading protocol buffer node " + n 345 + " found implementation" + getNode(impl) 346 + " which is not a ClassNode!"); 347 } catch (final NameResolutionException e2) { 348 throw new IllegalStateException( 349 "Got 'cant happen' exception when producing error message for " 350 + e, e2); 351 } 352 } 353 } 354 } 355 356 for (final ClassHierarchyProto.Node child : n.getChildrenList()) { 357 wireUpInheritanceRelationships(child); 358 } 359 } 360 361 @Override 362 public Node getNode(final String fullName) throws NameResolutionException { 363 364 final Node ret = lookupTable.get(fullName); 365 if (ret != null) { 366 return ret; 367 } else { 368 throw new NameResolutionException(fullName, ""); 369 } 370 } 371 372 @Override 373 public boolean isImplementation(final ClassNode<?> inter, final ClassNode<?> impl) { 374 return impl.isImplementationOf(inter); 375 } 376 377 @Override 378 public ClassHierarchy merge(final ClassHierarchy ch) { 379 if (this == ch) { 380 return this; 381 } 382 if (!(ch instanceof ProtocolBufferClassHierarchy)) { 383 throw new UnsupportedOperationException( 384 "Cannot merge with class hierarchies of type: " + ch.getClass().getName()); 385 } 386 387 final ProtocolBufferClassHierarchy pch = (ProtocolBufferClassHierarchy) ch; 388 for (final String key : pch.lookupTable.keySet()) { 389 if (!this.lookupTable.containsKey(key)) { 390 this.lookupTable.put(key, pch.lookupTable.get(key)); 391 } 392 393 for (final Node n : ch.getNamespace().getChildren()) { 394 if (!this.namespace.contains(n.getFullName())) { 395 if (n instanceof NamedParameter) { 396 final NamedParameterNode np = (NamedParameterNode) n; 397 new NamedParameterNodeImpl<>(this.namespace, np.getName(), 398 np.getFullName(), np.getFullArgName(), np.getSimpleArgName(), 399 np.isSet(), np.isList(), np.getDocumentation(), np.getShortName(), 400 np.getDefaultInstanceAsStrings()); 401 } else if (n instanceof ClassNode) { 402 final ClassNode cn = (ClassNode) n; 403 new ClassNodeImpl(namespace, cn.getName(), cn.getFullName(), 404 cn.isUnit(), cn.isInjectionCandidate(), 405 cn.isExternalConstructor(), cn.getInjectableConstructors(), 406 cn.getAllConstructors(), cn.getDefaultImplementation()); 407 } 408 } 409 } 410 } 411 return this; 412 } 413 414 @Override 415 public Node getNamespace() { 416 return namespace; 417 } 418}