blob: 5d2926821164043db18cc3713484c095785cf620 [file] [log] [blame]
Yao Chend54f9dd2017-10-17 17:37:48 +00001/*
2 * Copyright (C) 2017, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "Collation.h"
18
19#include <stdio.h>
20#include <map>
21
22namespace android {
23namespace stats_log_api_gen {
24
25using google::protobuf::FieldDescriptor;
26using google::protobuf::FileDescriptor;
27using google::protobuf::SourceLocation;
28using std::map;
29
30
31//
32// AtomDecl class
33//
34
35AtomDecl::AtomDecl()
36 :code(0),
37 name()
38{
39}
40
41AtomDecl::AtomDecl(const AtomDecl& that)
42 :code(that.code),
43 name(that.name),
44 message(that.message),
45 fields(that.fields)
46{
47}
48
49AtomDecl::AtomDecl(int c, const string& n, const string& m)
50 :code(c),
51 name(n),
52 message(m)
53{
54}
55
56AtomDecl::~AtomDecl()
57{
58}
59
60
61/**
62 * Print an error message for a FieldDescriptor, including the file name and line number.
63 */
64static void
65print_error(const FieldDescriptor* field, const char* format, ...)
66{
67 const Descriptor* message = field->containing_type();
68 const FileDescriptor* file = message->file();
69
70 SourceLocation loc;
71 if (field->GetSourceLocation(&loc)) {
72 // TODO: this will work if we can figure out how to pass --include_source_info to protoc
73 fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
74 } else {
75 fprintf(stderr, "%s: ", file->name().c_str());
76 }
77 va_list args;
78 va_start(args, format);
79 vfprintf(stderr, format, args);
80 va_end (args);
81}
82
83/**
84 * Convert a protobuf type into a java type.
85 */
86static java_type_t
87java_type(const FieldDescriptor* field)
88{
89 int protoType = field->type();
90 switch (protoType) {
91 case FieldDescriptor::TYPE_DOUBLE:
92 return JAVA_TYPE_DOUBLE;
93 case FieldDescriptor::TYPE_FLOAT:
94 return JAVA_TYPE_FLOAT;
95 case FieldDescriptor::TYPE_INT64:
96 return JAVA_TYPE_LONG;
97 case FieldDescriptor::TYPE_UINT64:
98 return JAVA_TYPE_LONG;
99 case FieldDescriptor::TYPE_INT32:
100 return JAVA_TYPE_INT;
101 case FieldDescriptor::TYPE_FIXED64:
102 return JAVA_TYPE_LONG;
103 case FieldDescriptor::TYPE_FIXED32:
104 return JAVA_TYPE_INT;
105 case FieldDescriptor::TYPE_BOOL:
106 return JAVA_TYPE_BOOLEAN;
107 case FieldDescriptor::TYPE_STRING:
108 return JAVA_TYPE_STRING;
109 case FieldDescriptor::TYPE_GROUP:
110 return JAVA_TYPE_UNKNOWN;
111 case FieldDescriptor::TYPE_MESSAGE:
112 // TODO: not the final package name
113 if (field->message_type()->full_name() == "android.os.statsd.WorkSource") {
114 return JAVA_TYPE_WORK_SOURCE;
115 } else {
116 return JAVA_TYPE_OBJECT;
117 }
118 case FieldDescriptor::TYPE_BYTES:
119 return JAVA_TYPE_BYTE_ARRAY;
120 case FieldDescriptor::TYPE_UINT32:
121 return JAVA_TYPE_INT;
122 case FieldDescriptor::TYPE_ENUM:
123 return JAVA_TYPE_INT;
124 case FieldDescriptor::TYPE_SFIXED32:
125 return JAVA_TYPE_INT;
126 case FieldDescriptor::TYPE_SFIXED64:
127 return JAVA_TYPE_LONG;
128 case FieldDescriptor::TYPE_SINT32:
129 return JAVA_TYPE_INT;
130 case FieldDescriptor::TYPE_SINT64:
131 return JAVA_TYPE_LONG;
132 default:
133 return JAVA_TYPE_UNKNOWN;
134 }
135}
136
137/**
138 * Gather the info about the atoms.
139 */
140int
141collate_atoms(const Descriptor* descriptor, Atoms* atoms)
142{
143 int errorCount = 0;
144 const bool dbg = false;
145
146 for (int i=0; i<descriptor->field_count(); i++) {
147 const FieldDescriptor* atomField = descriptor->field(i);
148
149 if (dbg) {
150 printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
151 }
152
153 // StatsEvent only has one oneof, which contains only messages. Don't allow other types.
154 if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
155 print_error(atomField,
156 "Bad type for atom. StatsEvent can only have message type fields: %s\n",
157 atomField->name().c_str());
158 errorCount++;
159 continue;
160 }
161
162 const Descriptor* atom = atomField->message_type();
163
164 // Build a sorted list of the fields. Descriptor has them in source file order.
165 map<int,const FieldDescriptor*> fields;
166 for (int j=0; j<atom->field_count(); j++) {
167 const FieldDescriptor* field = atom->field(j);
168 fields[field->number()] = field;
169 }
170
171 // Check that the parameters start at 1 and go up sequentially.
172 int expectedNumber = 1;
173 for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
174 it != fields.end(); it++) {
175 const int number = it->first;
176 const FieldDescriptor* field = it->second;
177 if (number != expectedNumber) {
178 print_error(field, "Fields must be numbered consecutively starting at 1:"
179 " '%s' is %d but should be %d\n",
180 field->name().c_str(), number, expectedNumber);
181 errorCount++;
182 expectedNumber = number;
183 continue;
184 }
185 expectedNumber++;
186 }
187
188 // Check that only allowed types are present. Remove any invalid ones.
189 for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
190 it != fields.end(); it++) {
191 const FieldDescriptor* field = it->second;
192
193 java_type_t javaType = java_type(field);
194
195 if (javaType == JAVA_TYPE_UNKNOWN) {
196 print_error(field, "Unkown type for field: %s\n", field->name().c_str());
197 errorCount++;
198 continue;
199 } else if (javaType == JAVA_TYPE_OBJECT) {
200 // Allow WorkSources, but only at position 1.
201 print_error(field, "Message type not allowed for field: %s\n",
202 field->name().c_str());
203 errorCount++;
204 continue;
205 } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
206 print_error(field, "Raw bytes type not allowed for field: %s\n",
207 field->name().c_str());
208 errorCount++;
209 continue;
210 }
211
212 }
213
214 // Check that if there's a WorkSource, it's at position 1.
215 for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
216 it != fields.end(); it++) {
217 int number = it->first;
218 if (number != 1) {
219 const FieldDescriptor* field = it->second;
220 java_type_t javaType = java_type(field);
221 if (javaType == JAVA_TYPE_WORK_SOURCE) {
222 print_error(field, "WorkSource fields must have field id 1, in message: '%s'\n",
223 atom->name().c_str());
224 errorCount++;
225 }
226 }
227 }
228
229 AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
230
231 // Build the type signature
232 vector<java_type_t> signature;
233 for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
234 it != fields.end(); it++) {
235 const FieldDescriptor* field = it->second;
236 java_type_t javaType = java_type(field);
237
238 atomDecl.fields.push_back(AtomField(field->name(), javaType));
239 signature.push_back(javaType);
240 }
241
242 atoms->signatures.insert(signature);
243 atoms->decls.insert(atomDecl);
244 }
245
246 if (dbg) {
247 printf("signatures = [\n");
248 for (set<vector<java_type_t>>::const_iterator it = atoms->signatures.begin();
249 it != atoms->signatures.end(); it++) {
250 printf(" ");
251 for (vector<java_type_t>::const_iterator jt = it->begin(); jt != it->end(); jt++) {
252 printf(" %d", (int)*jt);
253 }
254 printf("\n");
255 }
256 printf("]\n");
257 }
258
259 return errorCount;
260}
261
262} // namespace stats_log_api_gen
263} // namespace android
264
265