This project has retired. For details please refer to its Attic page.
Source code
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("The value of j should be 1, but actually is " + j);
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}