001 /*
002 * Copyright 2008-2013 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package griffon.util;
017
018 import java.util.ArrayList;
019 import java.util.Iterator;
020 import java.util.List;
021 import java.util.Locale;
022
023 /**
024 * Contains utility methods for converting between different name types,
025 * for example from class names -> property names and vice-versa. The
026 * key aspect of this class is that it has no dependencies outside the
027 * JDK!
028 */
029 public class GriffonNameUtils {
030 private static final String PROPERTY_SET_PREFIX = "set";
031
032 /**
033 * Capitalizes a String (makes the first char uppercase) taking care
034 * of blank strings and single character strings.
035 *
036 * @param str The String to be capitalized
037 * @return Capitalized version of the target string if it is not blank
038 */
039 public static String capitalize(String str) {
040 if (isBlank(str)) return str;
041 if (str.length() == 1) return str.toUpperCase();
042 return str.substring(0, 1).toUpperCase(Locale.ENGLISH) + str.substring(1);
043 }
044
045 /**
046 * Uncapitalizes a String (makes the first char lowercase) taking care
047 * of blank strings and single character strings.
048 *
049 * @param str The String to be uncapitalized
050 * @return Uncapitalized version of the target string if it is not blank
051 */
052 public static String uncapitalize(String str) {
053 if (isBlank(str)) return str;
054 if (str.length() == 1) return String.valueOf(Character.toLowerCase(str.charAt(0)));
055 return Character.toLowerCase(str.charAt(0)) + str.substring(1);
056 }
057
058 /**
059 * Retrieves the name of a setter for the specified property name
060 *
061 * @param propertyName The property name
062 * @return The setter equivalent
063 */
064 public static String getSetterName(String propertyName) {
065 return PROPERTY_SET_PREFIX + capitalize(propertyName);
066 }
067
068 /**
069 * Calculate the name for a getter method to retrieve the specified property
070 *
071 * @param propertyName
072 * @return The name for the getter method for this property, if it were to exist, i.e. getConstraints
073 */
074 public static String getGetterName(String propertyName) {
075 return "get" + capitalize(propertyName);
076 }
077
078 /**
079 * Returns the class name for the given logical name and trailing name. For example "person" and "Controller" would evaluate to "PersonController"
080 *
081 * @param logicalName The logical name
082 * @param trailingName The trailing name
083 * @return The class name
084 */
085 public static String getClassName(String logicalName, String trailingName) {
086 if (isBlank(logicalName)) {
087 throw new IllegalArgumentException("Argument [logicalName] cannot be null or blank");
088 }
089
090 String className = capitalize(logicalName);
091 if (trailingName != null) {
092 className = className + trailingName;
093 }
094 return className;
095 }
096
097 /**
098 * Returns the class name representation of the given name
099 *
100 * @param name The name to convert
101 * @return The property name representation
102 */
103 public static String getClassNameRepresentation(String name) {
104 StringBuilder buf = new StringBuilder();
105 if (name != null && name.length() > 0) {
106 String[] tokens = name.split("[^\\w\\d]");
107 for (String token1 : tokens) {
108 String token = token1.trim();
109 buf.append(capitalize(token));
110 }
111 }
112
113 return buf.toString();
114 }
115
116 /**
117 * Converts foo-bar into FooBar. Empty and null strings are returned
118 * as-is.
119 *
120 * @param name The lower case hyphen separated name
121 * @return The class name equivalent.
122 */
123 public static String getClassNameForLowerCaseHyphenSeparatedName(String name) {
124 // Handle null and empty strings.
125 if (isBlank(name)) return name;
126
127 if (name.indexOf('-') > -1) {
128 StringBuilder buf = new StringBuilder();
129 String[] tokens = name.split("-");
130 for (String token : tokens) {
131 if (token == null || token.length() == 0) continue;
132 buf.append(capitalize(token));
133 }
134 return buf.toString();
135 }
136
137 return capitalize(name);
138 }
139
140 /**
141 * Retrieves the logical class name of a Griffon artifact given the Griffon class
142 * and a specified trailing name
143 *
144 * @param clazz The class
145 * @param trailingName The trailing name such as "Controller" or "TagLib"
146 * @return The logical class name
147 */
148 public static String getLogicalName(Class<?> clazz, String trailingName) {
149 return getLogicalName(clazz.getName(), trailingName);
150 }
151
152 /**
153 * Retrieves the logical name of the class without the trailing name
154 *
155 * @param name The name of the class
156 * @param trailingName The trailing name
157 * @return The logical name
158 */
159 public static String getLogicalName(String name, String trailingName) {
160 if (!isBlank(trailingName)) {
161 String shortName = getShortName(name);
162 if (shortName.indexOf(trailingName) > -1) {
163 return shortName.substring(0, shortName.length() - trailingName.length());
164 }
165 }
166 return name;
167 }
168
169 public static String getLogicalPropertyName(String className, String trailingName) {
170 if (!isBlank(className) && !isBlank(trailingName)) {
171 if (className.length() == trailingName.length() + 1 && className.endsWith(trailingName)) {
172 return className.substring(0, 1).toLowerCase();
173 }
174 }
175 return getLogicalName(getPropertyName(className), trailingName);
176 }
177
178 /**
179 * Shorter version of getPropertyNameRepresentation
180 *
181 * @param name The name to convert
182 * @return The property name version
183 */
184 public static String getPropertyName(String name) {
185 return getPropertyNameRepresentation(name);
186 }
187
188 /**
189 * Shorter version of getPropertyNameRepresentation
190 *
191 * @param clazz The clazz to convert
192 * @return The property name version
193 */
194 public static String getPropertyName(Class<?> clazz) {
195 return getPropertyNameRepresentation(clazz);
196 }
197
198 /**
199 * Returns the property name equivalent for the specified class
200 *
201 * @param targetClass The class to get the property name for
202 * @return A property name reperesentation of the class name (eg. MyClass becomes myClass)
203 */
204 public static String getPropertyNameRepresentation(Class<?> targetClass) {
205 String shortName = getShortName(targetClass);
206 return getPropertyNameRepresentation(shortName);
207 }
208
209 /**
210 * Returns the property name representation of the given name
211 *
212 * @param name The name to convert
213 * @return The property name representation
214 */
215 public static String getPropertyNameRepresentation(String name) {
216 if (isBlank(name)) return name;
217 // Strip any package from the name.
218 int pos = name.lastIndexOf('.');
219 if (pos != -1) {
220 name = name.substring(pos + 1);
221 }
222
223 // Check whether the name begins with two upper case letters.
224 if (name.length() > 1 && Character.isUpperCase(name.charAt(0)) && Character.isUpperCase(name.charAt(1))) {
225 return name;
226 }
227
228 String propertyName = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
229 if (propertyName.indexOf(' ') > -1) {
230 propertyName = propertyName.replaceAll("\\s", "");
231 }
232 return propertyName;
233 }
234
235 /**
236 * Converts foo-bar into fooBar
237 *
238 * @param name The lower case hyphen separated name
239 * @return The property name equivalent
240 */
241 public static String getPropertyNameForLowerCaseHyphenSeparatedName(String name) {
242 return getPropertyName(getClassNameForLowerCaseHyphenSeparatedName(name));
243 }
244
245 /**
246 * Returns the class name without the package prefix
247 *
248 * @param targetClass The class to get a short name for
249 * @return The short name of the class
250 */
251 public static String getShortName(Class<?> targetClass) {
252 String className = targetClass.getName();
253 return getShortName(className);
254 }
255
256 /**
257 * Returns the class name without the package prefix
258 *
259 * @param className The class name to get a short name for
260 * @return The short name of the class
261 */
262 public static String getShortName(String className) {
263 if (isBlank(className)) return className;
264 int i = className.lastIndexOf(".");
265 if (i > -1) {
266 className = className.substring(i + 1, className.length());
267 }
268 return className;
269 }
270
271 /**
272 * Converts a property name into its natural language equivalent eg ('firstName' becomes 'First Name')
273 *
274 * @param name The property name to convert
275 * @return The converted property name
276 */
277 public static String getNaturalName(String name) {
278 name = getShortName(name);
279 if (isBlank(name)) return name;
280 List<String> words = new ArrayList<String>();
281 int i = 0;
282 char[] chars = name.toCharArray();
283 for (int j = 0; j < chars.length; j++) {
284 char c = chars[j];
285 String w;
286 if (i >= words.size()) {
287 w = "";
288 words.add(i, w);
289 } else {
290 w = words.get(i);
291 }
292
293 if (Character.isLowerCase(c) || Character.isDigit(c)) {
294 if (Character.isLowerCase(c) && w.length() == 0) {
295 c = Character.toUpperCase(c);
296 } else if (w.length() > 1 && Character.isUpperCase(w.charAt(w.length() - 1))) {
297 w = "";
298 words.add(++i, w);
299 }
300
301 words.set(i, w + c);
302 } else if (Character.isUpperCase(c)) {
303 if ((i == 0 && w.length() == 0) || Character.isUpperCase(w.charAt(w.length() - 1))) {
304 words.set(i, w + c);
305 } else {
306 words.add(++i, String.valueOf(c));
307 }
308 }
309 }
310
311 StringBuilder buf = new StringBuilder();
312 for (Iterator<String> j = words.iterator(); j.hasNext(); ) {
313 String word = j.next();
314 buf.append(word);
315 if (j.hasNext()) {
316 buf.append(' ');
317 }
318 }
319 return buf.toString();
320 }
321
322 /**
323 * <p>Determines whether a given string is <code>null</code>, empty,
324 * or only contains whitespace. If it contains anything other than
325 * whitespace then the string is not considered to be blank and the
326 * method returns <code>false</code>.</p>
327 * <p>We could use Commons Lang for this, but we don't want GriffonNameUtils
328 * to have a dependency on any external library to minimise the number of
329 * dependencies required to bootstrap Griffon.</p>
330 *
331 * @param str The string to test.
332 * @return <code>true</code> if the string is <code>null</code>, or
333 * blank.
334 */
335 public static boolean isBlank(String str) {
336 return str == null || str.trim().length() == 0;
337 }
338
339 /**
340 * Retrieves the hyphenated name representation of the supplied class. For example
341 * MyFunkyGriffonThingy would be my-funky-griffon-thingy.
342 *
343 * @param clazz The class to convert
344 * @return The hyphenated name representation
345 */
346 public static String getHyphenatedName(Class clazz) {
347 if (clazz == null) {
348 return null;
349 }
350 return getHyphenatedName(clazz.getName());
351 }
352
353 /**
354 * Retrieves the hyphenated name representation of the given class name.
355 * For example MyFunkyGriffonThingy would be my-funky-griffon-thingy.
356 *
357 * @param name The class name to convert.
358 * @return The hyphenated name representation.
359 */
360 public static String getHyphenatedName(String name) {
361 if (isBlank(name)) return name;
362 if (name.endsWith(".groovy")) {
363 name = name.substring(0, name.length() - 7);
364 }
365 String naturalName = getNaturalName(getShortName(name));
366 return naturalName.replaceAll("\\s", "-").toLowerCase();
367 }
368
369 /**
370 * Applies single or double quotes to a string if it contains whitespace characters
371 *
372 * @param str the String to be surrounded by quotes
373 * @return a copy of the original String, surrounded by quotes
374 */
375 public static String quote(String str) {
376 if (isBlank(str)) return str;
377 for (int i = 0; i < str.length(); i++) {
378 if (Character.isWhitespace(str.charAt(i))) {
379 str = applyQuotes(str);
380 break;
381 }
382 }
383 return str;
384 }
385
386 /**
387 * Removes single or double quotes from a String
388 *
389 * @param str the String from which quotes will be removed
390 * @return the unquoted String
391 */
392 public static String unquote(String str) {
393 if (isBlank(str)) return str;
394 if ((str.startsWith("'") && str.endsWith("'")) ||
395 (str.startsWith("\"") && str.endsWith("\""))) {
396 return str.substring(1, str.length() - 1);
397 }
398 return str;
399 }
400
401 private static String applyQuotes(String string) {
402 if (string == null || string.length() == 0) {
403 return "\"\"";
404 }
405
406 char b;
407 char c = 0;
408 int i;
409 int len = string.length();
410 StringBuffer sb = new StringBuffer(len * 2);
411 String t;
412 char[] chars = string.toCharArray();
413 char[] buffer = new char[1030];
414 int bufferIndex = 0;
415 sb.append('"');
416 for (i = 0; i < len; i += 1) {
417 if (bufferIndex > 1024) {
418 sb.append(buffer, 0, bufferIndex);
419 bufferIndex = 0;
420 }
421 b = c;
422 c = chars[i];
423 switch (c) {
424 case '\\':
425 case '"':
426 buffer[bufferIndex++] = '\\';
427 buffer[bufferIndex++] = c;
428 break;
429 case '/':
430 if (b == '<') {
431 buffer[bufferIndex++] = '\\';
432 }
433 buffer[bufferIndex++] = c;
434 break;
435 default:
436 if (c < ' ') {
437 switch (c) {
438 case '\b':
439 buffer[bufferIndex++] = '\\';
440 buffer[bufferIndex++] = 'b';
441 break;
442 case '\t':
443 buffer[bufferIndex++] = '\\';
444 buffer[bufferIndex++] = 't';
445 break;
446 case '\n':
447 buffer[bufferIndex++] = '\\';
448 buffer[bufferIndex++] = 'n';
449 break;
450 case '\f':
451 buffer[bufferIndex++] = '\\';
452 buffer[bufferIndex++] = 'f';
453 break;
454 case '\r':
455 buffer[bufferIndex++] = '\\';
456 buffer[bufferIndex++] = 'r';
457 break;
458 default:
459 t = "000" + Integer.toHexString(c);
460 int tLength = t.length();
461 buffer[bufferIndex++] = '\\';
462 buffer[bufferIndex++] = 'u';
463 buffer[bufferIndex++] = t.charAt(tLength - 4);
464 buffer[bufferIndex++] = t.charAt(tLength - 3);
465 buffer[bufferIndex++] = t.charAt(tLength - 2);
466 buffer[bufferIndex++] = t.charAt(tLength - 1);
467 }
468 } else {
469 buffer[bufferIndex++] = c;
470 }
471 }
472 }
473 sb.append(buffer, 0, bufferIndex);
474 sb.append('"');
475 return sb.toString();
476 }
477 }
|