Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * Data structures representing an API definition, and visitor base classes | |
| 7 * for visiting those data structures. | |
| 8 */ | |
| 9 library api; | |
| 10 | |
| 11 import 'dart:collection'; | |
| 12 | |
| 13 import 'package:html5lib/dom.dart' as dom; | |
| 14 | |
| 15 /** | |
| 16 * Base class for visiting the API definition. | |
| 17 */ | |
| 18 abstract class ApiVisitor<T> { | |
| 19 T visitTypeReference(TypeReference typeReference); | |
| 20 T visitTypeObject(TypeObject typeObject); | |
| 21 T visitTypeList(TypeList typeList); | |
| 22 T visitTypeMap(TypeMap typeMap); | |
| 23 T visitTypeEnum(TypeEnum typeEnum); | |
| 24 | |
| 25 /** | |
| 26 * Dispatch the given [node] to the visitor. | |
|
scheglov
2014/08/06 18:20:39
[node] -> [type]
Paul Berry
2014/08/06 18:32:43
Done.
| |
| 27 */ | |
| 28 T visitTypeDecl(TypeDecl type) => type.accept(this); | |
| 29 } | |
| 30 | |
| 31 /** | |
| 32 * API visitor that visits the entire API hierarchically by default. | |
| 33 */ | |
| 34 class HierarchicalApiVisitor extends ApiVisitor { | |
| 35 /** | |
| 36 * The API to visit. | |
| 37 */ | |
| 38 final Api api; | |
| 39 | |
| 40 HierarchicalApiVisitor(this.api); | |
| 41 | |
| 42 void visitApi() { | |
| 43 api.domains.forEach(visitDomain); | |
| 44 visitTypes(api.types); | |
| 45 visitRefactorings(api.refactorings); | |
| 46 } | |
| 47 | |
| 48 void visitRefactorings(Refactorings refactorings) { | |
| 49 refactorings.forEach(visitRefactoring); | |
| 50 } | |
| 51 | |
| 52 void visitRefactoring(Refactoring refactoring) { | |
| 53 if (refactoring.feedback != null) { | |
| 54 visitTypeDecl(refactoring.feedback); | |
| 55 } | |
| 56 if (refactoring.options != null) { | |
| 57 visitTypeDecl(refactoring.options); | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 void visitTypes(Types types) { | |
| 62 types.forEach(visitTypeDefinition); | |
| 63 } | |
| 64 | |
| 65 void visitDomain(Domain domain) { | |
| 66 domain.requests.forEach(visitRequest); | |
| 67 domain.notifications.forEach(visitNotification); | |
| 68 } | |
| 69 | |
| 70 void visitNotification(Notification notification) { | |
| 71 if (notification.params != null) { | |
| 72 visitTypeDecl(notification.params); | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 void visitRequest(Request request) { | |
| 77 if (request.params != null) { | |
| 78 visitTypeDecl(request.params); | |
| 79 } | |
| 80 if (request.result != null) { | |
| 81 visitTypeDecl(request.result); | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 void visitTypeDefinition(TypeDefinition typeDefinition) { | |
| 86 visitTypeDecl(typeDefinition.type); | |
| 87 } | |
| 88 | |
| 89 @override | |
| 90 void visitTypeEnum(TypeEnum typeEnum) { | |
| 91 typeEnum.values.forEach(visitTypeEnumValue); | |
| 92 } | |
| 93 | |
| 94 void visitTypeEnumValue(TypeEnumValue typeEnumValue) { | |
| 95 } | |
| 96 | |
| 97 @override | |
| 98 void visitTypeList(TypeList typeList) { | |
| 99 visitTypeDecl(typeList.itemType); | |
| 100 } | |
| 101 | |
| 102 @override | |
| 103 void visitTypeMap(TypeMap typeMap) { | |
| 104 visitTypeDecl(typeMap.keyType); | |
| 105 visitTypeDecl(typeMap.valueType); | |
| 106 } | |
| 107 | |
| 108 @override | |
| 109 void visitTypeObject(TypeObject typeObject) { | |
| 110 typeObject.fields.forEach(visitTypeObjectField); | |
| 111 } | |
| 112 | |
| 113 void visitTypeObjectField(TypeObjectField typeObjectField) { | |
| 114 visitTypeDecl(typeObjectField.type); | |
| 115 } | |
| 116 | |
| 117 @override | |
| 118 void visitTypeReference(TypeReference typeReference) { | |
| 119 } | |
| 120 | |
| 121 /** | |
| 122 * If [type] is a [TypeReference] which points to another [TypeReference], | |
| 123 * follow the chain until the last [TypeReference] is reached. | |
| 124 */ | |
| 125 TypeReference resolveTypeReferenceChain(TypeReference type) { | |
| 126 while (api.types.containsKey(type.typeName)) { | |
| 127 TypeDecl referredType = api.types[type.typeName].type; | |
| 128 if (referredType is TypeReference) { | |
| 129 type = referredType; | |
| 130 continue; | |
| 131 } | |
| 132 break; | |
| 133 } | |
| 134 return type; | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Base class for objects in the API model | |
|
scheglov
2014/08/06 18:20:39
.
Paul Berry
2014/08/06 18:32:43
Done.
| |
| 140 */ | |
| 141 class ApiNode { | |
| 142 /** | |
| 143 * Html element representing this part of the API. | |
| 144 */ | |
| 145 final dom.Element html; | |
| 146 | |
| 147 ApiNode(this.html); | |
| 148 } | |
| 149 | |
| 150 /** | |
| 151 * Toplevel container for the API. | |
| 152 */ | |
| 153 class Api extends ApiNode { | |
| 154 final String version; | |
| 155 final List<Domain> domains; | |
| 156 final Types types; | |
| 157 final Refactorings refactorings; | |
| 158 | |
| 159 Api(this.version, this.domains, this.types, this.refactorings, dom.Element | |
| 160 html) : super(html); | |
| 161 } | |
| 162 | |
| 163 /** | |
| 164 * A collection of refactoring definitions. | |
| 165 */ | |
| 166 class Refactorings extends ApiNode with IterableMixin<Refactoring> { | |
| 167 final List<Refactoring> refactorings; | |
| 168 | |
| 169 Refactorings(this.refactorings, dom.Element html) : super(html); | |
| 170 | |
| 171 @override | |
| 172 Iterator<Refactoring> get iterator => refactorings.iterator; | |
| 173 } | |
| 174 | |
| 175 /** | |
| 176 * Description of a single refactoring. | |
| 177 */ | |
| 178 class Refactoring extends ApiNode { | |
| 179 /** | |
| 180 * Name of the refactoring. This should match one of the values allowed for | |
| 181 * RefactoringKind. | |
| 182 */ | |
| 183 final String kind; | |
| 184 | |
| 185 /** | |
| 186 * Type of the refactoring feedback, or null if the refactoring has no | |
| 187 * feedback. | |
| 188 */ | |
| 189 final TypeObject feedback; | |
| 190 | |
| 191 /** | |
| 192 * Type of the refactoring options, or null if the refactoring has no options. | |
| 193 */ | |
| 194 final TypeObject options; | |
| 195 | |
| 196 Refactoring(this.kind, this.feedback, this.options, dom.Element html) : super( | |
| 197 html); | |
| 198 } | |
| 199 | |
| 200 /** | |
| 201 * A collection of type definitions. | |
| 202 */ | |
| 203 class Types extends ApiNode with IterableMixin<TypeDefinition> { | |
| 204 final Map<String, TypeDefinition> types; | |
| 205 | |
| 206 Types(this.types, dom.Element html) : super(html); | |
| 207 | |
| 208 bool containsKey(String typeName) => types.containsKey(typeName); | |
| 209 | |
| 210 @override | |
| 211 Iterator<TypeDefinition> get iterator => types.values.iterator; | |
| 212 | |
| 213 TypeDefinition operator [](String typeName) => types[typeName]; | |
| 214 | |
| 215 Iterable<String> get keys => types.keys; | |
| 216 } | |
| 217 | |
| 218 /** | |
| 219 * Definition of a single domain. | |
| 220 */ | |
| 221 class Domain extends ApiNode { | |
| 222 final String name; | |
| 223 final List<Request> requests; | |
| 224 final List<Notification> notifications; | |
| 225 | |
| 226 Domain(this.name, this.requests, this.notifications, dom.Element html) : | |
| 227 super(html); | |
| 228 } | |
| 229 | |
| 230 /** | |
| 231 * Description of a request method. | |
| 232 */ | |
| 233 class Request extends ApiNode { | |
| 234 /** | |
| 235 * Name of the domain enclosing this request. | |
| 236 */ | |
| 237 final String domainName; | |
| 238 | |
| 239 /** | |
| 240 * Name of the request, without the domain prefix. | |
| 241 */ | |
| 242 final String method; | |
| 243 | |
| 244 /** | |
| 245 * Type of the object associated with the "params" key in the request object, | |
| 246 * or null if the request has no parameters. | |
| 247 */ | |
| 248 final TypeObject params; | |
| 249 | |
| 250 /** | |
| 251 * Type of the object associated with the "result" key in the response object, | |
| 252 * or null if the response has no results. | |
| 253 */ | |
| 254 final TypeObject result; | |
| 255 | |
| 256 Request(this.domainName, this.method, this.params, this.result, dom.Element | |
| 257 html) : super(html); | |
| 258 | |
| 259 /** | |
| 260 * Get the name of the request, including the domain prefix. | |
| 261 */ | |
| 262 String get longMethod => '$domainName.$method'; | |
| 263 | |
| 264 /** | |
| 265 * Get the full type of the request object, including the common "id" and | |
| 266 * "method" fields. | |
| 267 */ | |
| 268 TypeDecl get requestType { | |
| 269 List<TypeObjectField> fields = [new TypeObjectField('id', new TypeReference( | |
| 270 'String', null), null), new TypeObjectField('method', new TypeReference( | |
| 271 'String', null), null, value: '$domainName.$method')]; | |
| 272 if (params != null) { | |
| 273 fields.add(new TypeObjectField('params', params, null)); | |
| 274 } | |
| 275 return new TypeObject(fields, null); | |
| 276 } | |
| 277 | |
| 278 /** | |
| 279 * Get the full type of the response object, including the common "id" and | |
| 280 * "error" fields. | |
| 281 */ | |
| 282 TypeDecl get responseType { | |
| 283 List<TypeObjectField> fields = [new TypeObjectField('id', new TypeReference( | |
| 284 'String', null), null), new TypeObjectField('error', new TypeReference(' Error', | |
| 285 null), null, optional: true)]; | |
| 286 if (result != null) { | |
| 287 fields.add(new TypeObjectField('result', result, null)); | |
| 288 } | |
| 289 return new TypeObject(fields, null); | |
| 290 } | |
| 291 } | |
| 292 | |
| 293 /** | |
| 294 * Description of a request method. | |
| 295 */ | |
| 296 class Notification extends ApiNode { | |
| 297 /** | |
| 298 * Name of the domain enclosing this request. | |
| 299 */ | |
| 300 final String domainName; | |
| 301 | |
| 302 /** | |
| 303 * Name of the notification, without the domain prefix. | |
| 304 */ | |
| 305 final String event; | |
| 306 | |
| 307 /** | |
| 308 * Type of the object associated with the "params" key in the notification | |
| 309 * object, or null if the notification has no parameters. | |
| 310 */ | |
| 311 final TypeObject params; | |
| 312 | |
| 313 Notification(this.domainName, this.event, this.params, dom.Element html) : | |
| 314 super(html); | |
| 315 | |
| 316 /** | |
| 317 * Get the name of the notification, including the domain prefix. | |
| 318 */ | |
| 319 String get longEvent => '$domainName.$event'; | |
| 320 | |
| 321 /** | |
| 322 * Get the full type of the notification object, including the common "id" | |
| 323 * and "error" fields. | |
| 324 */ | |
| 325 TypeDecl get notificationType { | |
| 326 List<TypeObjectField> fields = [new TypeObjectField('event', | |
| 327 new TypeReference('String', null), null, value: '$domainName.$event')]; | |
| 328 if (params != null) { | |
| 329 fields.add(new TypeObjectField('params', params, null)); | |
| 330 } | |
| 331 return new TypeObject(fields, null); | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 /** | |
| 336 * Description of a named type definition. | |
| 337 */ | |
| 338 class TypeDefinition extends ApiNode { | |
| 339 final String name; | |
| 340 final TypeDecl type; | |
| 341 | |
| 342 TypeDefinition(this.name, this.type, dom.Element html) : super(html); | |
| 343 } | |
| 344 | |
| 345 /** | |
| 346 * Base class for all possible types. | |
| 347 */ | |
| 348 abstract class TypeDecl extends ApiNode { | |
| 349 TypeDecl(dom.Element html) : super(html); | |
| 350 | |
| 351 accept(ApiVisitor visitor); | |
| 352 } | |
| 353 | |
| 354 /** | |
| 355 * A reference to a type which is either defined elsewhere in the API or which | |
| 356 * is built-in ([String], [bool], or [int]). | |
| 357 */ | |
| 358 class TypeReference extends TypeDecl { | |
| 359 final String typeName; | |
| 360 | |
| 361 TypeReference(this.typeName, dom.Element html) : super(html) { | |
| 362 if (typeName.isEmpty) { | |
| 363 throw new Exception('Empty type name'); | |
| 364 } | |
| 365 } | |
| 366 | |
| 367 accept(ApiVisitor visitor) => visitor.visitTypeReference(this); | |
| 368 } | |
| 369 | |
| 370 /** | |
| 371 * Type of a JSON object with specified fields, some of which may be optional. | |
| 372 */ | |
| 373 class TypeObject extends TypeDecl { | |
| 374 final List<TypeObjectField> fields; | |
| 375 | |
| 376 TypeObject(this.fields, dom.Element html) : super(html); | |
| 377 | |
| 378 accept(ApiVisitor visitor) => visitor.visitTypeObject(this); | |
| 379 } | |
| 380 | |
| 381 /** | |
| 382 * Description of a single field in a [TypeObject]. | |
| 383 */ | |
| 384 class TypeObjectField extends ApiNode { | |
| 385 final String name; | |
| 386 final TypeDecl type; | |
| 387 final bool optional; | |
| 388 | |
| 389 /** | |
| 390 * Value which the field is required to contain, or null if it may vary. | |
| 391 */ | |
| 392 final Object value; | |
| 393 | |
| 394 TypeObjectField(this.name, this.type, dom.Element html, {this.optional: | |
| 395 false, this.value}) : super(html); | |
| 396 } | |
| 397 | |
| 398 /** | |
| 399 * Type of a JSON list. | |
| 400 */ | |
| 401 class TypeList extends TypeDecl { | |
| 402 final TypeDecl itemType; | |
| 403 | |
| 404 TypeList(this.itemType, dom.Element html) : super(html); | |
| 405 | |
| 406 accept(ApiVisitor visitor) => visitor.visitTypeList(this); | |
| 407 } | |
| 408 | |
| 409 /** | |
| 410 * Type of a JSON map. | |
| 411 */ | |
| 412 class TypeMap extends TypeDecl { | |
| 413 /** | |
| 414 * Type of map keys. Note that since JSON map keys must always be strings, | |
| 415 * this must either be a [TypeReference] for [String], or a [TypeReference] | |
| 416 * to a type which is defined in the API as an enum or a synonym for [String]. | |
| 417 */ | |
| 418 final TypeReference keyType; | |
| 419 | |
| 420 /** | |
| 421 * Type of map values. | |
| 422 */ | |
| 423 final TypeDecl valueType; | |
| 424 | |
| 425 TypeMap(this.keyType, this.valueType, dom.Element html) : super(html); | |
| 426 | |
| 427 accept(ApiVisitor visitor) => visitor.visitTypeMap(this); | |
| 428 } | |
| 429 | |
| 430 /** | |
| 431 * Type of an enum. We represent enums in JSON as strings, so this type | |
| 432 * declaration simply lists the allowed values. | |
| 433 */ | |
| 434 class TypeEnum extends TypeDecl { | |
| 435 final List<TypeEnumValue> values; | |
| 436 | |
| 437 TypeEnum(this.values, dom.Element html) : super(html); | |
| 438 | |
| 439 accept(ApiVisitor visitor) => visitor.visitTypeEnum(this); | |
| 440 } | |
| 441 | |
| 442 /** | |
| 443 * Description of a single allowed value for an enum. | |
| 444 */ | |
| 445 class TypeEnumValue extends ApiNode { | |
| 446 final String value; | |
| 447 | |
| 448 TypeEnumValue(this.value, dom.Element html) : super(html); | |
| 449 } | |
| OLD | NEW |