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 // This code was auto-generated, is not intended to be edited, and is subject to | |
6 // significant change. Please see the README file for more information. | |
7 | |
8 library engine.source; | |
9 | |
10 import 'dart:collection'; | |
11 import "dart:math" as math; | |
12 | |
13 import 'package:analyzer/file_system/file_system.dart'; | |
14 import 'package:analyzer/file_system/physical_file_system.dart'; | |
15 import 'package:analyzer/source/package_map_resolver.dart'; | |
16 import 'package:analyzer/src/generated/utilities_dart.dart' as utils; | |
17 import 'package:analyzer/task/model.dart'; | |
18 import 'package:package_config/packages.dart'; | |
19 import 'package:path/path.dart' as pathos; | |
20 | |
21 import 'engine.dart'; | |
22 import 'java_core.dart'; | |
23 import 'java_engine.dart'; | |
24 import 'java_io.dart' show JavaFile; | |
25 import 'sdk.dart' show DartSdk; | |
26 import 'source_io.dart' show FileBasedSource; | |
27 | |
28 /** | |
29 * A function that is used to visit [ContentCache] entries. | |
30 */ | |
31 typedef void ContentCacheVisitor(String fullPath, int stamp, String contents); | |
32 | |
33 /** | |
34 * A cache used to override the default content of a [Source]. | |
35 */ | |
36 class ContentCache { | |
37 /** | |
38 * A table mapping the full path of sources to the contents of those sources. | |
39 * This is used to override the default contents of a source. | |
40 */ | |
41 HashMap<String, String> _contentMap = new HashMap<String, String>(); | |
42 | |
43 /** | |
44 * A table mapping the full path of sources to the modification stamps of | |
45 * those sources. This is used when the default contents of a source has been | |
46 * overridden. | |
47 */ | |
48 HashMap<String, int> _stampMap = new HashMap<String, int>(); | |
49 | |
50 /** | |
51 * Visit all entries of this cache. | |
52 */ | |
53 void accept(ContentCacheVisitor visitor) { | |
54 _contentMap.forEach((String fullPath, String contents) { | |
55 int stamp = _stampMap[fullPath]; | |
56 visitor(fullPath, stamp, contents); | |
57 }); | |
58 } | |
59 | |
60 /** | |
61 * Return the contents of the given [source], or `null` if this cache does not | |
62 * override the contents of the source. | |
63 * | |
64 * <b>Note:</b> This method is not intended to be used except by | |
65 * [AnalysisContext.getContents]. | |
66 */ | |
67 String getContents(Source source) => _contentMap[source.fullName]; | |
68 | |
69 /** | |
70 * Return the modification stamp of the given [source], or `null` if this | |
71 * cache does not override the contents of the source. | |
72 * | |
73 * <b>Note:</b> This method is not intended to be used except by | |
74 * [AnalysisContext.getModificationStamp]. | |
75 */ | |
76 int getModificationStamp(Source source) => _stampMap[source.fullName]; | |
77 | |
78 /** | |
79 * Set the contents of the given [source] to the given [contents]. This has | |
80 * the effect of overriding the default contents of the source. If the | |
81 * contents are `null` the override is removed so that the default contents | |
82 * will be returned. | |
83 */ | |
84 String setContents(Source source, String contents) { | |
85 String fullName = source.fullName; | |
86 if (contents == null) { | |
87 _stampMap.remove(fullName); | |
88 return _contentMap.remove(fullName); | |
89 } else { | |
90 int newStamp = JavaSystem.currentTimeMillis(); | |
91 int oldStamp = _stampMap[fullName]; | |
92 _stampMap[fullName] = newStamp; | |
93 // Occasionally, if this method is called in rapid succession, the | |
94 // timestamps are equal. Guard against this by artificially incrementing | |
95 // the new timestamp. | |
96 if (newStamp == oldStamp) { | |
97 _stampMap[fullName] = newStamp + 1; | |
98 } | |
99 String oldContent = _contentMap[fullName]; | |
100 _contentMap[fullName] = contents; | |
101 return oldContent; | |
102 } | |
103 } | |
104 } | |
105 | |
106 class CustomUriResolver extends UriResolver { | |
107 final Map<String, String> _urlMappings; | |
108 | |
109 CustomUriResolver(this._urlMappings); | |
110 | |
111 @override | |
112 Source resolveAbsolute(Uri uri) { | |
113 String mapping = _urlMappings[uri.toString()]; | |
114 if (mapping == null) return null; | |
115 | |
116 Uri fileUri = new Uri.file(mapping); | |
117 if (!fileUri.isAbsolute) return null; | |
118 | |
119 JavaFile javaFile = new JavaFile.fromUri(fileUri); | |
120 return new FileBasedSource(javaFile); | |
121 } | |
122 } | |
123 | |
124 /** | |
125 * Instances of the class `DartUriResolver` resolve `dart` URI's. | |
126 */ | |
127 class DartUriResolver extends UriResolver { | |
128 /** | |
129 * The name of the `dart` scheme. | |
130 */ | |
131 static String DART_SCHEME = "dart"; | |
132 | |
133 /** | |
134 * The prefix of a URI using the dart-ext scheme to reference a native code li
brary. | |
135 */ | |
136 static String _DART_EXT_SCHEME = "dart-ext:"; | |
137 | |
138 /** | |
139 * The Dart SDK against which URI's are to be resolved. | |
140 */ | |
141 final DartSdk _sdk; | |
142 | |
143 /** | |
144 * Initialize a newly created resolver to resolve Dart URI's against the given
platform within the | |
145 * given Dart SDK. | |
146 * | |
147 * @param sdk the Dart SDK against which URI's are to be resolved | |
148 */ | |
149 DartUriResolver(this._sdk); | |
150 | |
151 /** | |
152 * Return the [DartSdk] against which URIs are to be resolved. | |
153 * | |
154 * @return the [DartSdk] against which URIs are to be resolved. | |
155 */ | |
156 DartSdk get dartSdk => _sdk; | |
157 | |
158 @override | |
159 Source resolveAbsolute(Uri uri) { | |
160 if (!isDartUri(uri)) { | |
161 return null; | |
162 } | |
163 return _sdk.mapDartUri(uri.toString()); | |
164 } | |
165 | |
166 /** | |
167 * Return `true` if the given URI is a `dart-ext:` URI. | |
168 * | |
169 * @param uriContent the textual representation of the URI being tested | |
170 * @return `true` if the given URI is a `dart-ext:` URI | |
171 */ | |
172 static bool isDartExtUri(String uriContent) => | |
173 uriContent != null && uriContent.startsWith(_DART_EXT_SCHEME); | |
174 | |
175 /** | |
176 * Return `true` if the given URI is a `dart:` URI. | |
177 * | |
178 * @param uri the URI being tested | |
179 * @return `true` if the given URI is a `dart:` URI | |
180 */ | |
181 static bool isDartUri(Uri uri) => DART_SCHEME == uri.scheme; | |
182 } | |
183 | |
184 /** | |
185 * Instances of the class `LineInfo` encapsulate information about line and colu
mn information | |
186 * within a source file. | |
187 */ | |
188 class LineInfo { | |
189 /** | |
190 * An array containing the offsets of the first character of each line in the
source code. | |
191 */ | |
192 final List<int> _lineStarts; | |
193 | |
194 /** | |
195 * The zero-based [_lineStarts] index resulting from the last call to | |
196 * [getLocation]. | |
197 */ | |
198 int _previousLine = 0; | |
199 | |
200 /** | |
201 * Initialize a newly created set of line information to represent the data en
coded in the given | |
202 * array. | |
203 * | |
204 * @param lineStarts the offsets of the first character of each line in the so
urce code | |
205 */ | |
206 LineInfo(this._lineStarts) { | |
207 if (_lineStarts == null) { | |
208 throw new IllegalArgumentException("lineStarts must be non-null"); | |
209 } else if (_lineStarts.length < 1) { | |
210 throw new IllegalArgumentException("lineStarts must be non-empty"); | |
211 } | |
212 } | |
213 | |
214 /** | |
215 * Return the location information for the character at the given offset. | |
216 * | |
217 * @param offset the offset of the character for which location information is
to be returned | |
218 * @return the location information for the character at the given offset | |
219 */ | |
220 LineInfo_Location getLocation(int offset) { | |
221 var min = 0; | |
222 var max = _lineStarts.length - 1; | |
223 | |
224 // Subsequent calls to [getLocation] are often for offsets near each other. | |
225 // To take advantage of that, we cache the index of the line start we found | |
226 // when this was last called. If the current offset is on that line or | |
227 // later, we'll skip those early indices completely when searching. | |
228 if (offset >= _lineStarts[_previousLine]) { | |
229 min = _previousLine; | |
230 | |
231 // Before kicking off a full binary search, do a quick check here to see | |
232 // if the new offset is on that exact line. | |
233 if (min == _lineStarts.length - 1 || offset < _lineStarts[min + 1]) { | |
234 return new LineInfo_Location(min + 1, offset - _lineStarts[min] + 1); | |
235 } | |
236 } | |
237 | |
238 // Binary search to fine the line containing this offset. | |
239 while (min < max) { | |
240 var midpoint = (max - min + 1) ~/ 2 + min; | |
241 | |
242 if (_lineStarts[midpoint] > offset) { | |
243 max = midpoint - 1; | |
244 } else { | |
245 min = midpoint; | |
246 } | |
247 } | |
248 | |
249 _previousLine = min; | |
250 | |
251 return new LineInfo_Location(min + 1, offset - _lineStarts[min] + 1); | |
252 } | |
253 } | |
254 | |
255 /** | |
256 * Instances of the class `Location` represent the location of a character as a
line and | |
257 * column pair. | |
258 */ | |
259 class LineInfo_Location { | |
260 /** | |
261 * The one-based index of the line containing the character. | |
262 */ | |
263 final int lineNumber; | |
264 | |
265 /** | |
266 * The one-based index of the column containing the character. | |
267 */ | |
268 final int columnNumber; | |
269 | |
270 /** | |
271 * Initialize a newly created location to represent the location of the charac
ter at the given | |
272 * line and column position. | |
273 * | |
274 * @param lineNumber the one-based index of the line containing the character | |
275 * @param columnNumber the one-based index of the column containing the charac
ter | |
276 */ | |
277 LineInfo_Location(this.lineNumber, this.columnNumber); | |
278 } | |
279 | |
280 /** | |
281 * Instances of interface `LocalSourcePredicate` are used to determine if the gi
ven | |
282 * [Source] is "local" in some sense, so can be updated. | |
283 */ | |
284 abstract class LocalSourcePredicate { | |
285 /** | |
286 * Instance of [LocalSourcePredicate] that always returns `false`. | |
287 */ | |
288 static final LocalSourcePredicate FALSE = new LocalSourcePredicate_FALSE(); | |
289 | |
290 /** | |
291 * Instance of [LocalSourcePredicate] that always returns `true`. | |
292 */ | |
293 static final LocalSourcePredicate TRUE = new LocalSourcePredicate_TRUE(); | |
294 | |
295 /** | |
296 * Instance of [LocalSourcePredicate] that returns `true` for all [Source]s | |
297 * except of SDK. | |
298 */ | |
299 static final LocalSourcePredicate NOT_SDK = | |
300 new LocalSourcePredicate_NOT_SDK(); | |
301 | |
302 /** | |
303 * Determines if the given [Source] is local. | |
304 * | |
305 * @param source the [Source] to analyze | |
306 * @return `true` if the given [Source] is local | |
307 */ | |
308 bool isLocal(Source source); | |
309 } | |
310 | |
311 class LocalSourcePredicate_FALSE implements LocalSourcePredicate { | |
312 @override | |
313 bool isLocal(Source source) => false; | |
314 } | |
315 | |
316 class LocalSourcePredicate_NOT_SDK implements LocalSourcePredicate { | |
317 @override | |
318 bool isLocal(Source source) => source.uriKind != UriKind.DART_URI; | |
319 } | |
320 | |
321 class LocalSourcePredicate_TRUE implements LocalSourcePredicate { | |
322 @override | |
323 bool isLocal(Source source) => true; | |
324 } | |
325 | |
326 /** | |
327 * An implementation of an non-existing [Source]. | |
328 */ | |
329 class NonExistingSource extends Source { | |
330 @override | |
331 final String fullName; | |
332 | |
333 @override | |
334 final Uri uri; | |
335 | |
336 final UriKind uriKind; | |
337 | |
338 NonExistingSource(this.fullName, this.uri, this.uriKind); | |
339 | |
340 @override | |
341 TimestampedData<String> get contents { | |
342 throw new UnsupportedOperationException('$fullName does not exist.'); | |
343 } | |
344 | |
345 @override | |
346 String get encoding { | |
347 throw new UnsupportedOperationException('$fullName does not exist.'); | |
348 } | |
349 | |
350 @override | |
351 int get hashCode => fullName.hashCode; | |
352 | |
353 @override | |
354 bool get isInSystemLibrary => false; | |
355 | |
356 @override | |
357 int get modificationStamp => -1; | |
358 | |
359 @override | |
360 String get shortName => pathos.basename(fullName); | |
361 | |
362 @override | |
363 bool operator ==(Object other) { | |
364 if (other is NonExistingSource) { | |
365 return other.uriKind == uriKind && other.fullName == fullName; | |
366 } | |
367 return false; | |
368 } | |
369 | |
370 @override | |
371 bool exists() => false; | |
372 | |
373 @override | |
374 Uri resolveRelativeUri(Uri relativeUri) { | |
375 throw new UnsupportedOperationException('$fullName does not exist.'); | |
376 } | |
377 } | |
378 | |
379 /** | |
380 * The interface `Source` defines the behavior of objects representing source co
de that can be | |
381 * analyzed by the analysis engine. | |
382 * | |
383 * Implementations of this interface need to be aware of some assumptions made b
y the analysis | |
384 * engine concerning sources: | |
385 * * Sources are not required to be unique. That is, there can be multiple insta
nces representing | |
386 * the same source. | |
387 * * Sources are long lived. That is, the engine is allowed to hold on to a sour
ce for an extended | |
388 * period of time and that source must continue to report accurate and up-to-dat
e information. | |
389 * Because of these assumptions, most implementations will not maintain any stat
e but will delegate | |
390 * to an authoritative system of record in order to implement this API. For exam
ple, a source that | |
391 * represents files on disk would typically query the file system to determine t
he state of the | |
392 * file. | |
393 * | |
394 * If the instances that implement this API are the system of record, then they
will typically be | |
395 * unique. In that case, sources that are created that represent non-existent fi
les must also be | |
396 * retained so that if those files are created at a later date the long-lived so
urces representing | |
397 * those files will know that they now exist. | |
398 */ | |
399 abstract class Source implements AnalysisTarget { | |
400 /** | |
401 * An empty list of sources. | |
402 */ | |
403 @deprecated // Use Source.EMPTY_LIST | |
404 static const List<Source> EMPTY_ARRAY = EMPTY_LIST; | |
405 | |
406 /** | |
407 * An empty list of sources. | |
408 */ | |
409 static const List<Source> EMPTY_LIST = const <Source>[]; | |
410 | |
411 /** | |
412 * Get the contents and timestamp of this source. | |
413 * | |
414 * Clients should consider using the the method [AnalysisContext.getContents] | |
415 * because contexts can have local overrides of the content of a source that t
he source is not | |
416 * aware of. | |
417 * | |
418 * @return the contents and timestamp of the source | |
419 * @throws Exception if the contents of this source could not be accessed | |
420 */ | |
421 TimestampedData<String> get contents; | |
422 | |
423 /** | |
424 * Return an encoded representation of this source that can be used to create
a source that is | |
425 * equal to this source. | |
426 * | |
427 * @return an encoded representation of this source | |
428 * See [SourceFactory.fromEncoding]. | |
429 */ | |
430 String get encoding; | |
431 | |
432 /** | |
433 * Return the full (long) version of the name that can be displayed to the use
r to denote this | |
434 * source. For example, for a source representing a file this would typically
be the absolute path | |
435 * of the file. | |
436 * | |
437 * @return a name that can be displayed to the user to denote this source | |
438 */ | |
439 String get fullName; | |
440 | |
441 /** | |
442 * Return a hash code for this source. | |
443 * | |
444 * @return a hash code for this source | |
445 * See [Object.hashCode]. | |
446 */ | |
447 @override | |
448 int get hashCode; | |
449 | |
450 /** | |
451 * Return `true` if this source is in one of the system libraries. | |
452 * | |
453 * @return `true` if this is in a system library | |
454 */ | |
455 bool get isInSystemLibrary; | |
456 | |
457 /** | |
458 * Return the modification stamp for this source, or a negative value if the | |
459 * source does not exist. A modification stamp is a non-negative integer with | |
460 * the property that if the contents of the source have not been modified | |
461 * since the last time the modification stamp was accessed then the same value | |
462 * will be returned, but if the contents of the source have been modified one | |
463 * or more times (even if the net change is zero) the stamps will be different
. | |
464 * | |
465 * Clients should consider using the the method | |
466 * [AnalysisContext.getModificationStamp] because contexts can have local | |
467 * overrides of the content of a source that the source is not aware of. | |
468 */ | |
469 int get modificationStamp; | |
470 | |
471 /** | |
472 * Return a short version of the name that can be displayed to the user to den
ote this source. For | |
473 * example, for a source representing a file this would typically be the name
of the file. | |
474 * | |
475 * @return a name that can be displayed to the user to denote this source | |
476 */ | |
477 String get shortName; | |
478 | |
479 @override | |
480 Source get source => this; | |
481 | |
482 /** | |
483 * Return the URI from which this source was originally derived. | |
484 * | |
485 * @return the URI from which this source was originally derived | |
486 */ | |
487 Uri get uri; | |
488 | |
489 /** | |
490 * Return the kind of URI from which this source was originally derived. If th
is source was | |
491 * created from an absolute URI, then the returned kind will reflect the schem
e of the absolute | |
492 * URI. If it was created from a relative URI, then the returned kind will be
the same as the kind | |
493 * of the source against which the relative URI was resolved. | |
494 * | |
495 * @return the kind of URI from which this source was originally derived | |
496 */ | |
497 UriKind get uriKind; | |
498 | |
499 /** | |
500 * Return `true` if the given object is a source that represents the same sour
ce code as | |
501 * this source. | |
502 * | |
503 * @param object the object to be compared with this object | |
504 * @return `true` if the given object is a source that represents the same sou
rce code as | |
505 * this source | |
506 * See [Object.==]. | |
507 */ | |
508 @override | |
509 bool operator ==(Object object); | |
510 | |
511 /** | |
512 * Return `true` if this source exists. | |
513 * | |
514 * Clients should consider using the the method [AnalysisContext.exists] becau
se | |
515 * contexts can have local overrides of the content of a source that the sourc
e is not aware of | |
516 * and a source with local content is considered to exist even if there is no
file on disk. | |
517 * | |
518 * @return `true` if this source exists | |
519 */ | |
520 bool exists(); | |
521 | |
522 /** | |
523 * Resolve the relative URI against the URI associated with this source object
. | |
524 * | |
525 * Note: This method is not intended for public use, it is only visible out of
necessity. It is | |
526 * only intended to be invoked by a [SourceFactory]. Source factories will | |
527 * only invoke this method if the URI is relative, so implementations of this
method are not | |
528 * required to, and generally do not, verify the argument. The result of invok
ing this method with | |
529 * an absolute URI is intentionally left unspecified. | |
530 * | |
531 * @param relativeUri the relative URI to be resolved against this source | |
532 * @return the URI to which given URI was resolved | |
533 * @throws AnalysisException if the relative URI could not be resolved | |
534 */ | |
535 Uri resolveRelativeUri(Uri relativeUri); | |
536 } | |
537 | |
538 /** | |
539 * The interface `ContentReceiver` defines the behavior of objects that can rece
ive the | |
540 * content of a source. | |
541 */ | |
542 abstract class Source_ContentReceiver { | |
543 /** | |
544 * Accept the contents of a source. | |
545 * | |
546 * @param contents the contents of the source | |
547 * @param modificationTime the time at which the contents were last set | |
548 */ | |
549 void accept(String contents, int modificationTime); | |
550 } | |
551 | |
552 /** | |
553 * The interface `SourceContainer` is used by clients to define a collection of
sources | |
554 * | |
555 * Source containers are not used within analysis engine, but can be used by cli
ents to group | |
556 * sources for the purposes of accessing composite dependency information. For e
xample, the Eclipse | |
557 * client uses source containers to represent Eclipse projects, which allows it
to easily compute | |
558 * project-level dependencies. | |
559 */ | |
560 abstract class SourceContainer { | |
561 /** | |
562 * Determine if the specified source is part of the receiver's collection of s
ources. | |
563 * | |
564 * @param source the source in question | |
565 * @return `true` if the receiver contains the source, else `false` | |
566 */ | |
567 bool contains(Source source); | |
568 } | |
569 | |
570 /** | |
571 * Instances of the class `SourceFactory` resolve possibly relative URI's agains
t an existing | |
572 * [Source]. | |
573 */ | |
574 class SourceFactory { | |
575 /** | |
576 * The analysis context that this source factory is associated with. | |
577 */ | |
578 AnalysisContext context; | |
579 | |
580 /** | |
581 * URI processor used to find mappings for `package:` URIs found in a `.packag
es` config | |
582 * file. | |
583 */ | |
584 final Packages _packages; | |
585 | |
586 /** | |
587 * Resource provider used in working with package maps. | |
588 */ | |
589 final ResourceProvider _resourceProvider; | |
590 | |
591 /** | |
592 * The resolvers used to resolve absolute URI's. | |
593 */ | |
594 final List<UriResolver> _resolvers; | |
595 | |
596 /** | |
597 * The predicate to determine is [Source] is local. | |
598 */ | |
599 LocalSourcePredicate _localSourcePredicate = LocalSourcePredicate.NOT_SDK; | |
600 | |
601 /** | |
602 * Initialize a newly created source factory with the given absolute URI [reso
lvers] and | |
603 * optional [packages] resolution helper. | |
604 */ | |
605 SourceFactory(this._resolvers, | |
606 [this._packages, ResourceProvider resourceProvider]) | |
607 : _resourceProvider = resourceProvider != null | |
608 ? resourceProvider | |
609 : PhysicalResourceProvider.INSTANCE; | |
610 | |
611 /** | |
612 * Return the [DartSdk] associated with this [SourceFactory], or `null` if the
re | |
613 * is no such SDK. | |
614 * | |
615 * @return the [DartSdk] associated with this [SourceFactory], or `null` if | |
616 * there is no such SDK | |
617 */ | |
618 DartSdk get dartSdk { | |
619 for (UriResolver resolver in _resolvers) { | |
620 if (resolver is DartUriResolver) { | |
621 DartUriResolver dartUriResolver = resolver; | |
622 return dartUriResolver.dartSdk; | |
623 } | |
624 } | |
625 return null; | |
626 } | |
627 | |
628 /** | |
629 * Sets the [LocalSourcePredicate]. | |
630 * | |
631 * @param localSourcePredicate the predicate to determine is [Source] is local | |
632 */ | |
633 void set localSourcePredicate(LocalSourcePredicate localSourcePredicate) { | |
634 this._localSourcePredicate = localSourcePredicate; | |
635 } | |
636 | |
637 /// A table mapping package names to paths of directories containing | |
638 /// the package (or [null] if there is no registered package URI resolver). | |
639 Map<String, List<Folder>> get packageMap { | |
640 // Start by looking in .packages. | |
641 if (_packages != null) { | |
642 Map<String, List<Folder>> packageMap = <String, List<Folder>>{}; | |
643 _packages.asMap().forEach((String name, Uri uri) { | |
644 if (uri.scheme == 'file' || uri.scheme == '' /* unspecified */) { | |
645 packageMap[name] = | |
646 <Folder>[_resourceProvider.getFolder(uri.toFilePath())]; | |
647 } | |
648 }); | |
649 return packageMap; | |
650 } | |
651 | |
652 // Default to the PackageMapUriResolver. | |
653 PackageMapUriResolver resolver = _resolvers.firstWhere( | |
654 (r) => r is PackageMapUriResolver, orElse: () => null); | |
655 return resolver != null ? resolver.packageMap : null; | |
656 } | |
657 | |
658 /** | |
659 * Return a source object representing the given absolute URI, or `null` if th
e URI is not a | |
660 * valid URI or if it is not an absolute URI. | |
661 * | |
662 * @param absoluteUri the absolute URI to be resolved | |
663 * @return a source object representing the absolute URI | |
664 */ | |
665 Source forUri(String absoluteUri) { | |
666 try { | |
667 Uri uri = parseUriWithException(absoluteUri); | |
668 if (uri.isAbsolute) { | |
669 return _internalResolveUri(null, uri); | |
670 } | |
671 } catch (exception, stackTrace) { | |
672 AnalysisEngine.instance.logger.logError( | |
673 "Could not resolve URI: $absoluteUri", | |
674 new CaughtException(exception, stackTrace)); | |
675 } | |
676 return null; | |
677 } | |
678 | |
679 /** | |
680 * Return a source object representing the given absolute URI, or `null` if th
e URI is not | |
681 * an absolute URI. | |
682 * | |
683 * @param absoluteUri the absolute URI to be resolved | |
684 * @return a source object representing the absolute URI | |
685 */ | |
686 Source forUri2(Uri absoluteUri) { | |
687 if (absoluteUri.isAbsolute) { | |
688 try { | |
689 return _internalResolveUri(null, absoluteUri); | |
690 } on AnalysisException catch (exception, stackTrace) { | |
691 AnalysisEngine.instance.logger.logError( | |
692 "Could not resolve URI: $absoluteUri", | |
693 new CaughtException(exception, stackTrace)); | |
694 } | |
695 } | |
696 return null; | |
697 } | |
698 | |
699 /** | |
700 * Return a source object that is equal to the source object used to obtain th
e given encoding. | |
701 * | |
702 * @param encoding the encoding of a source object | |
703 * @return a source object that is described by the given encoding | |
704 * @throws IllegalArgumentException if the argument is not a valid encoding | |
705 * See [Source.encoding]. | |
706 */ | |
707 Source fromEncoding(String encoding) { | |
708 Source source = forUri(encoding); | |
709 if (source == null) { | |
710 throw new IllegalArgumentException( | |
711 "Invalid source encoding: '$encoding'"); | |
712 } | |
713 return source; | |
714 } | |
715 | |
716 /** | |
717 * Determines if the given [Source] is local. | |
718 * | |
719 * @param source the [Source] to analyze | |
720 * @return `true` if the given [Source] is local | |
721 */ | |
722 bool isLocalSource(Source source) => _localSourcePredicate.isLocal(source); | |
723 | |
724 /** | |
725 * Return a source object representing the URI that results from resolving the
given (possibly | |
726 * relative) contained URI against the URI associated with an existing source
object, whether or | |
727 * not the resulting source exists, or `null` if either the contained URI is i
nvalid or if | |
728 * it cannot be resolved against the source object's URI. | |
729 * | |
730 * @param containingSource the source containing the given URI | |
731 * @param containedUri the (possibly relative) URI to be resolved against the
containing source | |
732 * @return the source representing the contained URI | |
733 */ | |
734 Source resolveUri(Source containingSource, String containedUri) { | |
735 if (containedUri == null || containedUri.isEmpty) { | |
736 return null; | |
737 } | |
738 try { | |
739 // Force the creation of an escaped URI to deal with spaces, etc. | |
740 return _internalResolveUri( | |
741 containingSource, parseUriWithException(containedUri)); | |
742 } catch (exception, stackTrace) { | |
743 String containingFullName = | |
744 containingSource != null ? containingSource.fullName : '<null>'; | |
745 AnalysisEngine.instance.logger.logError( | |
746 "Could not resolve URI ($containedUri) relative to source ($containing
FullName)", | |
747 new CaughtException(exception, stackTrace)); | |
748 return null; | |
749 } | |
750 } | |
751 | |
752 /** | |
753 * Return an absolute URI that represents the given source, or `null` if a val
id URI cannot | |
754 * be computed. | |
755 * | |
756 * @param source the source to get URI for | |
757 * @return the absolute URI representing the given source | |
758 */ | |
759 Uri restoreUri(Source source) { | |
760 // First see if a resolver can restore the URI. | |
761 for (UriResolver resolver in _resolvers) { | |
762 Uri uri = resolver.restoreAbsolute(source); | |
763 if (uri != null) { | |
764 // Now see if there's a package mapping. | |
765 Uri packageMappedUri = _getPackageMapping(uri); | |
766 if (packageMappedUri != null) { | |
767 return packageMappedUri; | |
768 } | |
769 // Fall back to the resolver's computed URI. | |
770 return uri; | |
771 } | |
772 } | |
773 | |
774 return null; | |
775 } | |
776 | |
777 Uri _getPackageMapping(Uri sourceUri) { | |
778 if (_packages == null) { | |
779 return null; | |
780 } | |
781 if (sourceUri.scheme != 'file') { | |
782 //TODO(pquitslund): verify this works for non-file URIs. | |
783 return null; | |
784 } | |
785 | |
786 Uri packageUri; | |
787 _packages.asMap().forEach((String name, Uri uri) { | |
788 if (packageUri == null) { | |
789 if (utils.startsWith(sourceUri, uri)) { | |
790 packageUri = Uri.parse( | |
791 'package:$name/${sourceUri.path.substring(uri.path.length)}'); | |
792 } | |
793 } | |
794 }); | |
795 return packageUri; | |
796 } | |
797 | |
798 /** | |
799 * Return a source object representing the URI that results from resolving the
given (possibly | |
800 * relative) contained URI against the URI associated with an existing source
object, or | |
801 * `null` if the URI could not be resolved. | |
802 * | |
803 * @param containingSource the source containing the given URI | |
804 * @param containedUri the (possibly relative) URI to be resolved against the
containing source | |
805 * @return the source representing the contained URI | |
806 * @throws AnalysisException if either the contained URI is invalid or if it c
annot be resolved | |
807 * against the source object's URI | |
808 */ | |
809 Source _internalResolveUri(Source containingSource, Uri containedUri) { | |
810 if (!containedUri.isAbsolute) { | |
811 if (containingSource == null) { | |
812 throw new AnalysisException( | |
813 "Cannot resolve a relative URI without a containing source: $contain
edUri"); | |
814 } | |
815 containedUri = containingSource.resolveRelativeUri(containedUri); | |
816 } | |
817 // Now check .packages. | |
818 if (_packages != null && containedUri.scheme == 'package') { | |
819 Uri packageUri = | |
820 _packages.resolve(containedUri, notFound: (Uri packageUri) => null); | |
821 // Ensure scheme is set. | |
822 if (packageUri != null && packageUri.scheme == '') { | |
823 packageUri = packageUri.replace(scheme: 'file'); | |
824 } | |
825 containedUri = packageUri; | |
826 } | |
827 for (UriResolver resolver in _resolvers) { | |
828 Source result = resolver.resolveAbsolute(containedUri); | |
829 if (result != null) { | |
830 return result; | |
831 } | |
832 } | |
833 return null; | |
834 } | |
835 } | |
836 | |
837 /** | |
838 * The enumeration `SourceKind` defines the different kinds of sources that are
known to the | |
839 * analysis engine. | |
840 */ | |
841 class SourceKind extends Enum<SourceKind> { | |
842 /** | |
843 * A source containing HTML. The HTML might or might not contain Dart scripts. | |
844 */ | |
845 static const SourceKind HTML = const SourceKind('HTML', 0); | |
846 | |
847 /** | |
848 * A Dart compilation unit that is not a part of another library. Libraries mi
ght or might not | |
849 * contain any directives, including a library directive. | |
850 */ | |
851 static const SourceKind LIBRARY = const SourceKind('LIBRARY', 1); | |
852 | |
853 /** | |
854 * A Dart compilation unit that is part of another library. Parts contain a pa
rt-of directive. | |
855 */ | |
856 static const SourceKind PART = const SourceKind('PART', 2); | |
857 | |
858 /** | |
859 * An unknown kind of source. Used both when it is not possible to identify th
e kind of a source | |
860 * and also when the kind of a source is not known without performing a comput
ation and the client | |
861 * does not want to spend the time to identify the kind. | |
862 */ | |
863 static const SourceKind UNKNOWN = const SourceKind('UNKNOWN', 3); | |
864 | |
865 static const List<SourceKind> values = const [HTML, LIBRARY, PART, UNKNOWN]; | |
866 | |
867 const SourceKind(String name, int ordinal) : super(name, ordinal); | |
868 } | |
869 | |
870 /** | |
871 * A source range defines an [Element]'s source coordinates relative to its [Sou
rce]. | |
872 */ | |
873 class SourceRange { | |
874 /** | |
875 * An empty [SourceRange] with offset `0` and length `0`. | |
876 */ | |
877 static SourceRange EMPTY = new SourceRange(0, 0); | |
878 | |
879 /** | |
880 * The 0-based index of the first character of the source code for this elemen
t, relative to the | |
881 * source buffer in which this element is contained. | |
882 */ | |
883 final int offset; | |
884 | |
885 /** | |
886 * The number of characters of the source code for this element, relative to t
he source buffer in | |
887 * which this element is contained. | |
888 */ | |
889 final int length; | |
890 | |
891 /** | |
892 * Initialize a newly created source range using the given offset and the give
n length. | |
893 * | |
894 * @param offset the given offset | |
895 * @param length the given length | |
896 */ | |
897 SourceRange(this.offset, this.length); | |
898 | |
899 /** | |
900 * @return the 0-based index of the after-last character of the source code fo
r this element, | |
901 * relative to the source buffer in which this element is contained. | |
902 */ | |
903 int get end => offset + length; | |
904 | |
905 @override | |
906 int get hashCode => 31 * offset + length; | |
907 | |
908 @override | |
909 bool operator ==(Object obj) { | |
910 if (obj is! SourceRange) { | |
911 return false; | |
912 } | |
913 SourceRange sourceRange = obj as SourceRange; | |
914 return sourceRange.offset == offset && sourceRange.length == length; | |
915 } | |
916 | |
917 /** | |
918 * @return `true` if <code>x</code> is in [offset, offset + length) interval. | |
919 */ | |
920 bool contains(int x) => offset <= x && x < offset + length; | |
921 | |
922 /** | |
923 * @return `true` if <code>x</code> is in (offset, offset + length) interval. | |
924 */ | |
925 bool containsExclusive(int x) => offset < x && x < offset + length; | |
926 | |
927 /** | |
928 * @return `true` if <code>otherRange</code> covers this [SourceRange]. | |
929 */ | |
930 bool coveredBy(SourceRange otherRange) => otherRange.covers(this); | |
931 | |
932 /** | |
933 * @return `true` if this [SourceRange] covers <code>otherRange</code>. | |
934 */ | |
935 bool covers(SourceRange otherRange) => | |
936 offset <= otherRange.offset && otherRange.end <= end; | |
937 | |
938 /** | |
939 * @return `true` if this [SourceRange] ends in <code>otherRange</code>. | |
940 */ | |
941 bool endsIn(SourceRange otherRange) { | |
942 int thisEnd = end; | |
943 return otherRange.contains(thisEnd); | |
944 } | |
945 | |
946 /** | |
947 * @return the expanded instance of [SourceRange], which has the same center. | |
948 */ | |
949 SourceRange getExpanded(int delta) => | |
950 new SourceRange(offset - delta, delta + length + delta); | |
951 | |
952 /** | |
953 * @return the instance of [SourceRange] with end moved on "delta". | |
954 */ | |
955 SourceRange getMoveEnd(int delta) => new SourceRange(offset, length + delta); | |
956 | |
957 /** | |
958 * @return the expanded translated of [SourceRange], with moved start and the
same length. | |
959 */ | |
960 SourceRange getTranslated(int delta) => | |
961 new SourceRange(offset + delta, length); | |
962 | |
963 /** | |
964 * @return the minimal [SourceRange] that cover this and the given [SourceRang
e]s. | |
965 */ | |
966 SourceRange getUnion(SourceRange other) { | |
967 int newOffset = math.min(offset, other.offset); | |
968 int newEnd = math.max(offset + length, other.offset + other.length); | |
969 return new SourceRange(newOffset, newEnd - newOffset); | |
970 } | |
971 | |
972 /** | |
973 * @return `true` if this [SourceRange] intersects with given. | |
974 */ | |
975 bool intersects(SourceRange other) { | |
976 if (other == null) { | |
977 return false; | |
978 } | |
979 if (end <= other.offset) { | |
980 return false; | |
981 } | |
982 if (offset >= other.end) { | |
983 return false; | |
984 } | |
985 return true; | |
986 } | |
987 | |
988 /** | |
989 * Return `true` if this [SourceRange] starts in the [otherRange]. | |
990 */ | |
991 bool startsIn(SourceRange otherRange) => otherRange.contains(offset); | |
992 | |
993 @override | |
994 String toString() => '[offset=$offset, length=$length]'; | |
995 } | |
996 | |
997 /** | |
998 * The enumeration `UriKind` defines the different kinds of URI's that are known
to the | |
999 * analysis engine. These are used to keep track of the kind of URI associated w
ith a given source. | |
1000 */ | |
1001 class UriKind extends Enum<UriKind> { | |
1002 /** | |
1003 * A 'dart:' URI. | |
1004 */ | |
1005 static const UriKind DART_URI = const UriKind('DART_URI', 0, 0x64); | |
1006 | |
1007 /** | |
1008 * A 'file:' URI. | |
1009 */ | |
1010 static const UriKind FILE_URI = const UriKind('FILE_URI', 1, 0x66); | |
1011 | |
1012 /** | |
1013 * A 'package:' URI. | |
1014 */ | |
1015 static const UriKind PACKAGE_URI = const UriKind('PACKAGE_URI', 2, 0x70); | |
1016 | |
1017 static const List<UriKind> values = const [DART_URI, FILE_URI, PACKAGE_URI]; | |
1018 | |
1019 /** | |
1020 * The single character encoding used to identify this kind of URI. | |
1021 */ | |
1022 final int encoding; | |
1023 | |
1024 /** | |
1025 * Initialize a newly created URI kind to have the given encoding. | |
1026 * | |
1027 * @param encoding the single character encoding used to identify this kind of
URI. | |
1028 */ | |
1029 const UriKind(String name, int ordinal, this.encoding) : super(name, ordinal); | |
1030 | |
1031 /** | |
1032 * Return the URI kind represented by the given encoding, or `null` if there i
s no kind with | |
1033 * the given encoding. | |
1034 * | |
1035 * @param encoding the single character encoding used to identify the URI kind
to be returned | |
1036 * @return the URI kind represented by the given encoding | |
1037 */ | |
1038 static UriKind fromEncoding(int encoding) { | |
1039 while (true) { | |
1040 if (encoding == 0x64) { | |
1041 return DART_URI; | |
1042 } else if (encoding == 0x66) { | |
1043 return FILE_URI; | |
1044 } else if (encoding == 0x70) { | |
1045 return PACKAGE_URI; | |
1046 } | |
1047 break; | |
1048 } | |
1049 return null; | |
1050 } | |
1051 } | |
1052 | |
1053 /** | |
1054 * The abstract class `UriResolver` defines the behavior of objects that are use
d to resolve | |
1055 * URI's for a source factory. Subclasses of this class are expected to resolve
a single scheme of | |
1056 * absolute URI. | |
1057 */ | |
1058 abstract class UriResolver { | |
1059 /** | |
1060 * Resolve the given absolute URI. Return a [Source] representing the file to
which | |
1061 * it was resolved, whether or not the resulting source exists, or `null` if i
t could not be | |
1062 * resolved because the URI is invalid. | |
1063 * | |
1064 * @param uri the URI to be resolved | |
1065 * @return a [Source] representing the file to which given URI was resolved | |
1066 */ | |
1067 Source resolveAbsolute(Uri uri); | |
1068 | |
1069 /** | |
1070 * Return an absolute URI that represents the given source, or `null` if a val
id URI cannot | |
1071 * be computed. | |
1072 * | |
1073 * @param source the source to get URI for | |
1074 * @return the absolute URI representing the given source | |
1075 */ | |
1076 Uri restoreAbsolute(Source source) => null; | |
1077 } | |
OLD | NEW |