| 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'; |
| 11 | 11 |
| 12 import 'package:html5lib/dom.dart' as dom; | 12 import 'package:html5lib/dom.dart' as dom; |
| 13 import 'package:path/path.dart'; | 13 import 'package:path/path.dart'; |
| 14 | 14 |
| 15 import 'text_formatter.dart'; | 15 import 'text_formatter.dart'; |
| 16 import 'html_tools.dart'; | 16 import 'html_tools.dart'; |
| 17 | 17 |
| 18 /** | 18 /** |
| 19 * Join the given strings using camelCase. If [capitalize] is true, the first | 19 * Join the given strings using camelCase. If [doCapitalize] is true, the first |
| 20 * part will be capitalized as well. | 20 * part will be capitalized as well. |
| 21 */ | 21 */ |
| 22 String camelJoin(List<String> parts, {bool capitalize: false}) { | 22 String camelJoin(List<String> parts, {bool doCapitalize: false}) { |
| 23 List<String> upcasedParts = <String>[]; | 23 List<String> upcasedParts = <String>[]; |
| 24 for (int i = 0; i < parts.length; i++) { | 24 for (int i = 0; i < parts.length; i++) { |
| 25 if (i == 0 && !capitalize) { | 25 if (i == 0 && !doCapitalize) { |
| 26 upcasedParts.add(parts[i]); | 26 upcasedParts.add(parts[i]); |
| 27 } else { | 27 } else { |
| 28 upcasedParts.add(parts[i][0].toUpperCase() + parts[i].substring(1)); | 28 upcasedParts.add(capitalize(parts[i])); |
| 29 } | 29 } |
| 30 } | 30 } |
| 31 return upcasedParts.join(); | 31 return upcasedParts.join(); |
| 32 } | 32 } |
| 33 | 33 |
| 34 /** |
| 35 * Capitalize and return the passed String. |
| 36 */ |
| 37 String capitalize(String string) { |
| 38 return string[0].toUpperCase() + string.substring(1); |
| 39 } |
| 40 |
| 34 final RegExp trailingWhitespaceRegExp = new RegExp(r' +$', multiLine: true); | 41 final RegExp trailingWhitespaceRegExp = new RegExp(r' +$', multiLine: true); |
| 35 | 42 |
| 36 /** | 43 /** |
| 37 * Mixin class for generating code. | 44 * Mixin class for generating code. |
| 38 */ | 45 */ |
| 39 class CodeGenerator { | 46 class CodeGenerator { |
| 40 _CodeGeneratorState _state; | 47 _CodeGeneratorState _state; |
| 41 | 48 |
| 42 /** | 49 /** |
| 43 * Execute [callback], collecting any code that is output using [write] | 50 * Execute [callback], collecting any code that is output using [write] |
| (...skipping 25 matching lines...) Expand all Loading... |
| 69 } | 76 } |
| 70 | 77 |
| 71 /** | 78 /** |
| 72 * Execute [callback], indenting any code it outputs by two spaces. | 79 * Execute [callback], indenting any code it outputs by two spaces. |
| 73 */ | 80 */ |
| 74 void indent(void callback()) => indentSpecial(' ', ' ', callback); | 81 void indent(void callback()) => indentSpecial(' ', ' ', callback); |
| 75 | 82 |
| 76 /** | 83 /** |
| 77 * Execute [callback], using [additionalIndent] to indent any code it outputs. | 84 * Execute [callback], using [additionalIndent] to indent any code it outputs. |
| 78 */ | 85 */ |
| 79 void indentBy(String additionalIndent, void callback()) => indentSpecial( | 86 void indentBy(String additionalIndent, void callback()) => indentSpecial(addit
ionalIndent, additionalIndent, callback); |
| 80 additionalIndent, additionalIndent, callback); | |
| 81 | 87 |
| 82 /** | 88 /** |
| 83 * Execute [callback], using [additionalIndent] to indent any code it outputs. | 89 * Execute [callback], using [additionalIndent] to indent any code it outputs. |
| 84 * The first line of output is indented by [firstAdditionalIndent] instead of | 90 * The first line of output is indented by [firstAdditionalIndent] instead of |
| 85 * [additionalIndent]. | 91 * [additionalIndent]. |
| 86 */ | 92 */ |
| 87 void indentSpecial(String firstAdditionalIndent, String additionalIndent, void | 93 void indentSpecial(String firstAdditionalIndent, String additionalIndent, void
callback()) { |
| 88 callback()) { | |
| 89 String oldNextIndent = _state.nextIndent; | 94 String oldNextIndent = _state.nextIndent; |
| 90 String oldIndent = _state.indent; | 95 String oldIndent = _state.indent; |
| 91 try { | 96 try { |
| 92 _state.nextIndent += firstAdditionalIndent; | 97 _state.nextIndent += firstAdditionalIndent; |
| 93 _state.indent += additionalIndent; | 98 _state.indent += additionalIndent; |
| 94 callback(); | 99 callback(); |
| 95 } finally { | 100 } finally { |
| 96 _state.nextIndent = oldNextIndent; | 101 _state.nextIndent = oldNextIndent; |
| 97 _state.indent = oldIndent; | 102 _state.indent = oldIndent; |
| 98 } | 103 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 112 void docComment(List<dom.Node> docs, {int width: 79, bool javadocStyle: false}
) { | 117 void docComment(List<dom.Node> docs, {int width: 79, bool javadocStyle: false}
) { |
| 113 writeln('/**'); | 118 writeln('/**'); |
| 114 indentBy(' * ', () { | 119 indentBy(' * ', () { |
| 115 write(nodesToText(docs, width - _state.indent.length, javadocStyle)); | 120 write(nodesToText(docs, width - _state.indent.length, javadocStyle)); |
| 116 }); | 121 }); |
| 117 writeln(' */'); | 122 writeln(' */'); |
| 118 } | 123 } |
| 119 | 124 |
| 120 void outputHeader({bool javaStyle: false}) { | 125 void outputHeader({bool javaStyle: false}) { |
| 121 String header; | 126 String header; |
| 122 if(javaStyle) { | 127 if (javaStyle) { |
| 123 header = ''' | 128 header = ''' |
| 124 /* | 129 /* |
| 125 * Copyright (c) 2014, the Dart project authors. | 130 * Copyright (c) 2014, the Dart project authors. |
| 126 * | 131 * |
| 127 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not u
se this file except | 132 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not u
se this file except |
| 128 * in compliance with the License. You may obtain a copy of the License at | 133 * in compliance with the License. You may obtain a copy of the License at |
| 129 * | 134 * |
| 130 * http://www.eclipse.org/legal/epl-v10.html | 135 * http://www.eclipse.org/legal/epl-v10.html |
| 131 * | 136 * |
| 132 * Unless required by applicable law or agreed to in writing, software distribut
ed under the License | 137 * Unless required by applicable law or agreed to in writing, software distribut
ed under the License |
| 133 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY K
IND, either express | 138 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY K
IND, either express |
| 134 * or implied. See the License for the specific language governing permissions a
nd limitations under | 139 * or implied. See the License for the specific language governing permissions a
nd limitations under |
| 135 * the License. | 140 * the License. |
| 136 * | 141 * |
| 137 * This file has been automatically generated. Please do not edit it manually. | 142 * This file has been automatically generated. Please do not edit it manually. |
| 138 * To regenerate the file, use the script "pkg/analysis_server/spec/generate_fil
es". | 143 * To regenerate the file, use the script "pkg/analysis_server/spec/generate_fil
es". |
| 139 */'''; | 144 */'''; |
| 140 } | 145 } else { |
| 141 else { | |
| 142 header = ''' | 146 header = ''' |
| 143 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 147 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 144 // for details. All rights reserved. Use of this source code is governed by a | 148 // for details. All rights reserved. Use of this source code is governed by a |
| 145 // BSD-style license that can be found in the LICENSE file. | 149 // BSD-style license that can be found in the LICENSE file. |
| 146 // | 150 // |
| 147 // This file has been automatically generated. Please do not edit it manually. | 151 // This file has been automatically generated. Please do not edit it manually. |
| 148 // To regenerate the file, use the script | 152 // To regenerate the file, use the script |
| 149 // "pkg/analysis_server/spec/generate_files". | 153 // "pkg/analysis_server/spec/generate_files". |
| 150 '''; | 154 '''; |
| 151 } | 155 } |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 290 } else { | 294 } else { |
| 291 buffer.add(new dom.Text(lines.join('\n$indent'))); | 295 buffer.add(new dom.Text(lines.join('\n$indent'))); |
| 292 indentNeeded = false; | 296 indentNeeded = false; |
| 293 } | 297 } |
| 294 } | 298 } |
| 295 } | 299 } |
| 296 | 300 |
| 297 /** | 301 /** |
| 298 * Type of functions used to compute the contents of generated files. | 302 * Type of functions used to compute the contents of generated files. |
| 299 */ | 303 */ |
| 300 typedef String ContentsComputer(); | 304 typedef String FileContentsComputer(); |
| 305 |
| 306 typedef Map<String, FileContentsComputer> DirectoryContentsComputer(); |
| 307 |
| 308 abstract class GeneratedContent { |
| 309 bool check(); |
| 310 void generate(); |
| 311 } |
| 301 | 312 |
| 302 /** | 313 /** |
| 303 * Class representing a single output file (either generated code or generated | 314 * Class representing a single output file (either generated code or generated |
| 304 * HTML). | 315 * HTML). |
| 305 */ | 316 */ |
| 306 class GeneratedFile { | 317 class GeneratedFile extends GeneratedContent { |
| 307 /** | 318 /** |
| 308 * The output file to which generated output should be written, relative to | 319 * The output file to which generated output should be written, relative to |
| 309 * the "tool/spec" directory. This filename uses the posix path separator | 320 * the "tool/spec" directory. This filename uses the posix path separator |
| 310 * ('/') regardless of the OS. | 321 * ('/') regardless of the OS. |
| 311 */ | 322 */ |
| 312 final String outputPath; | 323 final String outputPath; |
| 313 | 324 |
| 314 /** | 325 /** |
| 315 * Callback function which computes the file. | 326 * Callback function which computes the file. |
| 316 */ | 327 */ |
| 317 final ContentsComputer computeContents; | 328 final FileContentsComputer computeContents; |
| 318 | 329 |
| 319 GeneratedFile(this.outputPath, this.computeContents); | 330 GeneratedFile(this.outputPath, this.computeContents); |
| 320 | 331 |
| 321 /** | 332 /** |
| 322 * Get a File object representing the output file. | 333 * Get a File object representing the output file. |
| 323 */ | 334 */ |
| 324 File get outputFile => new File(joinAll(posix.split(outputPath))); | 335 File get outputFile => new File(joinAll(posix.split(outputPath))); |
| 325 | 336 |
| 326 /** | 337 /** |
| 327 * Check whether the file has the correct contents, and return true if it | 338 * Check whether the file has the correct contents, and return true if it |
| 328 * does. | 339 * does. |
| 329 */ | 340 */ |
| 341 @override |
| 330 bool check() { | 342 bool check() { |
| 331 String expectedContents = computeContents(); | 343 String expectedContents = computeContents(); |
| 332 try { | 344 try { |
| 333 return expectedContents == outputFile.readAsStringSync(); | 345 return expectedContents == outputFile.readAsStringSync(); |
| 334 } catch(e) { | 346 } catch (e) { |
| 335 // There was a problem reading the file (most likely because it didn't | 347 // There was a problem reading the file (most likely because it didn't |
| 336 // exist). Treat that the same as if the file doesn't have the expected | 348 // exist). Treat that the same as if the file doesn't have the expected |
| 337 // contents. | 349 // contents. |
| 338 return false; | 350 return false; |
| 339 } | 351 } |
| 340 } | 352 } |
| 341 | 353 |
| 342 /** | 354 /** |
| 343 * Replace the file with the correct contents. [spec] is the "tool/spec" | 355 * Replace the file with the correct contents. [spec] is the "tool/spec" |
| 344 * directory. If [spec] is unspecified, it is assumed to be the directory | 356 * directory. If [spec] is unspecified, it is assumed to be the directory |
| 345 * containing Platform.executable. | 357 * containing Platform.executable. |
| 346 */ | 358 */ |
| 347 void generate() { | 359 void generate() { |
| 348 outputFile.writeAsStringSync(computeContents()); | 360 outputFile.writeAsStringSync(computeContents()); |
| 349 } | 361 } |
| 350 } | 362 } |
| 363 |
| 364 class GeneratedDirectory extends GeneratedContent { |
| 365 |
| 366 final String outputDirPath; |
| 367 final DirectoryContentsComputer directoryContentsComputer; |
| 368 GeneratedDirectory(this.outputDirPath, this.directoryContentsComputer); |
| 369 |
| 370 /** |
| 371 * Get a Directory object representing the output directory. |
| 372 */ |
| 373 Directory get outputDir => new Directory(joinAll(posix.split(outputDirPath))); |
| 374 |
| 375 @override |
| 376 bool check() { |
| 377 // TODO (jwren) the lists of files in the directories need to be compared to |
| 378 // ensure no unexpected files have been added |
| 379 Map<String, FileContentsComputer> map = directoryContentsComputer(); |
| 380 map.forEach((String file, FileContentsComputer fileContentsComputer) { |
| 381 String expectedContents = fileContentsComputer(); |
| 382 File outputFile = new File(joinAll(posix.split(outputDirPath + file))); |
| 383 try { |
| 384 if (expectedContents != outputFile.readAsStringSync()) { |
| 385 return false; |
| 386 } |
| 387 } catch (e) { |
| 388 // There was a problem reading the file (most likely because it didn't |
| 389 // exist). Treat that the same as if the file doesn't have the expected |
| 390 // contents. |
| 391 return false; |
| 392 } |
| 393 }); |
| 394 return true; |
| 395 } |
| 396 |
| 397 @override |
| 398 void generate() { |
| 399 // TODO (jwren) Delete contents in the directory first. |
| 400 Map<String, FileContentsComputer> map = directoryContentsComputer(); |
| 401 map.forEach((String file, FileContentsComputer fileContentsComputer) { |
| 402 File outputFile = new File(joinAll(posix.split(outputDirPath + file))); |
| 403 outputFile.writeAsStringSync(fileContentsComputer()); |
| 404 }); |
| 405 } |
| 406 } |
| OLD | NEW |