001 /*
002 * Copyright (C) 2006 Google Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the
010 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
011 * express or implied. See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014
015 package com.google.common.base;
016
017 import com.google.common.annotations.GwtCompatible;
018
019 /**
020 * Utility class for converting between various ASCII case formats.
021 *
022 * @author Mike Bostock
023 * @since 1
024 */
025 @GwtCompatible
026 public enum CaseFormat {
027 /**
028 * Hyphenated variable naming convention, e.g., "lower-hyphen".
029 */
030 LOWER_HYPHEN(CharMatcher.is('-'), "-"),
031
032 /**
033 * C++ variable naming convention, e.g., "lower_underscore".
034 */
035 LOWER_UNDERSCORE(CharMatcher.is('_'), "_"),
036
037 /**
038 * Java variable naming convention, e.g., "lowerCamel".
039 */
040 LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), ""),
041
042 /**
043 * Java and C++ class naming convention, e.g., "UpperCamel".
044 */
045 UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), ""),
046
047 /**
048 * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE".
049 */
050 UPPER_UNDERSCORE(CharMatcher.is('_'), "_");
051
052 private final CharMatcher wordBoundary;
053 private final String wordSeparator;
054
055 CaseFormat(CharMatcher wordBoundary, String wordSeparator) {
056 this.wordBoundary = wordBoundary;
057 this.wordSeparator = wordSeparator;
058 }
059
060 /**
061 * Converts the specified {@code String s} from this format to the specified {@code format}. A
062 * "best effort" approach is taken; if {@code s} does not conform to the assumed format, then the
063 * behavior of this method is undefined but we make a reasonable effort at converting anyway.
064 */
065 public String to(CaseFormat format, String s) {
066 if (format == null) {
067 throw new NullPointerException();
068 }
069 if (s == null) {
070 throw new NullPointerException();
071 }
072
073 if (format == this) {
074 return s;
075 }
076
077 /* optimize cases where no camel conversion is required */
078 switch (this) {
079 case LOWER_HYPHEN:
080 switch (format) {
081 case LOWER_UNDERSCORE:
082 return s.replace('-', '_');
083 case UPPER_UNDERSCORE:
084 return Ascii.toUpperCase(s.replace('-', '_'));
085 }
086 break;
087 case LOWER_UNDERSCORE:
088 switch (format) {
089 case LOWER_HYPHEN:
090 return s.replace('_', '-');
091 case UPPER_UNDERSCORE:
092 return Ascii.toUpperCase(s);
093 }
094 break;
095 case UPPER_UNDERSCORE:
096 switch (format) {
097 case LOWER_HYPHEN:
098 return Ascii.toLowerCase(s.replace('_', '-'));
099 case LOWER_UNDERSCORE:
100 return Ascii.toLowerCase(s);
101 }
102 break;
103 }
104
105 // otherwise, deal with camel conversion
106 StringBuilder out = null;
107 int i = 0;
108 int j = -1;
109 while ((j = wordBoundary.indexIn(s, ++j)) != -1) {
110 if (i == 0) {
111 // include some extra space for separators
112 out = new StringBuilder(s.length() + 4 * wordSeparator.length());
113 out.append(format.normalizeFirstWord(s.substring(i, j)));
114 } else {
115 out.append(format.normalizeWord(s.substring(i, j)));
116 }
117 out.append(format.wordSeparator);
118 i = j + wordSeparator.length();
119 }
120 if (i == 0) {
121 return format.normalizeFirstWord(s);
122 }
123 out.append(format.normalizeWord(s.substring(i)));
124 return out.toString();
125 }
126
127 private String normalizeFirstWord(String word) {
128 switch (this) {
129 case LOWER_CAMEL:
130 return Ascii.toLowerCase(word);
131 default:
132 return normalizeWord(word);
133 }
134 }
135
136 private String normalizeWord(String word) {
137 switch (this) {
138 case LOWER_HYPHEN:
139 return Ascii.toLowerCase(word);
140 case LOWER_UNDERSCORE:
141 return Ascii.toLowerCase(word);
142 case LOWER_CAMEL:
143 return firstCharOnlyToUpper(word);
144 case UPPER_CAMEL:
145 return firstCharOnlyToUpper(word);
146 case UPPER_UNDERSCORE:
147 return Ascii.toUpperCase(word);
148 }
149 throw new RuntimeException("unknown case: " + this);
150 }
151
152 private static String firstCharOnlyToUpper(String word) {
153 int length = word.length();
154 if (length == 0) {
155 return word;
156 }
157 return new StringBuilder(length)
158 .append(Ascii.toUpperCase(word.charAt(0)))
159 .append(Ascii.toLowerCase(word.substring(1)))
160 .toString();
161 }
162 }