| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, 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 library path.context; | |
| 6 | |
| 7 import 'internal_style.dart'; | |
| 8 import 'style.dart'; | |
| 9 import 'parsed_path.dart'; | |
| 10 import 'path_exception.dart'; | |
| 11 import '../path.dart' as p; | |
| 12 | |
| 13 Context createInternal() => new Context._internal(); | |
| 14 | |
| 15 /// An instantiable class for manipulating paths. Unlike the top-level | |
| 16 /// functions, this lets you explicitly select what platform the paths will use. | |
| 17 class Context { | |
| 18 /// Creates a new path context for the given style and current directory. | |
| 19 /// | |
| 20 /// If [style] is omitted, it uses the host operating system's path style. If | |
| 21 /// only [current] is omitted, it defaults ".". If *both* [style] and | |
| 22 /// [current] are omitted, [current] defaults to the real current working | |
| 23 /// directory. | |
| 24 /// | |
| 25 /// On the browser, [style] defaults to [Style.url] and [current] defaults to | |
| 26 /// the current URL. | |
| 27 factory Context({Style style, String current}) { | |
| 28 if (current == null) { | |
| 29 if (style == null) { | |
| 30 current = p.current; | |
| 31 } else { | |
| 32 current = "."; | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 if (style == null) { | |
| 37 style = Style.platform; | |
| 38 } else if (style is! InternalStyle) { | |
| 39 throw new ArgumentError("Only styles defined by the path package are " | |
| 40 "allowed."); | |
| 41 } | |
| 42 | |
| 43 return new Context._(style, current); | |
| 44 } | |
| 45 | |
| 46 /// Create a [Context] to be used internally within path. | |
| 47 Context._internal() : style = Style.platform, _current = null; | |
| 48 | |
| 49 Context._(this.style, this._current); | |
| 50 | |
| 51 /// The style of path that this context works with. | |
| 52 final InternalStyle style; | |
| 53 | |
| 54 /// The current directory given when Context was created. If null, current | |
| 55 /// directory is evaluated from 'p.current'. | |
| 56 final String _current; | |
| 57 | |
| 58 /// The current directory that relative paths are relative to. | |
| 59 String get current => _current != null ? _current : p.current; | |
| 60 | |
| 61 /// Gets the path separator for the context's [style]. On Mac and Linux, | |
| 62 /// this is `/`. On Windows, it's `\`. | |
| 63 String get separator => style.separator; | |
| 64 | |
| 65 /// Creates a new path by appending the given path parts to [current]. | |
| 66 /// Equivalent to [join()] with [current] as the first argument. Example: | |
| 67 /// | |
| 68 /// var context = new Context(current: '/root'); | |
| 69 /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' | |
| 70 /// | |
| 71 /// If [current] isn't absolute, this won't return an absolute path. | |
| 72 String absolute(String part1, [String part2, String part3, String part4, | |
| 73 String part5, String part6, String part7]) { | |
| 74 return join(current, part1, part2, part3, part4, part5, part6, part7); | |
| 75 } | |
| 76 | |
| 77 /// Gets the part of [path] after the last separator on the context's | |
| 78 /// platform. | |
| 79 /// | |
| 80 /// context.basename('path/to/foo.dart'); // -> 'foo.dart' | |
| 81 /// context.basename('path/to'); // -> 'to' | |
| 82 /// | |
| 83 /// Trailing separators are ignored. | |
| 84 /// | |
| 85 /// context.basename('path/to/'); // -> 'to' | |
| 86 String basename(String path) => _parse(path).basename; | |
| 87 | |
| 88 /// Gets the part of [path] after the last separator on the context's | |
| 89 /// platform, and without any trailing file extension. | |
| 90 /// | |
| 91 /// context.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' | |
| 92 /// | |
| 93 /// Trailing separators are ignored. | |
| 94 /// | |
| 95 /// context.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' | |
| 96 String basenameWithoutExtension(String path) => | |
| 97 _parse(path).basenameWithoutExtension; | |
| 98 | |
| 99 /// Gets the part of [path] before the last separator. | |
| 100 /// | |
| 101 /// context.dirname('path/to/foo.dart'); // -> 'path/to' | |
| 102 /// context.dirname('path/to'); // -> 'path' | |
| 103 /// | |
| 104 /// Trailing separators are ignored. | |
| 105 /// | |
| 106 /// context.dirname('path/to/'); // -> 'path' | |
| 107 String dirname(String path) { | |
| 108 var parsed = _parse(path); | |
| 109 parsed.removeTrailingSeparators(); | |
| 110 if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; | |
| 111 if (parsed.parts.length == 1) { | |
| 112 return parsed.root == null ? '.' : parsed.root; | |
| 113 } | |
| 114 parsed.parts.removeLast(); | |
| 115 parsed.separators.removeLast(); | |
| 116 parsed.removeTrailingSeparators(); | |
| 117 return parsed.toString(); | |
| 118 } | |
| 119 | |
| 120 /// Gets the file extension of [path]: the portion of [basename] from the last | |
| 121 /// `.` to the end (including the `.` itself). | |
| 122 /// | |
| 123 /// context.extension('path/to/foo.dart'); // -> '.dart' | |
| 124 /// context.extension('path/to/foo'); // -> '' | |
| 125 /// context.extension('path.to/foo'); // -> '' | |
| 126 /// context.extension('path/to/foo.dart.js'); // -> '.js' | |
| 127 /// | |
| 128 /// If the file name starts with a `.`, then it is not considered an | |
| 129 /// extension: | |
| 130 /// | |
| 131 /// context.extension('~/.bashrc'); // -> '' | |
| 132 /// context.extension('~/.notes.txt'); // -> '.txt' | |
| 133 String extension(String path) => _parse(path).extension; | |
| 134 | |
| 135 // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. | |
| 136 /// Returns the root of [path] if it's absolute, or an empty string if it's | |
| 137 /// relative. | |
| 138 /// | |
| 139 /// // Unix | |
| 140 /// context.rootPrefix('path/to/foo'); // -> '' | |
| 141 /// context.rootPrefix('/path/to/foo'); // -> '/' | |
| 142 /// | |
| 143 /// // Windows | |
| 144 /// context.rootPrefix(r'path\to\foo'); // -> '' | |
| 145 /// context.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' | |
| 146 /// | |
| 147 /// // URL | |
| 148 /// context.rootPrefix('path/to/foo'); // -> '' | |
| 149 /// context.rootPrefix('http://dartlang.org/path/to/foo'); | |
| 150 /// // -> 'http://dartlang.org' | |
| 151 String rootPrefix(String path) => path.substring(0, style.rootLength(path)); | |
| 152 | |
| 153 /// Returns `true` if [path] is an absolute path and `false` if it is a | |
| 154 /// relative path. | |
| 155 /// | |
| 156 /// On POSIX systems, absolute paths start with a `/` (forward slash). On | |
| 157 /// Windows, an absolute path starts with `\\`, or a drive letter followed by | |
| 158 /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and | |
| 159 /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. | |
| 160 /// | |
| 161 /// URLs that start with `/` are known as "root-relative", since they're | |
| 162 /// relative to the root of the current URL. Since root-relative paths are | |
| 163 /// still absolute in every other sense, [isAbsolute] will return true for | |
| 164 /// them. They can be detected using [isRootRelative]. | |
| 165 bool isAbsolute(String path) => style.rootLength(path) > 0; | |
| 166 | |
| 167 /// Returns `true` if [path] is a relative path and `false` if it is absolute. | |
| 168 /// On POSIX systems, absolute paths start with a `/` (forward slash). On | |
| 169 /// Windows, an absolute path starts with `\\`, or a drive letter followed by | |
| 170 /// `:/` or `:\`. | |
| 171 bool isRelative(String path) => !this.isAbsolute(path); | |
| 172 | |
| 173 /// Returns `true` if [path] is a root-relative path and `false` if it's not. | |
| 174 /// | |
| 175 /// URLs that start with `/` are known as "root-relative", since they're | |
| 176 /// relative to the root of the current URL. Since root-relative paths are | |
| 177 /// still absolute in every other sense, [isAbsolute] will return true for | |
| 178 /// them. They can be detected using [isRootRelative]. | |
| 179 /// | |
| 180 /// No POSIX and Windows paths are root-relative. | |
| 181 bool isRootRelative(String path) => style.isRootRelative(path); | |
| 182 | |
| 183 /// Joins the given path parts into a single path. Example: | |
| 184 /// | |
| 185 /// context.join('path', 'to', 'foo'); // -> 'path/to/foo' | |
| 186 /// | |
| 187 /// If any part ends in a path separator, then a redundant separator will not | |
| 188 /// be added: | |
| 189 /// | |
| 190 /// context.join('path/', 'to', 'foo'); // -> 'path/to/foo | |
| 191 /// | |
| 192 /// If a part is an absolute path, then anything before that will be ignored: | |
| 193 /// | |
| 194 /// context.join('path', '/to', 'foo'); // -> '/to/foo' | |
| 195 /// | |
| 196 String join(String part1, [String part2, String part3, String part4, | |
| 197 String part5, String part6, String part7, String part8]) { | |
| 198 var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; | |
| 199 _validateArgList("join", parts); | |
| 200 return joinAll(parts.where((part) => part != null)); | |
| 201 } | |
| 202 | |
| 203 /// Joins the given path parts into a single path. Example: | |
| 204 /// | |
| 205 /// context.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' | |
| 206 /// | |
| 207 /// If any part ends in a path separator, then a redundant separator will not | |
| 208 /// be added: | |
| 209 /// | |
| 210 /// context.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo | |
| 211 /// | |
| 212 /// If a part is an absolute path, then anything before that will be ignored: | |
| 213 /// | |
| 214 /// context.joinAll(['path', '/to', 'foo']); // -> '/to/foo' | |
| 215 /// | |
| 216 /// For a fixed number of parts, [join] is usually terser. | |
| 217 String joinAll(Iterable<String> parts) { | |
| 218 var buffer = new StringBuffer(); | |
| 219 var needsSeparator = false; | |
| 220 var isAbsoluteAndNotRootRelative = false; | |
| 221 | |
| 222 for (var part in parts.where((part) => part != '')) { | |
| 223 if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { | |
| 224 // If the new part is root-relative, it preserves the previous root but | |
| 225 // replaces the path after it. | |
| 226 var parsed = _parse(part); | |
| 227 parsed.root = this.rootPrefix(buffer.toString()); | |
| 228 if (style.needsSeparator(parsed.root)) { | |
| 229 parsed.separators[0] = style.separator; | |
| 230 } | |
| 231 buffer.clear(); | |
| 232 buffer.write(parsed.toString()); | |
| 233 } else if (this.isAbsolute(part)) { | |
| 234 isAbsoluteAndNotRootRelative = !this.isRootRelative(part); | |
| 235 // An absolute path discards everything before it. | |
| 236 buffer.clear(); | |
| 237 buffer.write(part); | |
| 238 } else { | |
| 239 if (part.length > 0 && style.containsSeparator(part[0])) { | |
| 240 // The part starts with a separator, so we don't need to add one. | |
| 241 } else if (needsSeparator) { | |
| 242 buffer.write(separator); | |
| 243 } | |
| 244 | |
| 245 buffer.write(part); | |
| 246 } | |
| 247 | |
| 248 // Unless this part ends with a separator, we'll need to add one before | |
| 249 // the next part. | |
| 250 needsSeparator = style.needsSeparator(part); | |
| 251 } | |
| 252 | |
| 253 return buffer.toString(); | |
| 254 } | |
| 255 | |
| 256 // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. | |
| 257 /// Splits [path] into its components using the current platform's | |
| 258 /// [separator]. Example: | |
| 259 /// | |
| 260 /// context.split('path/to/foo'); // -> ['path', 'to', 'foo'] | |
| 261 /// | |
| 262 /// The path will *not* be normalized before splitting. | |
| 263 /// | |
| 264 /// context.split('path/../foo'); // -> ['path', '..', 'foo'] | |
| 265 /// | |
| 266 /// If [path] is absolute, the root directory will be the first element in the | |
| 267 /// array. Example: | |
| 268 /// | |
| 269 /// // Unix | |
| 270 /// context.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] | |
| 271 /// | |
| 272 /// // Windows | |
| 273 /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] | |
| 274 List<String> split(String path) { | |
| 275 var parsed = _parse(path); | |
| 276 // Filter out empty parts that exist due to multiple separators in a row. | |
| 277 parsed.parts = parsed.parts.where((part) => !part.isEmpty) | |
| 278 .toList(); | |
| 279 if (parsed.root != null) parsed.parts.insert(0, parsed.root); | |
| 280 return parsed.parts; | |
| 281 } | |
| 282 | |
| 283 /// Normalizes [path], simplifying it by handling `..`, and `.`, and | |
| 284 /// removing redundant path separators whenever possible. | |
| 285 /// | |
| 286 /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' | |
| 287 String normalize(String path) { | |
| 288 var parsed = _parse(path); | |
| 289 parsed.normalize(); | |
| 290 return parsed.toString(); | |
| 291 } | |
| 292 | |
| 293 /// Attempts to convert [path] to an equivalent relative path relative to | |
| 294 /// [root]. | |
| 295 /// | |
| 296 /// var context = new Context(current: '/root/path'); | |
| 297 /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' | |
| 298 /// context.relative('/root/other.dart'); // -> '../other.dart' | |
| 299 /// | |
| 300 /// If the [from] argument is passed, [path] is made relative to that instead. | |
| 301 /// | |
| 302 /// context.relative('/root/path/a/b.dart', | |
| 303 /// from: '/root/path'); // -> 'a/b.dart' | |
| 304 /// context.relative('/root/other.dart', | |
| 305 /// from: '/root/path'); // -> '../other.dart' | |
| 306 /// | |
| 307 /// If [path] and/or [from] are relative paths, they are assumed to be | |
| 308 /// relative to [current]. | |
| 309 /// | |
| 310 /// Since there is no relative path from one drive letter to another on | |
| 311 /// Windows, this will return an absolute path in that case. | |
| 312 /// | |
| 313 /// context.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other' | |
| 314 /// | |
| 315 /// This will also return an absolute path if an absolute [path] is passed to | |
| 316 /// a context with a relative path for [current]. | |
| 317 /// | |
| 318 /// var context = new Context(r'some/relative/path'); | |
| 319 /// context.relative(r'/absolute/path'); // -> '/absolute/path' | |
| 320 /// | |
| 321 /// If [root] is relative, it may be impossible to determine a path from | |
| 322 /// [from] to [path]. For example, if [root] and [path] are "." and [from] is | |
| 323 /// "/", no path can be determined. In this case, a [PathException] will be | |
| 324 /// thrown. | |
| 325 String relative(String path, {String from}) { | |
| 326 from = from == null ? current : this.join(current, from); | |
| 327 | |
| 328 // We can't determine the path from a relative path to an absolute path. | |
| 329 if (this.isRelative(from) && this.isAbsolute(path)) { | |
| 330 return this.normalize(path); | |
| 331 } | |
| 332 | |
| 333 // If the given path is relative, resolve it relative to the context's | |
| 334 // current directory. | |
| 335 if (this.isRelative(path) || this.isRootRelative(path)) { | |
| 336 path = this.absolute(path); | |
| 337 } | |
| 338 | |
| 339 // If the path is still relative and `from` is absolute, we're unable to | |
| 340 // find a path from `from` to `path`. | |
| 341 if (this.isRelative(path) && this.isAbsolute(from)) { | |
| 342 throw new PathException('Unable to find a path to "$path" from "$from".'); | |
| 343 } | |
| 344 | |
| 345 var fromParsed = _parse(from)..normalize(); | |
| 346 var pathParsed = _parse(path)..normalize(); | |
| 347 | |
| 348 if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { | |
| 349 return pathParsed.toString(); | |
| 350 } | |
| 351 | |
| 352 // If the root prefixes don't match (for example, different drive letters | |
| 353 // on Windows), then there is no relative path, so just return the absolute | |
| 354 // one. In Windows, drive letters are case-insenstive and we allow | |
| 355 // calculation of relative paths, even if a path has not been normalized. | |
| 356 if (fromParsed.root != pathParsed.root && | |
| 357 ((fromParsed.root == null || pathParsed.root == null) || | |
| 358 fromParsed.root.toLowerCase().replaceAll('/', '\\') != | |
| 359 pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { | |
| 360 return pathParsed.toString(); | |
| 361 } | |
| 362 | |
| 363 // Strip off their common prefix. | |
| 364 while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && | |
| 365 fromParsed.parts[0] == pathParsed.parts[0]) { | |
| 366 fromParsed.parts.removeAt(0); | |
| 367 fromParsed.separators.removeAt(1); | |
| 368 pathParsed.parts.removeAt(0); | |
| 369 pathParsed.separators.removeAt(1); | |
| 370 } | |
| 371 | |
| 372 // If there are any directories left in the from path, we need to walk up | |
| 373 // out of them. If a directory left in the from path is '..', it cannot | |
| 374 // be cancelled by adding a '..'. | |
| 375 if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { | |
| 376 throw new PathException('Unable to find a path to "$path" from "$from".'); | |
| 377 } | |
| 378 pathParsed.parts.insertAll(0, | |
| 379 new List.filled(fromParsed.parts.length, '..')); | |
| 380 pathParsed.separators[0] = ''; | |
| 381 pathParsed.separators.insertAll(1, | |
| 382 new List.filled(fromParsed.parts.length, style.separator)); | |
| 383 | |
| 384 // Corner case: the paths completely collapsed. | |
| 385 if (pathParsed.parts.length == 0) return '.'; | |
| 386 | |
| 387 // Corner case: path was '.' and some '..' directories were added in front. | |
| 388 // Don't add a final '/.' in that case. | |
| 389 if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { | |
| 390 pathParsed.parts.removeLast(); | |
| 391 pathParsed.separators..removeLast()..removeLast()..add(''); | |
| 392 } | |
| 393 | |
| 394 // Make it relative. | |
| 395 pathParsed.root = ''; | |
| 396 pathParsed.removeTrailingSeparators(); | |
| 397 | |
| 398 return pathParsed.toString(); | |
| 399 } | |
| 400 | |
| 401 /// Returns `true` if [child] is a path beneath `parent`, and `false` | |
| 402 /// otherwise. | |
| 403 /// | |
| 404 /// path.isWithin('/root/path', '/root/path/a'); // -> true | |
| 405 /// path.isWithin('/root/path', '/root/other'); // -> false | |
| 406 /// path.isWithin('/root/path', '/root/path'); // -> false | |
| 407 bool isWithin(String parent, String child) { | |
| 408 var relative; | |
| 409 try { | |
| 410 relative = this.relative(child, from: parent); | |
| 411 } on PathException catch (_) { | |
| 412 // If no relative path from [parent] to [child] is found, [child] | |
| 413 // definitely isn't a child of [parent]. | |
| 414 return false; | |
| 415 } | |
| 416 | |
| 417 var parts = this.split(relative); | |
| 418 return this.isRelative(relative) && parts.first != '..' && | |
| 419 parts.first != '.'; | |
| 420 } | |
| 421 | |
| 422 /// Removes a trailing extension from the last part of [path]. | |
| 423 /// | |
| 424 /// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' | |
| 425 String withoutExtension(String path) { | |
| 426 var parsed = _parse(path); | |
| 427 | |
| 428 for (var i = parsed.parts.length - 1; i >= 0; i--) { | |
| 429 if (!parsed.parts[i].isEmpty) { | |
| 430 parsed.parts[i] = parsed.basenameWithoutExtension; | |
| 431 break; | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 return parsed.toString(); | |
| 436 } | |
| 437 | |
| 438 /// Returns the path represented by [uri], which may be a [String] or a [Uri]. | |
| 439 /// | |
| 440 /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL | |
| 441 /// style, this will just convert [uri] to a string. | |
| 442 /// | |
| 443 /// // POSIX | |
| 444 /// context.fromUri('file:///path/to/foo') | |
| 445 /// // -> '/path/to/foo' | |
| 446 /// | |
| 447 /// // Windows | |
| 448 /// context.fromUri('file:///C:/path/to/foo') | |
| 449 /// // -> r'C:\path\to\foo' | |
| 450 /// | |
| 451 /// // URL | |
| 452 /// context.fromUri('http://dartlang.org/path/to/foo') | |
| 453 /// // -> 'http://dartlang.org/path/to/foo' | |
| 454 /// | |
| 455 /// If [uri] is relative, a relative path will be returned. | |
| 456 /// | |
| 457 /// path.fromUri('path/to/foo'); // -> 'path/to/foo' | |
| 458 String fromUri(uri) { | |
| 459 if (uri is String) uri = Uri.parse(uri); | |
| 460 return style.pathFromUri(uri); | |
| 461 } | |
| 462 | |
| 463 /// Returns the URI that represents [path]. | |
| 464 /// | |
| 465 /// For POSIX and Windows styles, this will return a `file:` URI. For the URL | |
| 466 /// style, this will just convert [path] to a [Uri]. | |
| 467 /// | |
| 468 /// // POSIX | |
| 469 /// context.toUri('/path/to/foo') | |
| 470 /// // -> Uri.parse('file:///path/to/foo') | |
| 471 /// | |
| 472 /// // Windows | |
| 473 /// context.toUri(r'C:\path\to\foo') | |
| 474 /// // -> Uri.parse('file:///C:/path/to/foo') | |
| 475 /// | |
| 476 /// // URL | |
| 477 /// context.toUri('http://dartlang.org/path/to/foo') | |
| 478 /// // -> Uri.parse('http://dartlang.org/path/to/foo') | |
| 479 Uri toUri(String path) { | |
| 480 if (isRelative(path)) { | |
| 481 return style.relativePathToUri(path); | |
| 482 } else { | |
| 483 return style.absolutePathToUri(join(current, path)); | |
| 484 } | |
| 485 } | |
| 486 | |
| 487 /// Returns a terse, human-readable representation of [uri]. | |
| 488 /// | |
| 489 /// [uri] can be a [String] or a [Uri]. If it can be made relative to the | |
| 490 /// current working directory, that's done. Otherwise, it's returned as-is. | |
| 491 /// This gracefully handles non-`file:` URIs for [Style.posix] and | |
| 492 /// [Style.windows]. | |
| 493 /// | |
| 494 /// The returned value is meant for human consumption, and may be either URI- | |
| 495 /// or path-formatted. | |
| 496 /// | |
| 497 /// // POSIX | |
| 498 /// var context = new Context(current: '/root/path'); | |
| 499 /// context.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' | |
| 500 /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' | |
| 501 /// | |
| 502 /// // Windows | |
| 503 /// var context = new Context(current: r'C:\root\path'); | |
| 504 /// context.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' | |
| 505 /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' | |
| 506 /// | |
| 507 /// // URL | |
| 508 /// var context = new Context(current: 'http://dartlang.org/root/path'); | |
| 509 /// context.prettyUri('http://dartlang.org/root/path/a/b.dart'); | |
| 510 /// // -> r'a/b.dart' | |
| 511 /// context.prettyUri('file:///root/path'); // -> 'file:///root/path' | |
| 512 String prettyUri(uri) { | |
| 513 if (uri is String) uri = Uri.parse(uri); | |
| 514 if (uri.scheme == 'file' && style == Style.url) return uri.toString(); | |
| 515 if (uri.scheme != 'file' && uri.scheme != '' && style != Style.url) { | |
| 516 return uri.toString(); | |
| 517 } | |
| 518 | |
| 519 var path = normalize(fromUri(uri)); | |
| 520 var rel = relative(path); | |
| 521 var components = split(rel); | |
| 522 | |
| 523 // Only return a relative path if it's actually shorter than the absolute | |
| 524 // path. This avoids ugly things like long "../" chains to get to the root | |
| 525 // and then go back down. | |
| 526 return split(rel).length > split(path).length ? path : rel; | |
| 527 } | |
| 528 | |
| 529 ParsedPath _parse(String path) => new ParsedPath.parse(path, style); | |
| 530 } | |
| 531 | |
| 532 /// Validates that there are no non-null arguments following a null one and | |
| 533 /// throws an appropriate [ArgumentError] on failure. | |
| 534 _validateArgList(String method, List<String> args) { | |
| 535 for (var i = 1; i < args.length; i++) { | |
| 536 // Ignore nulls hanging off the end. | |
| 537 if (args[i] == null || args[i - 1] != null) continue; | |
| 538 | |
| 539 var numArgs; | |
| 540 for (numArgs = args.length; numArgs >= 1; numArgs--) { | |
| 541 if (args[numArgs - 1] != null) break; | |
| 542 } | |
| 543 | |
| 544 // Show the arguments. | |
| 545 var message = new StringBuffer(); | |
| 546 message.write("$method("); | |
| 547 message.write(args.take(numArgs) | |
| 548 .map((arg) => arg == null ? "null" : '"$arg"') | |
| 549 .join(", ")); | |
| 550 message.write("): part ${i - 1} was null, but part $i was not."); | |
| 551 throw new ArgumentError(message.toString()); | |
| 552 } | |
| 553 } | |
| OLD | NEW |