| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dartino 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.md file. | |
| 4 | |
| 5 library servicec.util; | |
| 6 | |
| 7 const String validIdentifier = | |
| 8 "A valid identifier contains only alphanumeric characters and " + | |
| 9 "underscores, and does not start with a digit."; | |
| 10 | |
| 11 /// Takes a [camelize]d string and reverses the camelization: each capital case | |
| 12 /// is considered to be the start of a word; each word is lowered and words are | |
| 13 /// joined together by underscores. Existing underscores are preserved, and if | |
| 14 /// they already separate words, no additional underscores are added (e.g. | |
| 15 /// "HelloWorld" and "Hello_World" both become "hello_world"). | |
| 16 /// | |
| 17 /// Throws an [ArgumentError] if the input is not a valid identifier. | |
| 18 String underscore(String text) { | |
| 19 if (!isValidIdentifier(text)) { | |
| 20 throw new ArgumentError( | |
| 21 "The argument should be a valid identifier. $validIdentifier"); | |
| 22 } | |
| 23 | |
| 24 List<String> chunks = <String>[]; | |
| 25 int chunkStart = 0; | |
| 26 // The result is that chunk[0] is never capitalized, and chunk[i>0] is always | |
| 27 // capitalized. | |
| 28 for (int i = 0; i < text.length; ++i) { | |
| 29 if (isUpper(text.codeUnitAt(i)) || isNumeric(text.codeUnitAt(i))) { | |
| 30 chunks.add(text.substring(chunkStart, i)); | |
| 31 chunkStart = i; | |
| 32 } | |
| 33 } | |
| 34 chunks.add(text.substring(chunkStart)); | |
| 35 | |
| 36 String result = chunks[0]; | |
| 37 for (int i = 1; i < chunks.length; ++i) { | |
| 38 if (result.isNotEmpty && | |
| 39 !result.endsWith('_')) { | |
| 40 result += '_'; | |
| 41 } | |
| 42 result += toLower(chunks[i]); | |
| 43 } | |
| 44 return result; | |
| 45 } | |
| 46 | |
| 47 /// Removes underscores and [capitalize]s the words which they surround. Throws | |
| 48 /// an [ArgumentError] if the input is not a valid identifier or if camelization | |
| 49 /// produces an invalid identifier (e.g. "_1_" would be camelized as "1", which | |
| 50 /// is not a valid identifier). | |
| 51 String camelize(String text) { | |
| 52 if (!isValidIdentifier(text)) { | |
| 53 throw new ArgumentError( | |
| 54 "The argument should be a valid identifier. $validIdentifier"); | |
| 55 } | |
| 56 String result = | |
| 57 text.splitMapJoin('_', onMatch: (_) => '', onNonMatch: capitalize); | |
| 58 if (!isValidIdentifier(result)) { | |
| 59 throw new ArgumentError( | |
| 60 "The argument should be such that the output of camelize is a valid " + | |
| 61 "identifier. $validIdentifier"); | |
| 62 } | |
| 63 return result; | |
| 64 } | |
| 65 | |
| 66 /// Checks that [text] contains only alphanumeric (a-z, A-Z, 0-9) characters and | |
| 67 /// underscores, and that it doesn't start with a digit. | |
| 68 bool isValidIdentifier(String text) { | |
| 69 if (text.isEmpty) return false; | |
| 70 if (!isAlphabetical(text.codeUnitAt(0)) && | |
| 71 !isUnderscore(text.codeUnitAt(0))) return false; | |
| 72 for (int i = 1; i < text.length; ++i) { | |
| 73 if (!isAlphanumericOrUnderscore(text.codeUnitAt(i))) return false; | |
| 74 } | |
| 75 return true; | |
| 76 } | |
| 77 | |
| 78 /// Uppers the first letter of the word and lowers the rest. If the word is | |
| 79 /// empty just returns it. | |
| 80 String capitalize(String word) { | |
| 81 if (word.isEmpty) return word; | |
| 82 return charToUpper(word[0]) + toLower(word.substring(1)); | |
| 83 } | |
| 84 | |
| 85 /// Makes a string containing a single latin letter uppercase. Assumes that the | |
| 86 /// size of [string] is 1. | |
| 87 String charToUpper(String string) { | |
| 88 int codeUnit = string.codeUnitAt(0); | |
| 89 if (isLower(codeUnit)) { | |
| 90 codeUnit += 'A'.codeUnitAt(0) - 'a'.codeUnitAt(0); | |
| 91 } | |
| 92 return new String.fromCharCode(codeUnit); | |
| 93 } | |
| 94 | |
| 95 /// Makes all latin letters in [string] lowercase. | |
| 96 String toLower(String string) { | |
| 97 List<int> codeUnits = new List<int>(string.length); | |
| 98 for (int i = 0; i < string.length; ++i) { | |
| 99 codeUnits[i] = string.codeUnitAt(i); | |
| 100 if (isUpper(codeUnits[i])) { | |
| 101 codeUnits[i] += 'a'.codeUnitAt(0) - 'A'.codeUnitAt(0); | |
| 102 } | |
| 103 } | |
| 104 return new String.fromCharCodes(codeUnits); | |
| 105 } | |
| 106 | |
| 107 /// Checks if [charCode] corresponds to a latin letter, a digit, or the | |
| 108 /// underscore character. | |
| 109 bool isAlphanumericOrUnderscore(int charCode) { | |
| 110 return isAlphabetical(charCode) || | |
| 111 isNumeric(charCode) || | |
| 112 isUnderscore(charCode); | |
| 113 } | |
| 114 | |
| 115 /// Checks if [charCode] corresponds to a latin letter. | |
| 116 bool isAlphabetical(int charCode) { | |
| 117 return isLower(charCode) || isUpper(charCode); | |
| 118 } | |
| 119 | |
| 120 /// Checks if [charCode] corresponds to a lowercase latin letter. | |
| 121 bool isLower(int charCode) { | |
| 122 final int a = 'a'.codeUnitAt(0); | |
| 123 final int z = 'z'.codeUnitAt(0); | |
| 124 return a <= charCode && charCode <= z; | |
| 125 } | |
| 126 | |
| 127 /// Checks if [charCode] corresponds to an uppercase latin letter. | |
| 128 bool isUpper(int charCode) { | |
| 129 final int A = 'A'.codeUnitAt(0); | |
| 130 final int Z = 'Z'.codeUnitAt(0); | |
| 131 return A <= charCode && charCode <= Z; | |
| 132 } | |
| 133 | |
| 134 /// Checks if [charCode] corresponds to a digit. | |
| 135 bool isNumeric(int charCode) { | |
| 136 final int _0 = '0'.codeUnitAt(0); | |
| 137 final int _9 = '9'.codeUnitAt(0); | |
| 138 return _0 <= charCode && charCode <= _9; | |
| 139 } | |
| 140 | |
| 141 /// Checks if [charCode] corresponds the underscore character. | |
| 142 bool isUnderscore(int charCode) { | |
| 143 final int _ = '_'.codeUnitAt(0); | |
| 144 return _ == charCode; | |
| 145 } | |
| OLD | NEW |