Collation.cpp revision b81d1a7b9a38dcb4d356ae3435a82fb52ba7d585
1/* 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 266