GriffonNameUtils.java
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() == 1return str.toUpperCase();
042         return str.substring(01).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() == 1return 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() == 0continue;
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() && className.endsWith(trailingName)) {
172                 return className.substring(01).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() && Character.isUpperCase(name.charAt(0)) && Character.isUpperCase(name.charAt(1))) {
225             return name;
226         }
227 
228         String propertyName = name.substring(01).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() && 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 == && 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 }