| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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 * Tools for code generation. | 6 * Tools for code generation. |
| 7 */ | 7 */ |
| 8 library codegen.tools; | 8 library codegen.tools; |
| 9 | 9 |
| 10 import 'dart:io'; | 10 import 'dart:io'; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 } | 76 } |
| 77 | 77 |
| 78 /** | 78 /** |
| 79 * Execute [callback], indenting any code it outputs by two spaces. | 79 * Execute [callback], indenting any code it outputs by two spaces. |
| 80 */ | 80 */ |
| 81 void indent(void callback()) => indentSpecial(' ', ' ', callback); | 81 void indent(void callback()) => indentSpecial(' ', ' ', callback); |
| 82 | 82 |
| 83 /** | 83 /** |
| 84 * Execute [callback], using [additionalIndent] to indent any code it outputs. | 84 * Execute [callback], using [additionalIndent] to indent any code it outputs. |
| 85 */ | 85 */ |
| 86 void indentBy(String additionalIndent, void callback()) => indentSpecial(addit
ionalIndent, additionalIndent, callback); | 86 void indentBy(String additionalIndent, void callback()) => |
| 87 indentSpecial(additionalIndent, additionalIndent, callback); |
| 87 | 88 |
| 88 /** | 89 /** |
| 89 * Execute [callback], using [additionalIndent] to indent any code it outputs. | 90 * Execute [callback], using [additionalIndent] to indent any code it outputs. |
| 90 * The first line of output is indented by [firstAdditionalIndent] instead of | 91 * The first line of output is indented by [firstAdditionalIndent] instead of |
| 91 * [additionalIndent]. | 92 * [additionalIndent]. |
| 92 */ | 93 */ |
| 93 void indentSpecial(String firstAdditionalIndent, String additionalIndent, void
callback()) { | 94 void indentSpecial(String firstAdditionalIndent, String additionalIndent, void |
| 95 callback()) { |
| 94 String oldNextIndent = _state.nextIndent; | 96 String oldNextIndent = _state.nextIndent; |
| 95 String oldIndent = _state.indent; | 97 String oldIndent = _state.indent; |
| 96 try { | 98 try { |
| 97 _state.nextIndent += firstAdditionalIndent; | 99 _state.nextIndent += firstAdditionalIndent; |
| 98 _state.indent += additionalIndent; | 100 _state.indent += additionalIndent; |
| 99 callback(); | 101 callback(); |
| 100 } finally { | 102 } finally { |
| 101 _state.nextIndent = oldNextIndent; | 103 _state.nextIndent = oldNextIndent; |
| 102 _state.indent = oldIndent; | 104 _state.indent = oldIndent; |
| 103 } | 105 } |
| 104 } | 106 } |
| 105 | 107 |
| 106 /** | 108 /** |
| 107 * Measure the width of the current indentation level. | 109 * Measure the width of the current indentation level. |
| 108 */ | 110 */ |
| 109 int get indentWidth => _state.nextIndent.length; | 111 int get indentWidth => _state.nextIndent.length; |
| 110 | 112 |
| 111 /** | 113 /** |
| 112 * Generate a doc comment based on the HTML in [docs]. | 114 * Generate a doc comment based on the HTML in [docs]. |
| 113 * | 115 * |
| 114 * If [javadocStyle] is true, then the output is compatable with Javadoc, | 116 * If [javadocStyle] is true, then the output is compatable with Javadoc, |
| 115 * which understands certain HTML constructs. | 117 * which understands certain HTML constructs. |
| 116 */ | 118 */ |
| 117 void docComment(List<dom.Node> docs, {int width: 79, bool javadocStyle: false}
) { | 119 void docComment(List<dom.Node> docs, {int width: 79, bool javadocStyle: |
| 120 false}) { |
| 118 if (containsOnlyWhitespace(docs)) { | 121 if (containsOnlyWhitespace(docs)) { |
| 119 return; | 122 return; |
| 120 } | 123 } |
| 121 writeln('/**'); | 124 writeln('/**'); |
| 122 indentBy(' * ', () { | 125 indentBy(' * ', () { |
| 123 write(nodesToText(docs, width - _state.indent.length, javadocStyle)); | 126 write(nodesToText(docs, width - _state.indent.length, javadocStyle)); |
| 124 }); | 127 }); |
| 125 writeln(' */'); | 128 writeln(' */'); |
| 126 } | 129 } |
| 127 | 130 |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 295 buffer.add(new dom.Text(lines.join('\n$indent') + '\n')); | 298 buffer.add(new dom.Text(lines.join('\n$indent') + '\n')); |
| 296 indentNeeded = true; | 299 indentNeeded = true; |
| 297 } else { | 300 } else { |
| 298 buffer.add(new dom.Text(lines.join('\n$indent'))); | 301 buffer.add(new dom.Text(lines.join('\n$indent'))); |
| 299 indentNeeded = false; | 302 indentNeeded = false; |
| 300 } | 303 } |
| 301 } | 304 } |
| 302 } | 305 } |
| 303 | 306 |
| 304 /** | 307 /** |
| 305 * Type of functions used to compute the contents of generated files. | 308 * Type of functions used to compute the contents of a generated file. |
| 306 */ | 309 */ |
| 307 typedef String FileContentsComputer(); | 310 typedef String FileContentsComputer(); |
| 308 | 311 |
| 312 /** |
| 313 * Type of functions used to compute the contents of a set of generated files. |
| 314 */ |
| 309 typedef Map<String, FileContentsComputer> DirectoryContentsComputer(); | 315 typedef Map<String, FileContentsComputer> DirectoryContentsComputer(); |
| 310 | 316 |
| 311 abstract class GeneratedContent { | 317 abstract class GeneratedContent { |
| 312 FileSystemEntity get outputFile; | 318 FileSystemEntity get outputFile; |
| 313 bool check(); | 319 bool check(); |
| 314 void generate(); | 320 void generate(); |
| 315 } | 321 } |
| 316 | 322 |
| 317 /** | 323 /** |
| 318 * Class representing a single output file (either generated code or generated | 324 * Class representing a single output file (either generated code or generated |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 358 /** | 364 /** |
| 359 * Replace the file with the correct contents. [spec] is the "tool/spec" | 365 * Replace the file with the correct contents. [spec] is the "tool/spec" |
| 360 * directory. If [spec] is unspecified, it is assumed to be the directory | 366 * directory. If [spec] is unspecified, it is assumed to be the directory |
| 361 * containing Platform.executable. | 367 * containing Platform.executable. |
| 362 */ | 368 */ |
| 363 void generate() { | 369 void generate() { |
| 364 outputFile.writeAsStringSync(computeContents()); | 370 outputFile.writeAsStringSync(computeContents()); |
| 365 } | 371 } |
| 366 } | 372 } |
| 367 | 373 |
| 374 /** |
| 375 * Class representing a single output directory (either generated code or |
| 376 * generated HTML). No other content should exisit in the directory. |
| 377 */ |
| 368 class GeneratedDirectory extends GeneratedContent { | 378 class GeneratedDirectory extends GeneratedContent { |
| 369 | 379 |
| 380 /** |
| 381 * The path to the directory that will have the generated content. |
| 382 */ |
| 370 final String outputDirPath; | 383 final String outputDirPath; |
| 384 |
| 385 /** |
| 386 * Callback function which computes the directory contents. |
| 387 */ |
| 371 final DirectoryContentsComputer directoryContentsComputer; | 388 final DirectoryContentsComputer directoryContentsComputer; |
| 389 |
| 372 GeneratedDirectory(this.outputDirPath, this.directoryContentsComputer); | 390 GeneratedDirectory(this.outputDirPath, this.directoryContentsComputer); |
| 373 | 391 |
| 374 /** | 392 /** |
| 375 * Get a Directory object representing the output directory. | 393 * Get a Directory object representing the output directory. |
| 376 */ | 394 */ |
| 377 Directory get outputFile => new Directory(joinAll(posix.split(outputDirPath)))
; | 395 Directory get outputFile => |
| 396 new Directory(joinAll(posix.split(outputDirPath))); |
| 378 | 397 |
| 398 /** |
| 399 * Check whether the directory has the correct contents, and return true if it |
| 400 * does. |
| 401 */ |
| 379 @override | 402 @override |
| 380 bool check() { | 403 bool check() { |
| 381 // TODO (jwren) the lists of files in the directories need to be compared to | |
| 382 // ensure no unexpected files have been added | |
| 383 Map<String, FileContentsComputer> map = directoryContentsComputer(); | 404 Map<String, FileContentsComputer> map = directoryContentsComputer(); |
| 384 map.forEach((String file, FileContentsComputer fileContentsComputer) { | 405 try { |
| 385 String expectedContents = fileContentsComputer(); | 406 map.forEach((String file, FileContentsComputer fileContentsComputer) { |
| 386 File outputFile = new File(joinAll(posix.split(outputDirPath + file))); | 407 String expectedContents = fileContentsComputer(); |
| 387 try { | 408 File outputFile = |
| 409 new File(joinAll(posix.split(posix.join(outputDirPath, file)))); |
| 388 if (expectedContents != outputFile.readAsStringSync()) { | 410 if (expectedContents != outputFile.readAsStringSync()) { |
| 389 return false; | 411 return false; |
| 390 } | 412 } |
| 391 } catch (e) { | 413 }); |
| 392 // There was a problem reading the file (most likely because it didn't | 414 if (outputFile.listSync().length != map.length) { |
| 393 // exist). Treat that the same as if the file doesn't have the expected | 415 // The number of files generated doesn't match the number we expected to |
| 394 // contents. | 416 // generate. |
| 395 return false; | 417 return false; |
| 396 } | 418 } |
| 397 }); | 419 } catch (e) { |
| 420 // There was a problem reading the file (most likely because it didn't |
| 421 // exist). Treat that the same as if the file doesn't have the expected |
| 422 // contents. |
| 423 return false; |
| 424 } |
| 398 return true; | 425 return true; |
| 399 } | 426 } |
| 400 | 427 |
| 428 /** |
| 429 * Replace the directory with the correct contents. [spec] is the "tool/spec" |
| 430 * directory. If [spec] is unspecified, it is assumed to be the directory |
| 431 * containing Platform.executable. |
| 432 */ |
| 401 @override | 433 @override |
| 402 void generate() { | 434 void generate() { |
| 403 // TODO (jwren) Delete contents in the directory first. | 435 try { |
| 436 // delete the contents of the directory (and the directory itself) |
| 437 outputFile.deleteSync(recursive: true); |
| 438 } catch (e) { |
| 439 // Error caught while trying to delete the directory, this can happen if |
| 440 // it didn't yet exist. |
| 441 } |
| 442 // re-create the empty directory |
| 443 outputFile.createSync(recursive: true); |
| 444 |
| 445 // generate all of the files in the directory |
| 404 Map<String, FileContentsComputer> map = directoryContentsComputer(); | 446 Map<String, FileContentsComputer> map = directoryContentsComputer(); |
| 405 map.forEach((String file, FileContentsComputer fileContentsComputer) { | 447 map.forEach((String file, FileContentsComputer fileContentsComputer) { |
| 406 File outputFile = new File(joinAll(posix.split(outputDirPath + file))); | 448 File outputFile = new File(joinAll(posix.split(outputDirPath + file))); |
| 407 outputFile.writeAsStringSync(fileContentsComputer()); | 449 outputFile.writeAsStringSync(fileContentsComputer()); |
| 408 }); | 450 }); |
| 409 } | 451 } |
| 410 } | 452 } |
| OLD | NEW |