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 |