• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.uber.nullaway.jarinfer;
2 
3 import com.google.common.collect.ImmutableList;
4 import com.google.common.collect.ImmutableSet;
5 import java.io.DataOutputStream;
6 import java.io.IOException;
7 import java.util.ArrayList;
8 import java.util.Collection;
9 import java.util.LinkedHashMap;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Set;
13 
14 /** Simple writer for the astubx format. */
15 final class StubxWriter {
16   /**
17    * The file magic number for version 0 .astubx files. It should be the first four bytes of any
18    * compatible .astubx file.
19    */
20   private static final int VERSION_0_FILE_MAGIC_NUMBER = 691458791;
21 
22   /**
23    * This method writes the provided list of annotations to a DataOutputStream in the astubx format.
24    *
25    * @param out Output stream.
26    * @param importedAnnotations Mapping of 'custom annotations' to their 'definition classes'.
27    * @param packageAnnotations Map of 'package names' to their 'list of package-level annotations'.
28    * @param typeAnnotations Map of 'type names' to their 'list of type annotations'.
29    * @param methodRecords Map of 'method signatures' to their 'method annotations record'. Method
30    *     annotations record consists of return value annotations and argument annotations. {@link
31    *     MethodAnnotationsRecord}
32    * @exception IOException On output error.
33    */
write( DataOutputStream out, Map<String, String> importedAnnotations, Map<String, Set<String>> packageAnnotations, Map<String, Set<String>> typeAnnotations, Map<String, MethodAnnotationsRecord> methodRecords)34   static void write(
35       DataOutputStream out,
36       Map<String, String> importedAnnotations,
37       Map<String, Set<String>> packageAnnotations,
38       Map<String, Set<String>> typeAnnotations,
39       Map<String, MethodAnnotationsRecord> methodRecords)
40       throws IOException {
41     // File format version/magic number
42     out.writeInt(VERSION_0_FILE_MAGIC_NUMBER);
43     // Followed by the number of string dictionary entries
44     int numStringEntries = 0;
45     Map<String, Integer> encodingDictionary = new LinkedHashMap<>();
46     List<String> strings = new ArrayList<String>();
47     List<Collection<String>> keysets =
48         ImmutableList.of(
49             importedAnnotations.values(),
50             packageAnnotations.keySet(),
51             typeAnnotations.keySet(),
52             methodRecords.keySet());
53     for (Collection<String> keyset : keysets) {
54       for (String key : keyset) {
55         assert !encodingDictionary.containsKey(key);
56         strings.add(key);
57         encodingDictionary.put(key, numStringEntries);
58         ++numStringEntries;
59       }
60     }
61     out.writeInt(numStringEntries);
62     // Followed by the entries themselves
63     for (String s : strings) {
64       out.writeUTF(s);
65     }
66     // Followed by the number of encoded package annotation records
67     int packageAnnotationSize = 0;
68     for (Map.Entry<String, Set<String>> entry : packageAnnotations.entrySet()) {
69       packageAnnotationSize += entry.getValue().size();
70     }
71     out.writeInt(packageAnnotationSize);
72     // Followed by those records as pairs of ints pointing into the dictionary
73     for (Map.Entry<String, Set<String>> entry : packageAnnotations.entrySet()) {
74       for (String annot : entry.getValue()) {
75         out.writeInt(encodingDictionary.get(entry.getKey()));
76         out.writeInt(encodingDictionary.get(importedAnnotations.get(annot)));
77       }
78     }
79     // Followed by the number of encoded type annotation records
80     int typeAnnotationSize = 0;
81     for (Map.Entry<String, Set<String>> entry : typeAnnotations.entrySet()) {
82       typeAnnotationSize += entry.getValue().size();
83     }
84     out.writeInt(typeAnnotationSize);
85     // Followed by those records as pairs of ints pointing into the dictionary
86     for (Map.Entry<String, Set<String>> entry : typeAnnotations.entrySet()) {
87       for (String annot : entry.getValue()) {
88         out.writeInt(encodingDictionary.get(entry.getKey()));
89         out.writeInt(encodingDictionary.get(importedAnnotations.get(annot)));
90       }
91     }
92     // Followed by the number of encoded method return/declaration annotation records
93     int methodAnnotationSize = 0;
94     int methodArgumentRecordsSize = 0;
95     for (Map.Entry<String, MethodAnnotationsRecord> entry : methodRecords.entrySet()) {
96       methodAnnotationSize += entry.getValue().getMethodAnnotations().size();
97       methodArgumentRecordsSize += entry.getValue().getArgumentAnnotations().size();
98     }
99     out.writeInt(methodAnnotationSize);
100     // Followed by those records as pairs of ints pointing into the dictionary
101     for (Map.Entry<String, MethodAnnotationsRecord> entry : methodRecords.entrySet()) {
102       for (String annot : entry.getValue().getMethodAnnotations()) {
103         out.writeInt(encodingDictionary.get(entry.getKey()));
104         out.writeInt(encodingDictionary.get(importedAnnotations.get(annot)));
105       }
106     }
107     // Followed by the number of encoded method argument annotation records
108     out.writeInt(methodArgumentRecordsSize);
109     // Followed by those records as a triplet of ints ( 0 and 2 point in the dictionary, 1 is the
110     //  argument position)
111     for (Map.Entry<String, MethodAnnotationsRecord> entry : methodRecords.entrySet()) {
112       for (Map.Entry<Integer, ImmutableSet<String>> argEntry :
113           entry.getValue().getArgumentAnnotations().entrySet()) {
114         for (String annot : argEntry.getValue()) {
115           out.writeInt(encodingDictionary.get(entry.getKey()));
116           out.writeInt(argEntry.getKey());
117           out.writeInt(encodingDictionary.get(importedAnnotations.get(annot)));
118         }
119       }
120     }
121   }
122 }
123