Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
|
Siggi Cherem (dart-lang)
2013/01/02 21:40:55
2013 =)
Jacob
2013/01/03 00:09:33
noooo! that's what i get for taking too long to co
| |
| 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 * AST describing all information about Dart libraries required to render | |
| 7 * Dart documentation. | |
| 8 */ | |
| 1 library ast; | 9 library ast; |
| 2 | 10 |
| 3 import 'package:web_ui/safe_html.dart'; | 11 import 'package:web_ui/safe_html.dart'; |
| 4 import 'markdown.dart' as md; | 12 import 'markdown.dart' as md; |
| 5 | 13 |
| 6 /** | 14 /** |
| 7 * Top level data model for the app. | 15 * Top level data model for the app. |
| 8 * Mapping from String ids to [LibraryElement] objects describing all currently | 16 * Mapping from String ids to [LibraryElement] objects describing all currently |
| 9 * loaded libraries. All code must be written to work properly if more libraries | 17 * loaded libraries. All code must be written to work properly if more libraries |
| 10 * are loaded incrementally. | 18 * are loaded incrementally. |
| 11 */ | 19 */ |
| 12 Map<String, LibraryElement> libraries = <LibraryElement>{}; | 20 Map<String, LibraryElement> libraries = <LibraryElement>{}; |
| 13 | 21 |
| 14 List<String> LIBRARY_KINDS = ['variable', 'property', 'method', 'class', 'except ion', 'typedef']; | 22 /** |
| 15 List<String> CLASS_KINDS = ['constructor', 'variable', 'property', 'method', 'op erator']; | 23 * Children of a library are shown in the UI grouped by type sorted in the order |
| 24 * specified by this list. | |
| 25 */ | |
| 26 List<String> LIBRARY_ITEMS = ['variable', 'property', 'method', 'class', | |
| 27 'exception', 'typedef']; | |
| 28 /** | |
| 29 * Children of a class are shown in the UI grouped by type sorted in the order | |
| 30 * specified by this list. | |
| 31 */ | |
| 32 List<String> CLASS_ITEMS = ['constructor', 'variable', 'property', 'method', | |
| 33 'operator']; | |
| 16 // TODO(jacobr): add package kinds? | 34 // TODO(jacobr): add package kinds? |
| 17 | 35 |
| 18 // TODO(jacobr): i18n | 36 // TODO(jacobr): i18n |
| 19 /** | 37 /** |
| 20 * Pretty names for the various kinds displayed. | 38 * Pretty names for the various kinds displayed. |
| 21 */ | 39 */ |
| 22 final KIND_TITLES = {'property': 'Properties', | 40 final KIND_TITLES = { |
| 23 'variable': 'Variables', | 41 'property': 'Properties', |
| 24 'method': 'Functions', | 42 'variable': 'Variables', |
| 25 'constructor': 'Constructors', | 43 'method': 'Functions', |
| 26 'class': 'Classes', | 44 'constructor': 'Constructors', |
| 27 'operator': 'Operators', | 45 'class': 'Classes', |
| 28 'typedef': 'Typedefs', | 46 'operator': 'Operators', |
| 29 'exception': 'Exceptions' | 47 'typedef': 'Typedefs', |
| 48 'exception': 'Exceptions' | |
| 30 }; | 49 }; |
| 31 | 50 |
| 32 /** | 51 /** |
| 33 * Block of elements to render summary documentation for that all share the | |
| 34 * same kind. | |
| 35 * | |
| 36 * For example, all properties, all functions, or all constructors. | |
| 37 */ | |
| 38 class ElementBlock { | |
| 39 String kind; | |
| 40 List<Element> elements; | |
| 41 | |
| 42 ElementBlock(this.kind, this.elements); | |
| 43 | |
| 44 String get kindTitle => KIND_TITLES[kind]; | |
| 45 } | |
| 46 | |
| 47 Reference jsonDeserializeReference(Map json) { | |
| 48 return json != null ? new Reference(json) : null; | |
| 49 } | |
| 50 | |
| 51 /** | |
| 52 * Deserializes JSON into [Element] or [Reference] objects. | |
| 53 */ | |
| 54 Element jsonDeserialize(Map json, Element parent) { | |
| 55 if (json == null) return null; | |
| 56 if (!json.containsKey('kind')) { | |
| 57 throw "Unable to deserialize $json"; | |
| 58 } | |
| 59 | |
| 60 switch (json['kind']) { | |
| 61 case 'class': | |
| 62 return new ClassElement(json, parent); | |
| 63 case 'typedef': | |
| 64 return new TypedefElement(json, parent); | |
| 65 case 'typeparam': | |
| 66 return new TypeParameterElement(json, parent); | |
| 67 case 'library': | |
| 68 return new LibraryElement(json, parent); | |
| 69 case 'method': | |
| 70 return new MethodElement(json, parent); | |
| 71 case 'property': | |
| 72 return new PropertyElement(json, parent); | |
| 73 case 'constructor': | |
| 74 return new ConstructorElement(json, parent); | |
| 75 case 'variable': | |
| 76 return new VariableElement(json, parent); | |
| 77 case 'param': | |
| 78 return new ParameterElement(json, parent); | |
| 79 default: | |
| 80 return new Element(json, parent); | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 List<Element> jsonDeserializeArray(List json, Element parent) { | |
| 85 var ret = <Element>[]; | |
| 86 if (json != null) { | |
| 87 for (Map elementJson in json) { | |
| 88 ret.add(jsonDeserialize(elementJson, parent)); | |
| 89 } | |
| 90 } | |
| 91 return ret; | |
| 92 } | |
| 93 | |
| 94 List<Reference> jsonDeserializeReferenceArray(List json) { | |
| 95 var ret = <Reference>[]; | |
| 96 if (json != null) { | |
| 97 for (Map referenceJson in json) { | |
| 98 ret.add(new Reference(referenceJson)); | |
| 99 } | |
| 100 } | |
| 101 return ret; | |
| 102 } | |
| 103 | |
| 104 /** | |
| 105 * Reference to an [Element]. | 52 * Reference to an [Element]. |
| 106 */ | 53 */ |
| 107 class Reference { | 54 class Reference { |
| 108 final String refId; | 55 final String refId; |
| 109 final String name; | 56 final String name; |
| 57 final List<Reference> arguments; | |
| 58 | |
| 110 Reference(Map json) : | 59 Reference(Map json) : |
| 111 name = json['name'], | 60 name = json['name'], |
| 112 refId = json['refId']; | 61 refId = json['refId'], |
| 62 arguments = _jsonDeserializeReferenceArray(json['arguments']); | |
| 63 | |
| 64 /** | |
| 65 * Short description appropriate for displaying in a tree control or other | |
| 66 * situtation where a short description is required. | |
| 67 */ | |
| 68 String get shortDescription { | |
| 69 if (arguments.isEmpty) { | |
| 70 return name; | |
| 71 } else { | |
| 72 var params = Strings.join( | |
| 73 arguments.map((param) => param.shortDescription), ', '); | |
| 74 return '$name<$params>'; | |
| 75 } | |
| 76 } | |
| 113 } | 77 } |
| 114 | 78 |
| 115 /** | 79 /** |
| 116 * Lookup a library based on the [libraryId]. | 80 * Lookup a library based on the [libraryId]. |
| 117 * | |
| 118 * If the library cannot be found, a stub dummy [Library] will be returned. | |
| 119 */ | 81 */ |
| 120 LibraryElement lookupLibrary(String libraryId) { | 82 LibraryElement lookupLibrary(String libraryId) { |
| 121 var library = libraries[libraryId]; | 83 return libraries[libraryId]; |
| 122 if (library == null) { | |
| 123 library = new LibraryElement.stub(libraryId, null); | |
| 124 } | |
| 125 return library; | |
| 126 } | 84 } |
| 127 | 85 |
| 128 /** | 86 /** |
| 129 * Resolve the [Element] matching the [referenceId]. | 87 * Resolve the [Element] matching the [referenceId]. |
| 130 * | 88 * |
| 131 * If the Element cannot be found, a stub dummy [Element] will be returned. | 89 * If the Element cannot be found, a stub dummy [Element] will be returned. |
| 132 */ | 90 */ |
| 133 Element lookupReferenceId(String referenceId) { | 91 Element lookupReferenceId(String referenceId) { |
| 134 var parts = referenceId.split(new RegExp('/')); | 92 var parts = referenceId.split(new RegExp('/')); |
| 135 Element current = lookupLibrary(parts.first); | 93 Element current = lookupLibrary(parts.first); |
| 136 var result = <Element>[current]; | 94 for (var i = 1; i < parts.length && current != null; i++) { |
| 137 for (var i = 1; i < parts.length; i++) { | |
| 138 var id = parts[i]; | 95 var id = parts[i]; |
| 139 var next = null; | 96 var next = null; |
| 140 for (var child in current.children) { | 97 for (var child in current.children) { |
| 141 if (child.id == id) { | 98 if (child.id == id) { |
| 142 next = child; | 99 next = child; |
| 143 break; | 100 break; |
| 144 } | 101 } |
| 145 } | 102 } |
| 146 if (next == null) { | |
| 147 next = new Element.stub(id, current); | |
| 148 } | |
| 149 current = next; | 103 current = next; |
| 150 } | 104 } |
| 151 return current; | 105 return current; |
| 152 } | 106 } |
| 153 | 107 |
| 154 /** | 108 /** |
| 155 * Invoke [callback] on every [Element] in the ast. | 109 * Invoke [callback] on every [Element] in the ast. |
| 156 */ | 110 */ |
| 157 _traverseWorld(void callback(Element)) { | 111 _traverseWorld(void callback(Element)) { |
| 158 for (var library in libraries.values) { | 112 for (var library in libraries.values) { |
| 159 library.traverse(callback); | 113 library.traverse(callback); |
| 160 } | 114 } |
| 161 } | 115 } |
| 162 | 116 |
| 163 // TODO(jacobr): remove this method when templates handle safe HTML containing | 117 // TODO(jacobr): remove this method when templates handle [SafeHTML] containing |
| 118 // multiple top level nodes correct. | |
| 164 SafeHtml _markdownToSafeHtml(String text) { | 119 SafeHtml _markdownToSafeHtml(String text) { |
| 165 // We currently have to insert an extra span for now because of | 120 // We currently have to insert an extra span for now because of |
| 166 // https://github.com/dart-lang/dart-web-components/issues/212 | 121 // https://github.com/dart-lang/web-ui/issues/212 |
| 167 return new SafeHtml.unsafe(text != null && !text.isEmpty ? | 122 return new SafeHtml.unsafe(text != null && !text.isEmpty ? |
| 168 '<span>${md.markdownToHtml(text)}</span>' : '<span><span>'); | 123 '<span>${md.markdownToHtml(text)}</span>' : '<span><span>'); |
| 169 } | 124 } |
| 170 | 125 |
| 171 /** | 126 /** |
| 172 * Specifies the order elements should appear in the UI. | |
| 173 */ | |
| 174 int elementUiOrder(Element a, Element b) { | |
| 175 if (a.isPrivate != b.isPrivate) { | |
| 176 return a.isPrivate == true ? 1 : -1; | |
| 177 } | |
| 178 return a.name.compareTo(b.name); | |
| 179 } | |
| 180 | |
| 181 /** | |
| 182 * Base class for all elements in the AST. | 127 * Base class for all elements in the AST. |
| 183 */ | 128 */ |
| 184 class Element { | 129 class Element implements Comparable { |
| 185 final Element parent; | 130 final Element parent; |
| 131 | |
| 186 /** Human readable type name for the node. */ | 132 /** Human readable type name for the node. */ |
| 187 final String rawKind; | 133 final String rawKind; |
| 134 | |
| 188 /** Human readable name for the element. */ | 135 /** Human readable name for the element. */ |
| 189 final String name; | 136 final String name; |
| 137 | |
| 190 /** Id for the node that is unique within its parent's children. */ | 138 /** Id for the node that is unique within its parent's children. */ |
| 191 final String id; | 139 final String id; |
| 140 | |
| 192 /** Raw text of the comment associated with the Element if any. */ | 141 /** Raw text of the comment associated with the Element if any. */ |
| 193 final String comment; | 142 final String comment; |
| 143 | |
| 194 /** Whether the node is private. */ | 144 /** Whether the node is private. */ |
| 195 final bool isPrivate; | 145 final bool isPrivate; |
| 196 | 146 |
| 197 final String _uri; | |
| 198 final String _line; | |
| 199 | |
| 200 /** Children of the node. */ | 147 /** Children of the node. */ |
| 201 List<Element> children; | 148 List<Element> children; |
| 202 | 149 |
| 203 /** Whether the [Element] is currently being loaded. */ | 150 /** Whether the [Element] is currently being loaded. */ |
| 204 final bool loading; | 151 final bool loading; |
| 205 | 152 |
| 153 final String _uri; | |
| 154 final String _line; | |
| 206 String _refId; | 155 String _refId; |
| 207 | |
| 208 Map _members; | |
| 209 SafeHtml _commentHtml; | 156 SafeHtml _commentHtml; |
| 210 List<Element> _references; | 157 List<Element> _references; |
| 158 List<Element> _typeParameters; | |
| 211 | 159 |
| 212 Element(Map json, this.parent) : | 160 Element(Map json, this.parent) : |
| 213 name = json['name'], | 161 name = json['name'], |
| 214 rawKind = json['kind'], | 162 rawKind = json['kind'], |
| 215 id = json['id'], | 163 id = json['id'], |
| 216 comment = json['comment'], | 164 comment = json['comment'], |
| 217 isPrivate = json['isPrivate'], | 165 isPrivate = json['isPrivate'] == true, |
| 218 _uri = json['uri'], | 166 _uri = json['uri'], |
| 219 _line = json['line'], | 167 _line = json['line'], |
| 220 loading = false { | 168 loading = false { |
| 221 children = jsonDeserializeArray(json['children'], this); | 169 children = _jsonDeserializeArray(json['children'], this); |
| 222 } | 170 } |
| 223 | 171 |
| 224 /** | 172 /** |
| 225 * Returns a kind name that make sense for the UI rather than the AST | 173 * Returns a kind name that make sense for the UI rather than the AST |
| 226 * kinds. For example, setters are considered properties instead of | 174 * kinds. For example, setters are considered properties instead of |
| 227 * methods. | 175 * methods in the UI but not the AST. |
| 228 */ | 176 */ |
| 229 String get uiKind => kind; | 177 String get uiKind => kind; |
| 230 | 178 |
| 179 /** | |
| 180 * Longer possibly multiple word description of the [kind]. | |
| 181 */ | |
| 182 String get kindDescription => uiKind; | |
| 183 | |
| 231 /** Invoke [callback] on this [Element] and all descendants. */ | 184 /** Invoke [callback] on this [Element] and all descendants. */ |
| 232 void traverse(void callback(Element)) { | 185 void traverse(void callback(Element)) { |
| 233 callback(this); | 186 callback(this); |
| 234 for (var child in children) { | 187 for (var child in children) { |
| 235 callback(child); | 188 callback(child); |
| 236 } | 189 } |
| 237 } | 190 } |
| 238 | 191 |
| 239 /** | 192 /** |
| 240 * Uri containing the definition of the element. | 193 * Uri containing the source code for the definition of the element. |
| 241 */ | 194 */ |
| 242 String get uri { | 195 String get uri => _uri != null ? _uri : (parent != null ? parent.uri : null); |
| 243 Element current = this; | |
| 244 while (current != null) { | |
| 245 if (current._uri != null) return current._uri; | |
| 246 current = current.parent; | |
| 247 } | |
| 248 return null; | |
| 249 } | |
| 250 | 196 |
| 251 /** | 197 /** |
| 252 * Line in the original source file that starts the definition of the element. | 198 * Line in the original source file that begins the definition of the element. |
| 253 */ | 199 */ |
| 254 String get line { | 200 String get line => _line != null ? |
| 255 Element current = this; | 201 _line : (parent != null ? parent.line : null); |
| 256 while (current != null) { | |
| 257 if (current._line != null) return current._line; | |
| 258 current = current.parent; | |
| 259 } | |
| 260 return null; | |
| 261 } | |
| 262 | |
| 263 Element.stub(this.id, this.parent) : | |
| 264 name = '???', // TODO(jacobr): remove/add | |
| 265 _uri = null, | |
| 266 _line = null, | |
| 267 comment = null, | |
| 268 rawKind = null, | |
| 269 children = <Element>[], | |
| 270 isPrivate = null, | |
| 271 loading = true; | |
| 272 | 202 |
| 273 /** | 203 /** |
| 274 * Globally unique identifier for this element. | 204 * Globally unique identifier for this element. |
| 275 */ | 205 */ |
| 276 String get refId { | 206 String get refId { |
| 277 if (_refId == null) { | 207 if (_refId == null) { |
| 278 if (parent == null) { | 208 _refId = (parent == null) ? id : '${parent.refId}/$id'; |
| 279 _refId = id; | |
| 280 } else { | |
| 281 _refId = '${parent.refId}/$id'; | |
| 282 } | |
| 283 } | 209 } |
| 284 return _refId; | 210 return _refId; |
| 285 } | 211 } |
| 286 | 212 |
| 287 /** | 213 /** |
| 288 * Whether this [Element] references the specified [referenceId]. | 214 * Whether this [Element] references the specified [referenceId]. |
| 289 */ | 215 */ |
| 290 bool hasReference(String referenceId) { | 216 bool hasReference(String referenceId) => |
| 291 for (var child in children) { | 217 children.some((child) => child.hasReference(referenceId)); |
| 292 if (child.hasReference(referenceId)) { | |
| 293 return true; | |
| 294 } | |
| 295 } | |
| 296 return false; | |
| 297 } | |
| 298 | 218 |
| 299 /** Returns all [Element]s that reference this [Element]. */ | 219 /** Returns all [Element]s that reference this [Element]. */ |
| 300 List<Element> get references { | 220 List<Element> get references { |
| 301 if (_references == null) { | 221 if (_references == null) { |
| 302 _references = <Element>[]; | 222 _references = <Element>[]; |
| 303 // TODO(jacobr): change to filterWorld and tweak meaning. | |
| 304 _traverseWorld((element) { | 223 _traverseWorld((element) { |
| 305 if (element.hasReference(refId)) { | 224 if (element.hasReference(refId)) { |
| 306 _references.add(element); | 225 _references.add(element); |
| 307 } | 226 } |
| 308 }); | 227 }); |
| 309 } | 228 } |
| 310 return _references; | 229 return _references; |
| 311 } | 230 } |
| 312 | 231 |
| 313 // TODO(jacobr): write without recursion. | 232 /** Path from the root of the tree to this [Element]. */ |
| 314 /** | 233 List<Element> get path => |
| 315 * Path from this [Element] to the root of the tree starting at the root. | 234 parent == null ? <Element>[this] : parent.path..add(this); |
| 316 */ | 235 |
| 317 List<Element> get path { | 236 List<Element> get typeParameters { |
| 318 if (parent == null) { | 237 if (_typeParameters == null) { |
| 319 return <Element>[this]; | 238 _typeParameters = _filterByKind('typeparam'); |
| 320 } else { | |
| 321 return parent.path..add(this); | |
| 322 } | 239 } |
| 240 return _typeParameters; | |
| 323 } | 241 } |
| 324 | 242 |
| 325 /** | 243 /** |
| 326 * [SafeHtml] for the comment associated with this [Element] generated from | 244 * [SafeHtml] for the comment associated with this [Element] generated from |
| 327 * the markdow comment associated with the element. | 245 * the markdow comment associated with the element. |
| 328 */ | 246 */ |
| 329 SafeHtml get commentHtml { | 247 SafeHtml get commentHtml { |
| 330 if (_commentHtml == null) { | 248 if (_commentHtml == null) { |
| 331 _commentHtml = _markdownToSafeHtml(comment); | 249 _commentHtml = _markdownToSafeHtml(comment); |
| 332 } | 250 } |
| 333 return _commentHtml; | 251 return _commentHtml; |
| 334 } | 252 } |
| 335 | 253 |
| 336 /** | 254 /** |
| 337 * Short description appropriate for displaying in a tree control or other | 255 * Short description appropriate for displaying in a tree control or other |
| 338 * situtation where a short description is required. | 256 * situtation where a short description is required. |
| 339 */ | 257 */ |
| 340 String get shortDescription => name; | 258 String get shortDescription { |
| 259 if (typeParameters.isEmpty) { | |
| 260 return name; | |
| 261 } else { | |
| 262 var params = Strings.join( | |
| 263 typeParameters.map((param) => param.shortDescription), | |
| 264 ', '); | |
| 265 return '$name<$params>'; | |
| 266 } | |
| 267 } | |
| 341 | 268 |
| 342 /** Possibly normalized representation of the node kind. */ | 269 /** |
| 270 * Ui specific representation of the node kind. | |
| 271 * For example, currently operators are considered their own kind even though | |
| 272 * they aren't their own kind in the AST. | |
| 273 */ | |
| 343 String get kind => rawKind; | 274 String get kind => rawKind; |
| 344 | 275 |
| 345 /** | 276 /** |
| 346 * Generate blocks of Elements for each kind in the list of [desiredKinds]. | 277 * Generate blocks of Elements for each kind in the list of [desiredKinds]. |
| 347 * | 278 * |
| 348 * This is helpful when rendering UI that segments members into blocks. | 279 * This is helpful when rendering UI that segments members into blocks. |
| 349 * Uses the kind types that make sense for the UI rather than the AST | 280 * Uses the kind types that make sense for the UI rather than the AST |
| 350 * kinds. For example, setters are considered properties instead of methods. | 281 * kinds. For example, setters are considered properties instead of methods. |
| 351 */ | 282 */ |
| 352 List<ElementBlock> _createElementBlocks(List<String> desiredKinds) { | 283 List<ElementBlock> _createElementBlocks(List<String> desiredKinds) { |
| 353 var blockMap = new Map<String, List<Element>>(); | 284 var blockMap = new Map<String, List<Element>>(); |
| 354 for (var child in children) { | 285 for (var child in children) { |
| 355 if (desiredKinds.contains(child.uiKind)) { | 286 if (desiredKinds.contains(child.uiKind)) { |
| 356 blockMap.putIfAbsent(child.uiKind, () => <Element>[]).add(child); | 287 blockMap.putIfAbsent(child.uiKind, () => <Element>[]).add(child); |
| 357 } | 288 } |
| 358 } | 289 } |
| 359 | 290 |
| 360 var blocks = <ElementBlock>[]; | 291 var blocks = <ElementBlock>[]; |
| 361 for (var kind in desiredKinds) { | 292 for (var kind in desiredKinds) { |
| 362 var elements = blockMap[kind]; | 293 var elements = blockMap[kind]; |
| 363 if (elements != null) { | 294 if (elements != null) { |
| 364 blocks.add(new ElementBlock(kind, elements..sort(elementUiOrder))); | 295 blocks.add(new ElementBlock(kind, elements..sort())); |
| 365 } | 296 } |
| 366 } | 297 } |
| 367 return blocks; | 298 return blocks; |
| 368 } | 299 } |
| 369 | 300 |
| 370 List<Element> _filterByKind(String kind) => | 301 List<Element> _filterByKind(String kind) => |
| 371 children.filter((child) => child.kind == kind); | 302 children.filter((child) => child.kind == kind); |
| 372 | 303 |
| 373 Map<String, Element> _mapForKind(String kind) { | 304 Map<String, Element> _mapForKind(String kind) { |
| 374 Map ret = {}; | 305 Map ret = {}; |
| 375 if (children != null) { | 306 if (children == null) return ret; |
| 376 for (var child in children) { | 307 |
| 377 if (child.kind == kind) { | 308 for (var child in children) { |
| 378 ret[child.id] = child; | 309 if (child.kind == kind) { |
| 379 } | 310 ret[child.id] = child; |
| 380 } | 311 } |
| 381 } | 312 } |
| 382 return ret; | 313 return ret; |
| 383 } | 314 } |
| 384 | 315 |
| 385 Map<String, Element> _mapForKinds(Map<String, Element> kinds) { | 316 /** |
| 386 Map ret = {}; | 317 * Specifies the order elements should appear in the UI. |
| 387 if (children != null) { | 318 */ |
| 388 for (var child in children) { | 319 int compareTo(Element other) { |
| 389 if (kinds.containsKey(child.kind)) { | 320 if (isPrivate != other.isPrivate) { |
| 390 ret[child.id] = child; | 321 return other.isPrivate ? 1 : -1; |
| 391 } | |
| 392 } | |
| 393 } | 322 } |
| 394 return ret; | 323 return name.compareTo(other.name); |
| 395 } | |
| 396 | |
| 397 Map<String, Element> get members { | |
| 398 if (_members == null) { | |
| 399 // TODO(jacobr): include properties???!? | |
| 400 _members = _mapForKinds({'method': true, 'property' : true}); | |
| 401 } | |
| 402 return _members; | |
| 403 } | 324 } |
| 404 } | 325 } |
| 405 | 326 |
| 406 /** | 327 /** |
| 407 * [Element] describing a Dart library. | 328 * [Element] describing a Dart library. |
| 408 * | 329 * |
| 409 * Adds convenience helpers for quickly accessing data about libraries. | 330 * Adds convenience helpers for quickly accessing data about libraries. |
| 410 */ | 331 */ |
| 411 class LibraryElement extends Element { | 332 class LibraryElement extends Element { |
| 412 Map<String, ClassElement> _classes; | 333 Map<String, ClassElement> _classes; |
| 413 List<ClassElement> _sortedClasses; | 334 List<ClassElement> _sortedClasses; |
| 414 List<ElementBlock> _childBlocks; | 335 List<ElementBlock> _childBlocks; |
| 415 | 336 |
| 416 LibraryElement(json, Element parent) : super(json, parent); | 337 LibraryElement(json, Element parent) : super(json, parent); |
| 417 LibraryElement.stub(String id, Element parent) : super.stub(id, parent); | |
| 418 | 338 |
| 419 /** Returns all classes defined by the library. */ | 339 /** Returns all classes defined by the library. */ |
| 420 Map<String, ClassElement> get classes { | 340 Map<String, ClassElement> get classes { |
| 421 if (_classes == null) { | 341 if (_classes == null) { |
| 422 _classes = _mapForKind('class'); | 342 _classes = _mapForKind('class'); |
| 423 } | 343 } |
| 424 return _classes; | 344 return _classes; |
| 425 } | 345 } |
| 426 | 346 |
| 427 /** | 347 /** |
| 428 * Returns all classes defined by the library sorted name and whether they | 348 * Returns all classes defined by the library sorted name and whether they |
| 429 * are private. | 349 * are private. |
| 430 */ | 350 */ |
| 431 List<ClassElement> get sortedClasses { | 351 List<ClassElement> get sortedClasses { |
| 432 if (_sortedClasses == null) { | 352 if (_sortedClasses == null) { |
| 433 _sortedClasses = []..addAll(classes.values)..sort(elementUiOrder); | 353 _sortedClasses = []..addAll(classes.values)..sort(); |
| 434 } | 354 } |
| 435 return _sortedClasses; | 355 return _sortedClasses; |
| 436 } | 356 } |
| 437 | 357 |
| 438 /** | 358 /** |
| 439 * Returns all blocks of elements that should be rendered by UI summarizing | 359 * Returns all blocks of elements that should be rendered by UI summarizing |
| 440 * the Library. | 360 * the Library. |
| 441 */ | 361 */ |
| 442 List<ElementBlock> get childBlocks { | 362 List<ElementBlock> get childBlocks { |
| 443 if (_childBlocks == null) _childBlocks = _createElementBlocks(LIBRARY_KINDS) ; | 363 if (_childBlocks == null) { |
| 364 _childBlocks = _createElementBlocks(LIBRARY_ITEMS); | |
| 365 } | |
| 444 return _childBlocks; | 366 return _childBlocks; |
| 445 } | 367 } |
| 446 } | 368 } |
| 447 | 369 |
| 448 /** | 370 /** |
| 449 * [Element] describing a Dart class. | 371 * [Element] describing a Dart class. |
| 450 */ | 372 */ |
| 451 class ClassElement extends Element { | 373 class ClassElement extends Element { |
| 374 | |
| 452 /** Members of the class grouped into logical blocks. */ | 375 /** Members of the class grouped into logical blocks. */ |
| 453 List<ElementBlock> _childBlocks; | 376 List<ElementBlock> _childBlocks; |
| 377 | |
| 454 /** Interfaces the class implements. */ | 378 /** Interfaces the class implements. */ |
| 455 final List<Reference> interfaces; | 379 final List<Reference> interfaces; |
| 380 | |
| 456 /** Superclass of this class. */ | 381 /** Superclass of this class. */ |
| 457 final Reference superclass; | 382 final Reference superclass; |
| 458 | 383 |
| 384 /** Whether the class is abstract. */ | |
| 385 final bool isAbstract; | |
| 386 | |
| 459 List<ClassElement> _superclasses; | 387 List<ClassElement> _superclasses; |
| 460 List<ClassElement> _subclasses; | 388 List<ClassElement> _subclasses; |
| 461 | 389 |
| 462 ClassElement(Map json, Element parent) | 390 ClassElement(Map json, Element parent) |
| 463 : super(json, parent), | 391 : super(json, parent), |
| 464 interfaces = jsonDeserializeReferenceArray(json['interfaces']), | 392 interfaces = _jsonDeserializeReferenceArray(json['interfaces']), |
| 465 superclass = jsonDeserializeReference(json['superclass']); | 393 superclass = jsonDeserializeReference(json['superclass']), |
| 466 | 394 isAbstract = json['isAbstract'] == true; |
| 467 ClassElement.stub(String id, Element parent) | |
| 468 : super.stub(id, parent), | |
| 469 interfaces = [], | |
| 470 superclass = null; | |
| 471 | 395 |
| 472 /** Returns all superclasses of this class. */ | 396 /** Returns all superclasses of this class. */ |
| 473 List<ClassElement> get superclasses { | 397 List<ClassElement> get superclasses { |
| 474 if (_superclasses == null) { | 398 if (_superclasses == null) { |
| 475 _superclasses = <ClassElement>[]; | 399 _superclasses = <ClassElement>[]; |
| 476 addSuperclasses(clazz) { | 400 addSuperclasses(clazz) { |
| 477 if (clazz.superclass != null) { | 401 if (clazz.superclass != null) { |
| 478 ClassElement superclassElement = | 402 ClassElement superclassElement = |
| 479 lookupReferenceId(clazz.superclass.refId); | 403 lookupReferenceId(clazz.superclass.refId); |
| 480 addSuperclasses(superclassElement); | 404 addSuperclasses(superclassElement); |
| 481 _superclasses.add(superclassElement); | 405 _superclasses.add(superclassElement); |
| 482 } | 406 } |
| 483 } | 407 } |
| 484 addSuperclasses(this); | 408 addSuperclasses(this); |
| 485 } | 409 } |
| 486 return _superclasses; | 410 return _superclasses; |
| 487 } | 411 } |
| 488 | 412 |
| 413 String get kindDescription => | |
| 414 isAbstract ? 'abstract $uiKind' : uiKind; | |
| 415 | |
| 489 /** | 416 /** |
| 490 * Returns classes that directly extend or implement this class. | 417 * Returns classes that directly extend or implement this class. |
| 491 */ | 418 */ |
| 492 List<ClassElement> get subclasses { | 419 List<ClassElement> get subclasses { |
| 493 if (_subclasses == null) { | 420 if (_subclasses == null) { |
| 494 _subclasses = <ClassElement>[]; | 421 _subclasses = <ClassElement>[]; |
| 495 for (var library in libraries.values) { | 422 for (var library in libraries.values) { |
| 496 for (ClassElement candidateClass in library.sortedClasses) { | 423 for (ClassElement candidateClass in library.sortedClasses) { |
| 497 if (candidateClass.implementsOrExtends(refId)) { | 424 if (candidateClass.implementsOrExtends(refId)) { |
| 498 _subclasses.add(candidateClass); | 425 _subclasses.add(candidateClass); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 512 if (interface.refId == referenceId) return true; | 439 if (interface.refId == referenceId) return true; |
| 513 } | 440 } |
| 514 return superclass != null && superclass.refId == referenceId; | 441 return superclass != null && superclass.refId == referenceId; |
| 515 } | 442 } |
| 516 | 443 |
| 517 /** | 444 /** |
| 518 * Returns blocks of elements clustered by kind ordered in the desired | 445 * Returns blocks of elements clustered by kind ordered in the desired |
| 519 * order for describing a class definition. | 446 * order for describing a class definition. |
| 520 */ | 447 */ |
| 521 List<ElementBlock> get childBlocks { | 448 List<ElementBlock> get childBlocks { |
| 522 if (_childBlocks == null) _childBlocks = _createElementBlocks(CLASS_KINDS); | 449 if (_childBlocks == null) _childBlocks = _createElementBlocks(CLASS_ITEMS); |
| 523 return _childBlocks; | 450 return _childBlocks; |
| 524 } | 451 } |
| 525 } | 452 } |
| 526 | 453 |
| 454 /** | |
| 455 * Element describing a typedef. | |
| 456 */ | |
| 527 class TypedefElement extends Element { | 457 class TypedefElement extends Element { |
| 528 final Reference returnType; | 458 final Reference returnType; |
| 529 List<ParameterElement> _parameters; | 459 List<Element> _parameters; |
| 530 | 460 |
| 531 TypedefElement(Map json, Element parent) : super(json, parent), | 461 TypedefElement(Map json, Element parent) : super(json, parent), |
| 532 returnType = jsonDeserializeReference(json['returnType']); | 462 returnType = jsonDeserializeReference(json['returnType']); |
| 533 | 463 |
| 534 /** | 464 /** |
| 535 * Returns a list of the parameters of the typedef. | 465 * Returns a list of the parameters of the typedef. |
| 536 */ | 466 */ |
| 537 List<ParameterElement> get parameters { | 467 List<Element> get parameters { |
| 538 if (_parameters == null) { | 468 if (_parameters == null) { |
| 539 _parameters = _filterByKind('param'); | 469 _parameters = _filterByKind('param'); |
| 540 } | 470 } |
| 541 return _parameters; | 471 return _parameters; |
| 542 } | 472 } |
| 543 } | 473 } |
| 544 | 474 |
| 545 /** | 475 /** |
| 546 * [Element] describing a method which may be a regular method, a setter, or an | 476 * [Element] describing a method which may be a regular method, a setter, or an |
| 547 * operator. | 477 * operator. |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 564 } | 494 } |
| 565 | 495 |
| 566 String get uiKind => isSetter ? 'property' : kind; | 496 String get uiKind => isSetter ? 'property' : kind; |
| 567 | 497 |
| 568 /** | 498 /** |
| 569 * Returns a plain text short description of the method suitable for rendering | 499 * Returns a plain text short description of the method suitable for rendering |
| 570 * in a tree control or other case where a short method description is | 500 * in a tree control or other case where a short method description is |
| 571 * required. | 501 * required. |
| 572 */ | 502 */ |
| 573 String get shortDescription { | 503 String get shortDescription { |
| 574 if (isSetter == true) { | 504 if (isSetter) { |
| 575 var sb = new StringBuffer('${name.substring(0, name.length-1)}'); | 505 var sb = new StringBuffer('${name.substring(0, name.length - 1)}'); |
| 576 if (!parameters.isEmpty && parameters.first != null | 506 if (!parameters.isEmpty && parameters.first != null |
| 577 && parameters.first.type != null) { | 507 && parameters.first.type != null) { |
| 578 sb..add(' ')..add(parameters.first.type.name); | 508 sb..add(' ')..add(parameters.first.type.name); |
| 579 } | 509 } |
| 580 return sb.toString(); | 510 return sb.toString(); |
| 581 } | 511 } |
| 582 return '$name(${Strings.join(parameters.map( | 512 return '$name(${Strings.join(parameters.map( |
| 583 (arg) => arg.type != null ? arg.type.name : ''), ', ')})'; | 513 (arg) => arg.type != null ? arg.type.name : ''), ', ')})'; |
| 584 } | 514 } |
| 585 | 515 |
| 586 Reference get returnType; | 516 Reference get returnType; |
| 587 List<ParameterElement> _parameters; | 517 List<Element> _parameters; |
| 588 | 518 |
| 589 /** | 519 /** |
| 590 * Returns a list of the parameters of the Method. | 520 * Returns a list of the parameters of the Method. |
| 591 */ | 521 */ |
| 592 List<ParameterElement> get parameters { | 522 List<Element> get parameters { |
| 593 if (_parameters == null) { | 523 if (_parameters == null) { |
| 594 _parameters = _filterByKind('param'); | 524 _parameters = _filterByKind('param'); |
| 595 } | 525 } |
| 596 return _parameters; | 526 return _parameters; |
| 597 } | 527 } |
| 598 | 528 |
| 599 // For UI purposes we want to treat operators as their own kind. | 529 /// For UI purposes we want to treat operators as their own kind. |
| 600 String get kind => isOperator ? 'operator' : rawKind; | 530 String get kind => isOperator ? 'operator' : rawKind; |
| 601 } | 531 } |
| 602 | 532 |
| 603 /** | 533 /** |
| 604 * Element describing a parameter. | 534 * Element describing a parameter. |
| 605 */ | 535 */ |
| 606 class ParameterElement extends Element { | 536 class ParameterElement extends Element { |
| 607 /** Type of the parameter. */ | 537 /** Type of the parameter. */ |
| 608 final Reference type; | 538 final Reference type; |
| 609 /** Whether the parameter is optional. */ | 539 /** Whether the parameter is optional. */ |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 624 * Element describing a generic type parameter. | 554 * Element describing a generic type parameter. |
| 625 */ | 555 */ |
| 626 class TypeParameterElement extends Element { | 556 class TypeParameterElement extends Element { |
| 627 /** Upper bound for the parameter. */ | 557 /** Upper bound for the parameter. */ |
| 628 Reference upperBound; | 558 Reference upperBound; |
| 629 | 559 |
| 630 TypeParameterElement(Map json, Element parent) : | 560 TypeParameterElement(Map json, Element parent) : |
| 631 super(json, parent), | 561 super(json, parent), |
| 632 upperBound = jsonDeserializeReference(json['upperBound']); | 562 upperBound = jsonDeserializeReference(json['upperBound']); |
| 633 | 563 |
| 564 String get shortDescription { | |
| 565 if (upperBound == null) { | |
| 566 return name; | |
| 567 } else { | |
| 568 return '$name extends ${upperBound.shortDescription}'; | |
| 569 } | |
| 570 } | |
| 571 | |
| 634 bool hasReference(String referenceId) { | 572 bool hasReference(String referenceId) { |
| 635 if (super.hasReference(referenceId)) return true; | 573 if (super.hasReference(referenceId)) return true; |
| 636 return upperBound != null && upperBound.refId == referenceId; | 574 return upperBound != null && upperBound.refId == referenceId; |
| 637 } | 575 } |
| 638 } | 576 } |
| 639 | 577 |
| 578 /** | |
| 579 * Element describing a method. | |
| 580 */ | |
| 640 class MethodElement extends MethodElementBase { | 581 class MethodElement extends MethodElementBase { |
| 641 | 582 |
| 642 final Reference returnType; | 583 final Reference returnType; |
| 643 | 584 |
| 644 MethodElement(Map json, Element parent) : super(json, parent), | 585 MethodElement(Map json, Element parent) : super(json, parent), |
| 645 returnType = jsonDeserializeReference(json['returnType']); | 586 returnType = jsonDeserializeReference(json['returnType']); |
| 646 } | 587 } |
| 647 | 588 |
| 589 /** | |
| 590 * Element describing a property getter. | |
| 591 */ | |
| 648 class PropertyElement extends MethodElementBase { | 592 class PropertyElement extends MethodElementBase { |
| 649 final Reference returnType; | 593 final Reference returnType; |
| 650 | 594 |
| 651 String get shortDescription => name; | 595 String get shortDescription => name; |
| 652 | 596 |
| 653 PropertyElement(Map json, Element parent) : super(json, parent), | 597 PropertyElement(Map json, Element parent) : super(json, parent), |
| 654 returnType = jsonDeserializeReference(json['ref']); | 598 returnType = jsonDeserializeReference(json['ref']); |
| 655 } | 599 } |
| 656 | 600 |
| 601 /** | |
| 602 * Element describing a variable. | |
| 603 */ | |
| 657 class VariableElement extends MethodElementBase { | 604 class VariableElement extends MethodElementBase { |
| 658 final Reference returnType; | 605 final Reference returnType; |
| 659 /** Whether this variable is final. */ | 606 /** Whether this variable is final. */ |
| 660 final bool isFinal; | 607 final bool isFinal; |
| 661 | 608 |
| 662 String get shortDescription => name; | 609 String get shortDescription => name; |
| 663 | 610 |
| 664 VariableElement(Map json, Element parent) : super(json, parent), | 611 VariableElement(Map json, Element parent) : super(json, parent), |
| 665 returnType = jsonDeserializeReference(json['ref']), | 612 returnType = jsonDeserializeReference(json['ref']), |
| 666 isFinal = json['isFinal']; | 613 isFinal = json['isFinal']; |
| 667 } | 614 } |
| 668 | 615 |
| 616 /** | |
| 617 * Element describing a constructor. | |
| 618 */ | |
| 669 class ConstructorElement extends MethodElementBase { | 619 class ConstructorElement extends MethodElementBase { |
| 670 ConstructorElement(json, Element parent) : super(json, parent); | 620 ConstructorElement(json, Element parent) : super(json, parent); |
| 671 | 621 |
| 672 Reference get returnType => null; | 622 Reference get returnType => null; |
| 673 } | 623 } |
| 624 | |
| 625 /** | |
| 626 * Block of elements to render summary documentation for all elements that share | |
| 627 * the same kind. | |
| 628 * | |
| 629 * For example, all properties, all functions, or all constructors. | |
| 630 */ | |
| 631 class ElementBlock { | |
| 632 String kind; | |
| 633 List<Element> elements; | |
| 634 | |
| 635 ElementBlock(this.kind, this.elements); | |
| 636 | |
| 637 String get kindTitle => KIND_TITLES[kind]; | |
| 638 } | |
| 639 | |
| 640 Reference jsonDeserializeReference(Map json) { | |
| 641 return json != null ? new Reference(json) : null; | |
| 642 } | |
| 643 | |
| 644 /** | |
| 645 * Deserializes JSON into an [Element] object. | |
| 646 */ | |
| 647 Element jsonDeserialize(Map json, Element parent) { | |
| 648 if (json == null) return null; | |
| 649 | |
| 650 var kind = json['kind']; | |
| 651 if (kind == null) { | |
| 652 throw "Unable to deserialize $json"; | |
| 653 } | |
| 654 | |
| 655 switch (kind) { | |
| 656 case 'class': | |
| 657 return new ClassElement(json, parent); | |
| 658 case 'typedef': | |
| 659 return new TypedefElement(json, parent); | |
| 660 case 'typeparam': | |
| 661 return new TypeParameterElement(json, parent); | |
| 662 case 'library': | |
| 663 return new LibraryElement(json, parent); | |
| 664 case 'method': | |
| 665 return new MethodElement(json, parent); | |
| 666 case 'property': | |
| 667 return new PropertyElement(json, parent); | |
| 668 case 'constructor': | |
| 669 return new ConstructorElement(json, parent); | |
| 670 case 'variable': | |
| 671 return new VariableElement(json, parent); | |
| 672 case 'param': | |
| 673 return new ParameterElement(json, parent); | |
| 674 default: | |
| 675 return new Element(json, parent); | |
| 676 } | |
| 677 } | |
| 678 | |
| 679 List<Element> _jsonDeserializeArray(List json, Element parent) { | |
| 680 var ret = <Element>[]; | |
| 681 if (json == null) return ret; | |
| 682 | |
| 683 for (Map elementJson in json) { | |
| 684 ret.add(jsonDeserialize(elementJson, parent)); | |
| 685 } | |
| 686 return ret; | |
| 687 } | |
| 688 | |
| 689 List<Reference> _jsonDeserializeReferenceArray(List json) { | |
| 690 var ret = <Reference>[]; | |
| 691 if (json == null) return ret; | |
| 692 | |
| 693 for (Map referenceJson in json) { | |
| 694 ret.add(new Reference(referenceJson)); | |
| 695 } | |
| 696 return ret; | |
| 697 } | |
| OLD | NEW |