1/* 2 * Copyright (C) 2006 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.common.base; 18 19import com.google.common.annotations.GwtCompatible; 20 21/** 22 * Utility class for converting between various ASCII case formats. 23 * 24 * @author Mike Bostock 25 * @since 1.0 26 */ 27@GwtCompatible 28public enum CaseFormat { 29 /** 30 * Hyphenated variable naming convention, e.g., "lower-hyphen". 31 */ 32 LOWER_HYPHEN(CharMatcher.is('-'), "-"), 33 34 /** 35 * C++ variable naming convention, e.g., "lower_underscore". 36 */ 37 LOWER_UNDERSCORE(CharMatcher.is('_'), "_"), 38 39 /** 40 * Java variable naming convention, e.g., "lowerCamel". 41 */ 42 LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), ""), 43 44 /** 45 * Java and C++ class naming convention, e.g., "UpperCamel". 46 */ 47 UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), ""), 48 49 /** 50 * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE". 51 */ 52 UPPER_UNDERSCORE(CharMatcher.is('_'), "_"); 53 54 private final CharMatcher wordBoundary; 55 private final String wordSeparator; 56 57 CaseFormat(CharMatcher wordBoundary, String wordSeparator) { 58 this.wordBoundary = wordBoundary; 59 this.wordSeparator = wordSeparator; 60 } 61 62 /** 63 * Converts the specified {@code String s} from this format to the specified {@code format}. A 64 * "best effort" approach is taken; if {@code s} does not conform to the assumed format, then the 65 * behavior of this method is undefined but we make a reasonable effort at converting anyway. 66 */ 67 public String to(CaseFormat format, String s) { 68 if (format == null) { 69 throw new NullPointerException(); 70 } 71 if (s == null) { 72 throw new NullPointerException(); 73 } 74 75 if (format == this) { 76 return s; 77 } 78 79 /* optimize cases where no camel conversion is required */ 80 switch (this) { 81 case LOWER_HYPHEN: 82 switch (format) { 83 case LOWER_UNDERSCORE: 84 return s.replace('-', '_'); 85 case UPPER_UNDERSCORE: 86 return Ascii.toUpperCase(s.replace('-', '_')); 87 } 88 break; 89 case LOWER_UNDERSCORE: 90 switch (format) { 91 case LOWER_HYPHEN: 92 return s.replace('_', '-'); 93 case UPPER_UNDERSCORE: 94 return Ascii.toUpperCase(s); 95 } 96 break; 97 case UPPER_UNDERSCORE: 98 switch (format) { 99 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} 165