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 }