001 /*
002 * Copyright (C) 2006 The Guava 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
017 package com.google.common.base;
018
019 import com.google.common.annotations.GwtCompatible;
020
021 /**
022 * Utility class for converting between various ASCII case formats.
023 *
024 * @author Mike Bostock
025 * @since 1.0
026 */
027 @GwtCompatible
028 public enum CaseFormat {
029 /**
030 * Hyphenated variable naming convention, e.g., "lower-hyphen".
031 */
032 LOWER_HYPHEN(CharMatcher.is('-'), "-"),
033
034 /**
035 * C++ variable naming convention, e.g., "lower_underscore".
036 */
037 LOWER_UNDERSCORE(CharMatcher.is('_'), "_"),
038
039 /**
040 * Java variable naming convention, e.g., "lowerCamel".
041 */
042 LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), ""),
043
044 /**
045 * Java and C++ class naming convention, e.g., "UpperCamel".
046 */
047 UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), ""),
048
049 /**
050 * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE".
051 */
052 UPPER_UNDERSCORE(CharMatcher.is('_'), "_");
053
054 private final CharMatcher wordBoundary;
055 private final String wordSeparator;
056
057 CaseFormat(CharMatcher wordBoundary, String wordSeparator) {
058 this.wordBoundary = wordBoundary;
059 this.wordSeparator = wordSeparator;
060 }
061
062 /**
063 * Converts the specified {@code String s} from this format to the specified {@code format}. A
064 * "best effort" approach is taken; if {@code s} does not conform to the assumed format, then the
065 * behavior of this method is undefined but we make a reasonable effort at converting anyway.
066 */
067 public String to(CaseFormat format, String s) {
068 if (format == null) {
069 throw new NullPointerException();
070 }
071 if (s == null) {
072 throw new NullPointerException();
073 }
074
075 if (format == this) {
076 return s;
077 }
078
079 /* optimize cases where no camel conversion is required */
080 switch (this) {
081 case LOWER_HYPHEN:
082 switch (format) {
083 case LOWER_UNDERSCORE:
084 return s.replace('-', '_');
085 case UPPER_UNDERSCORE:
086 return Ascii.toUpperCase(s.replace('-', '_'));
087 }
088 break;
089 case LOWER_UNDERSCORE:
090 switch (format) {
091 case LOWER_HYPHEN:
092 return s.replace('_', '-');
093 case UPPER_UNDERSCORE:
094 return Ascii.toUpperCase(s);
095 }
096 break;
097 case UPPER_UNDERSCORE:
098 switch (format) {
099 case LOWER_HYPHEN:
100 return Ascii.toLowerCase(s.replace('_', '-'));
101 case LOWER_UNDERSCORE:
102 return Ascii.toLowerCase(s);
103 }
104 break;
105 }
106
107 // otherwise, deal with camel conversion
108 StringBuilder out = null;
109 int i = 0;
110 int j = -1;
111 while ((j = wordBoundary.indexIn(s, ++j)) != -1) {
112 if (i == 0) {
113 // include some extra space for separators
114 out = new StringBuilder(s.length() + 4 * wordSeparator.length());
115 out.append(format.normalizeFirstWord(s.substring(i, j)));
116 } else {
117 out.append(format.normalizeWord(s.substring(i, j)));
118 }
119 out.append(format.wordSeparator);
120 i = j + wordSeparator.length();
121 }
122 if (i == 0) {
123 return format.normalizeFirstWord(s);
124 }
125 out.append(format.normalizeWord(s.substring(i)));
126 return out.toString();
127 }
128
129 private String normalizeFirstWord(String word) {
130 switch (this) {
131 case LOWER_CAMEL:
132 return Ascii.toLowerCase(word);
133 default:
134 return normalizeWord(word);
135 }
136 }
137
138 private String normalizeWord(String word) {
139 switch (this) {
140 case LOWER_HYPHEN:
141 return Ascii.toLowerCase(word);
142 case LOWER_UNDERSCORE:
143 return Ascii.toLowerCase(word);
144 case LOWER_CAMEL:
145 return firstCharOnlyToUpper(word);
146 case UPPER_CAMEL:
147 return firstCharOnlyToUpper(word);
148 case UPPER_UNDERSCORE:
149 return Ascii.toUpperCase(word);
150 }
151 throw new RuntimeException("unknown case: " + this);
152 }
153
154 private static String firstCharOnlyToUpper(String word) {
155 int length = word.length();
156 if (length == 0) {
157 return word;
158 }
159 return new StringBuilder(length)
160 .append(Ascii.toUpperCase(word.charAt(0)))
161 .append(Ascii.toLowerCase(word.substring(1)))
162 .toString();
163 }
164 }