OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * A script to assist in documenting the difference between the dart:html API | 6 * A script to assist in documenting the difference between the dart:html API |
7 * and the old DOM API. | 7 * and the old DOM API. |
8 */ | 8 */ |
9 #library('html_diff'); | 9 #library('html_diff'); |
10 | 10 |
11 #import('dart:coreimpl'); | 11 #import('dart:coreimpl'); |
12 #import('../../frog/lang.dart'); | 12 #import('../../frog/lang.dart'); |
13 #import('../../frog/file_system_vm.dart'); | 13 #import('../../frog/file_system_vm.dart'); |
14 #import('../../frog/file_system.dart'); | 14 #import('../../frog/file_system.dart'); |
15 #import('../../lib/dartdoc/dartdoc.dart'); | 15 #import('../../lib/dartdoc/dartdoc.dart'); |
16 | 16 |
17 /** | 17 /** |
18 * A class for computing a many-to-many mapping between the types and members in | 18 * A class for computing a many-to-many mapping between the types and members in |
19 * `dart:dom` and `dart:html`. This mapping is based on two indicators: | 19 * `dart:dom` and `dart:html`. This mapping is based on two indicators: |
20 * | 20 * |
21 * 1. Auto-detected wrappers. Most `dart:html` types correspond | 21 * 1. Auto-detected wrappers. Most `dart:html` types correspond |
22 * straightforwardly to a single `dart:dom` type, and have the same name. | 22 * straightforwardly to a single `dart:dom` type, and have the same name. |
23 * In addition, most `dart:htmlimpl` methods just call a single `dart:dom` | 23 * In addition, most `dart:html` methods just call a single `dart:dom` |
24 * method. This class detects these simple correspondences automatically. | 24 * method. This class detects these simple correspondences automatically. |
25 * | 25 * |
26 * 2. Manual annotations. When it's not clear which `dart:dom` items a given | 26 * 2. Manual annotations. When it's not clear which `dart:dom` items a given |
27 * `dart:html` item corresponds to, the `dart:htmlimpl` item can be | 27 * `dart:html` item corresponds to, the `dart:html` item can be |
28 * annotated in the documentation comments using the `@domName` annotation. | 28 * annotated in the documentation comments using the `@domName` annotation. |
29 * | 29 * |
30 * The `@domName` annotations for types and members are of the form `@domName | 30 * The `@domName` annotations for types and members are of the form `@domName |
31 * NAME(, NAME)*`, where the `NAME`s refer to the `dart:dom` types/members that | 31 * NAME(, NAME)*`, where the `NAME`s refer to the `dart:dom` types/members that |
32 * correspond to the annotated `dart:htmlimpl` type/member. `NAME`s on member | 32 * correspond to the annotated `dart:html` type/member. `NAME`s on member |
33 * annotations can refer to either fully-qualified member names (e.g. | 33 * annotations can refer to either fully-qualified member names (e.g. |
34 * `Document.createElement`) or unqualified member names (e.g. `createElement`). | 34 * `Document.createElement`) or unqualified member names (e.g. `createElement`). |
35 * Unqualified member names are assumed to refer to members of one of the | 35 * Unqualified member names are assumed to refer to members of one of the |
36 * corresponding `dart:dom` types. | 36 * corresponding `dart:dom` types. |
37 */ | 37 */ |
38 class HtmlDiff { | 38 class HtmlDiff { |
39 /** A map from `dart:dom` members to corresponding `dart:html` members. */ | 39 /** A map from `dart:dom` members to corresponding `dart:html` members. */ |
40 final Map<Member, Set<Member>> domToHtml; | 40 final Map<Member, Set<Member>> domToHtml; |
41 | 41 |
42 /** A map from `dart:html` members to corresponding `dart:dom` members. */ | 42 /** A map from `dart:html` members to corresponding `dart:dom` members. */ |
43 final Map<Member, Set<Member>> htmlToDom; | 43 final Map<Member, Set<Member>> htmlToDom; |
44 | 44 |
45 /** A map from `dart:dom` types to corresponding `dart:html` types. */ | 45 /** A map from `dart:dom` types to corresponding `dart:html` types. */ |
46 final Map<Type, Set<Type>> domTypesToHtml; | 46 final Map<Type, Set<Type>> domTypesToHtml; |
47 | 47 |
48 /** A map from `dart:html` types to corresponding `dart:dom` types. */ | 48 /** A map from `dart:html` types to corresponding `dart:dom` types. */ |
49 final Map<Type, Set<Type>> htmlTypesToDom; | 49 final Map<Type, Set<Type>> htmlTypesToDom; |
50 | 50 |
51 final CommentMap comments; | 51 final CommentMap comments; |
52 | 52 |
53 static Library dom; | 53 static Library dom; |
54 | 54 |
55 /** | 55 /** |
56 * Perform static initialization of [world]. This should be run before | 56 * Perform static initialization of [world]. This should be run before |
57 * calling [HtmlDiff.run]. | 57 * calling [HtmlDiff.run]. |
58 */ | 58 */ |
59 static void initialize() { | 59 static void initialize() { |
60 world.processDartScript('dart:htmlimpl'); | 60 world.processDartScript('dart:html'); |
61 world.resolveAll(); | 61 world.resolveAll(); |
62 dom = world.libraries['dart:dom']; | 62 dom = world.libraries['dart:dom']; |
63 } | 63 } |
64 | 64 |
65 HtmlDiff() : | 65 HtmlDiff() : |
66 domToHtml = new Map<Member, Set<Member>>(), | 66 domToHtml = new Map<Member, Set<Member>>(), |
67 htmlToDom = new Map<Member, Set<Member>>(), | 67 htmlToDom = new Map<Member, Set<Member>>(), |
68 domTypesToHtml = new Map<Type, Set<Type>>(), | 68 domTypesToHtml = new Map<Type, Set<Type>>(), |
69 htmlTypesToDom = new Map<Type, Set<Type>>(), | 69 htmlTypesToDom = new Map<Type, Set<Type>>(), |
70 comments = new CommentMap(); | 70 comments = new CommentMap(); |
71 | 71 |
72 /** | 72 /** |
73 * Computes the `dart:dom` to `dart:html` mapping, and places it in | 73 * Computes the `dart:dom` to `dart:html` mapping, and places it in |
74 * [domToHtml], [htmlToDom], [domTypesToHtml], and [htmlTypesToDom]. Before | 74 * [domToHtml], [htmlToDom], [domTypesToHtml], and [htmlTypesToDom]. Before |
75 * this is run, Frog should be initialized (via [parseOptions] and | 75 * this is run, Frog should be initialized (via [parseOptions] and |
76 * [initializeWorld]) and [HtmlDiff.initialize] should be called. | 76 * [initializeWorld]) and [HtmlDiff.initialize] should be called. |
77 */ | 77 */ |
78 void run() { | 78 void run() { |
79 final htmlLib = world.libraries['dart:htmlimpl']; | 79 final htmlLib = world.libraries['dart:html']; |
80 for (var implType in htmlLib.types.getValues()) { | 80 for (var implType in htmlLib.types.getValues()) { |
81 final domTypes = htmlToDomTypes(implType); | 81 final domTypes = htmlToDomTypes(implType); |
82 final htmlType = htmlImplToHtmlType(implType); | 82 final htmlType = htmlImplToHtmlType(implType); |
83 if (htmlType == null) continue; | 83 if (htmlType == null) continue; |
84 | 84 |
85 htmlTypesToDom.putIfAbsent(htmlType, () => new Set()).addAll(domTypes); | 85 htmlTypesToDom.putIfAbsent(htmlType, () => new Set()).addAll(domTypes); |
86 domTypes.forEach((t) => | 86 domTypes.forEach((t) => |
87 domTypesToHtml.putIfAbsent(t, () => new Set()).add(htmlType)); | 87 domTypesToHtml.putIfAbsent(t, () => new Set()).add(htmlType)); |
88 | 88 |
89 final members = new List.from(implType.members.getValues()); | 89 final members = new List.from(implType.members.getValues()); |
(...skipping 20 matching lines...) Expand all Loading... |
110 var htmlName = htmlMember.name; | 110 var htmlName = htmlMember.name; |
111 if (htmlName.startsWith('get:') || htmlName.startsWith('set:')) { | 111 if (htmlName.startsWith('get:') || htmlName.startsWith('set:')) { |
112 htmlName = htmlName.substring(4); | 112 htmlName = htmlName.substring(4); |
113 } | 113 } |
114 | 114 |
115 return domTypeName == htmlTypeName && domName == htmlName; | 115 return domTypeName == htmlTypeName && domName == htmlName; |
116 } | 116 } |
117 | 117 |
118 /** | 118 /** |
119 * Records the `dart:dom` to `dart:html` mapping for [implMember] (from | 119 * Records the `dart:dom` to `dart:html` mapping for [implMember] (from |
120 * `dart:htmlimpl`). [domTypes] are the `dart:dom` [Type]s that correspond to | 120 * `dart:html`). [domTypes] are the `dart:dom` [Type]s that correspond to |
121 * [implMember]'s defining [Type]. | 121 * [implMember]'s defining [Type]. |
122 */ | 122 */ |
123 void _addMemberDiff(Member implMember, List<Type> domTypes) { | 123 void _addMemberDiff(Member implMember, List<Type> domTypes) { |
124 if (implMember.isProperty) { | 124 if (implMember.isProperty) { |
125 if (implMember.canGet) _addMemberDiff(implMember.getter, domTypes); | 125 if (implMember.canGet) _addMemberDiff(implMember.getter, domTypes); |
126 if (implMember.canSet) _addMemberDiff(implMember.setter, domTypes); | 126 if (implMember.canSet) _addMemberDiff(implMember.setter, domTypes); |
127 } | 127 } |
128 | 128 |
129 var domMembers = htmlToDomMembers(implMember, domTypes); | 129 var domMembers = htmlToDomMembers(implMember, domTypes); |
130 var htmlMember = htmlImplToHtmlMember(implMember); | 130 var htmlMember = htmlImplToHtmlMember(implMember); |
131 if (htmlMember == null && !domMembers.isEmpty()) { | 131 if (htmlMember == null && !domMembers.isEmpty()) { |
132 print('Warning: dart:htmlimpl member ${implMember.declaringType.name}.' + | 132 print('Warning: dart:html member ${implMember.declaringType.name}.' + |
133 '${implMember.name} has no corresponding dart:html member.'); | 133 '${implMember.name} has no corresponding dart:html member.'); |
134 } | 134 } |
135 | 135 |
136 if (htmlMember == null) return; | 136 if (htmlMember == null) return; |
137 if (!domMembers.isEmpty()) htmlToDom[htmlMember] = domMembers; | 137 if (!domMembers.isEmpty()) htmlToDom[htmlMember] = domMembers; |
138 domMembers.forEach((m) => | 138 domMembers.forEach((m) => |
139 domToHtml.putIfAbsent(m, () => new Set()).add(htmlMember)); | 139 domToHtml.putIfAbsent(m, () => new Set()).add(htmlMember)); |
140 } | 140 } |
141 | 141 |
142 /** | 142 /** |
143 * Returns the `dart:html` [Type] that corresponds to [implType] from | 143 * Returns the `dart:html` [Type] that corresponds to [implType] from |
144 * `dart:htmlimpl`, or `null` if there is no such correspondence. | 144 * `dart:html`, or `null` if there is no such correspondence. |
145 */ | 145 */ |
146 Type htmlImplToHtmlType(Type implType) { | 146 Type htmlImplToHtmlType(Type implType) { |
147 if (implType == null || implType.isTop || implType.interfaces.isEmpty() || | 147 if (implType == null || implType.isTop || implType.interfaces.isEmpty() || |
148 implType.interfaces[0].library.name != 'html') { | 148 implType.interfaces[0].library.name != 'html') { |
149 return null; | 149 return null; |
150 } | 150 } |
151 | 151 |
152 return implType.interfaces[0]; | 152 return implType.interfaces[0]; |
153 } | 153 } |
154 | 154 |
155 /** | 155 /** |
156 * Returns the `dart:html` [Member] that corresponds to [implMember] from | 156 * Returns the `dart:html` [Member] that corresponds to [implMember] from |
157 * `dart:htmlimpl`, or `null` if there is no such correspondence. | 157 * `dart:html`, or `null` if there is no such correspondence. |
158 */ | 158 */ |
159 Member htmlImplToHtmlMember(Member implMember) { | 159 Member htmlImplToHtmlMember(Member implMember) { |
160 var htmlType = htmlImplToHtmlType(implMember.declaringType); | 160 var htmlType = htmlImplToHtmlType(implMember.declaringType); |
161 if (htmlType == null) return null; | 161 if (htmlType == null) return null; |
162 | 162 |
163 bool getter, setter; | 163 bool getter, setter; |
164 if (implMember.isConstructor || implMember.isFactory) { | 164 if (implMember.isConstructor || implMember.isFactory) { |
165 var constructor = htmlType.constructors[implMember.name]; | 165 var constructor = htmlType.constructors[implMember.name]; |
166 if (constructor != null) return constructor; | 166 if (constructor != null) return constructor; |
167 | 167 |
168 // Look for a factory constructor whose type and name matches the member. | 168 // Look for a factory constructor whose type and name matches the member. |
169 return htmlType.factories.getFactoriesFor(implMember.name)[ | 169 return htmlType.factories.getFactoriesFor(implMember.name)[ |
170 implMember.constructorName]; | 170 implMember.constructorName]; |
171 } else if ((getter = implMember.name.startsWith('get:')) || | 171 } else if ((getter = implMember.name.startsWith('get:')) || |
172 (setter = implMember.name.startsWith('set:'))) { | 172 (setter = implMember.name.startsWith('set:'))) { |
173 // Use getMember to follow interface inheritance chains. | 173 // Use getMember to follow interface inheritance chains. |
174 var htmlProperty = htmlType.getMember(implMember.name.substring(4)); | 174 var htmlProperty = htmlType.getMember(implMember.name.substring(4)); |
175 if (htmlProperty != null) { | 175 if (htmlProperty != null) { |
176 return getter ? htmlProperty.getter : htmlProperty.setter; | 176 return getter ? htmlProperty.getter : htmlProperty.setter; |
177 } else { | 177 } else { |
178 return null; | 178 return null; |
179 } | 179 } |
180 } else { | 180 } else { |
181 return htmlType.getMember(implMember.name); | 181 return htmlType.getMember(implMember.name); |
182 } | 182 } |
183 } | 183 } |
184 | 184 |
185 /** | 185 /** |
186 * Returns the `dart:dom` [Type]s that correspond to [htmlType] from | 186 * Returns the `dart:dom` [Type]s that correspond to [htmlType] from |
187 * `dart:htmlimpl`. This can be the empty list if no correspondence is found. | 187 * `dart:html`. This can be the empty list if no correspondence is found. |
188 */ | 188 */ |
189 List<Type> htmlToDomTypes(Type htmlType) { | 189 List<Type> htmlToDomTypes(Type htmlType) { |
190 if (htmlType.name == null) return []; | 190 if (htmlType.name == null) return []; |
191 final tags = _getTags(comments.find(htmlType.span)); | 191 final tags = _getTags(comments.find(htmlType.span)); |
192 | 192 |
193 if (tags.containsKey('domName')) { | 193 if (tags.containsKey('domName')) { |
194 var domNames = map(tags['domName'].split(','), (s) => s.trim()); | 194 var domNames = map(tags['domName'].split(','), (s) => s.trim()); |
195 if (domNames.length == 1 && domNames[0] == 'none') return []; | 195 if (domNames.length == 1 && domNames[0] == 'none') return []; |
196 return map(domNames, (domName) { | 196 return map(domNames, (domName) { |
197 // DOMWindow is Chrome-specific, so we don't use it in our annotations. | 197 // DOMWindow is Chrome-specific, so we don't use it in our annotations. |
198 if (domName == 'Window') domName = 'DOMWindow'; | 198 if (domName == 'Window') domName = 'DOMWindow'; |
199 final domType = dom.types[domName]; | 199 final domType = dom.types[domName]; |
200 if (domType == null) print('Warning: no dart:dom type named $domName'); | 200 if (domType == null) print('Warning: no dart:dom type named $domName'); |
201 return domType; | 201 return domType; |
202 }); | 202 }); |
203 } else { | 203 } else { |
204 if (!htmlType.name.endsWith('WrappingImplementation')) return []; | 204 if (!htmlType.name.endsWith('WrappingImplementation')) return []; |
205 final domName = htmlType.name.replaceFirst('WrappingImplementation', ''); | 205 final domName = htmlType.name.replaceFirst('WrappingImplementation', ''); |
206 var domType = dom.types[domName]; | 206 var domType = dom.types[domName]; |
207 if (domType == null && domName.endsWith('Element')) { | 207 if (domType == null && domName.endsWith('Element')) { |
208 domType = dom.types['HTML$domName']; | 208 domType = dom.types['HTML$domName']; |
209 } | 209 } |
210 if (domType == null) domType = dom.types['WebKit$domName']; | 210 if (domType == null) domType = dom.types['WebKit$domName']; |
211 if (domType == null) { | 211 if (domType == null) { |
212 print('Warning: no dart:dom type matches dart:htmlimpl ' + | 212 print('Warning: no dart:dom type matches dart:html ' + |
213 htmlType.name); | 213 htmlType.name); |
214 return []; | 214 return []; |
215 } | 215 } |
216 return [domType]; | 216 return [domType]; |
217 } | 217 } |
218 } | 218 } |
219 | 219 |
220 /** | 220 /** |
221 * Returns the `dart:dom` [Member]s that correspond to [htmlMember] from | 221 * Returns the `dart:dom` [Member]s that correspond to [htmlMember] from |
222 * `dart:htmlimpl`. This can be the empty set if no correspondence is found. | 222 * `dart:html`. This can be the empty set if no correspondence is found. |
223 * [domTypes] are the `dart:dom` [Type]s that correspond to [implMember]'s | 223 * [domTypes] are the `dart:dom` [Type]s that correspond to [implMember]'s |
224 * defining [Type]. | 224 * defining [Type]. |
225 */ | 225 */ |
226 Set<Member> htmlToDomMembers(Member htmlMember, List<Type> domTypes) { | 226 Set<Member> htmlToDomMembers(Member htmlMember, List<Type> domTypes) { |
227 if (htmlMember.isPrivate || htmlMember is! MethodMember) return new Set(); | 227 if (htmlMember.isPrivate || htmlMember is! MethodMember) return new Set(); |
228 final tags = _getTags(comments.find(htmlMember.span)); | 228 final tags = _getTags(comments.find(htmlMember.span)); |
229 if (tags.containsKey('domName')) { | 229 if (tags.containsKey('domName')) { |
230 final domNames = map(tags['domName'].split(','), (s) => s.trim()); | 230 final domNames = map(tags['domName'].split(','), (s) => s.trim()); |
231 if (domNames.length == 1 && domNames[0] == 'none') return new Set(); | 231 if (domNames.length == 1 && domNames[0] == 'none') return new Set(); |
232 final members = new Set(); | 232 final members = new Set(); |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 Map<String, String> _getTags(String comment) { | 394 Map<String, String> _getTags(String comment) { |
395 if (comment == null) return const <String>{}; | 395 if (comment == null) return const <String>{}; |
396 final re = new RegExp("@([a-zA-Z]+) ([^;]+)(?:;|\$)"); | 396 final re = new RegExp("@([a-zA-Z]+) ([^;]+)(?:;|\$)"); |
397 final tags = <String>{}; | 397 final tags = <String>{}; |
398 for (var m in re.allMatches(comment.trim())) { | 398 for (var m in re.allMatches(comment.trim())) { |
399 tags[m[1]] = m[2]; | 399 tags[m[1]] = m[2]; |
400 } | 400 } |
401 return tags; | 401 return tags; |
402 } | 402 } |
403 } | 403 } |
OLD | NEW |