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 017package com.google.common.base; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.annotations.GwtCompatible; 023 024import java.io.Serializable; 025 026import javax.annotation.Nullable; 027 028/** 029 * Utility class for converting between various ASCII case formats. Behavior is undefined for 030 * non-ASCII input. 031 * 032 * @author Mike Bostock 033 * @since 1.0 034 */ 035@GwtCompatible 036public enum CaseFormat { 037 /** 038 * Hyphenated variable naming convention, e.g., "lower-hyphen". 039 */ 040 LOWER_HYPHEN(CharMatcher.is('-'), "-") { 041 @Override String normalizeWord(String word) { 042 return Ascii.toLowerCase(word); 043 } 044 @Override String convert(CaseFormat format, String s) { 045 if (format == LOWER_UNDERSCORE) { 046 return s.replace('-', '_'); 047 } 048 if (format == UPPER_UNDERSCORE) { 049 return Ascii.toUpperCase(s.replace('-', '_')); 050 } 051 return super.convert(format, s); 052 } 053 }, 054 055 /** 056 * C++ variable naming convention, e.g., "lower_underscore". 057 */ 058 LOWER_UNDERSCORE(CharMatcher.is('_'), "_") { 059 @Override String normalizeWord(String word) { 060 return Ascii.toLowerCase(word); 061 } 062 @Override String convert(CaseFormat format, String s) { 063 if (format == LOWER_HYPHEN) { 064 return s.replace('_', '-'); 065 } 066 if (format == UPPER_UNDERSCORE) { 067 return Ascii.toUpperCase(s); 068 } 069 return super.convert(format, s); 070 } 071 }, 072 073 /** 074 * Java variable naming convention, e.g., "lowerCamel". 075 */ 076 LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { 077 @Override String normalizeWord(String word) { 078 return firstCharOnlyToUpper(word); 079 } 080 }, 081 082 /** 083 * Java and C++ class naming convention, e.g., "UpperCamel". 084 */ 085 UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { 086 @Override String normalizeWord(String word) { 087 return firstCharOnlyToUpper(word); 088 } 089 }, 090 091 /** 092 * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE". 093 */ 094 UPPER_UNDERSCORE(CharMatcher.is('_'), "_") { 095 @Override String normalizeWord(String word) { 096 return Ascii.toUpperCase(word); 097 } 098 @Override String convert(CaseFormat format, String s) { 099 if (format == LOWER_HYPHEN) { 100 return Ascii.toLowerCase(s.replace('_', '-')); 101 } 102 if (format == LOWER_UNDERSCORE) { 103 return Ascii.toLowerCase(s); 104 } 105 return super.convert(format, s); 106 } 107 }; 108 109 private final CharMatcher wordBoundary; 110 private final String wordSeparator; 111 112 CaseFormat(CharMatcher wordBoundary, String wordSeparator) { 113 this.wordBoundary = wordBoundary; 114 this.wordSeparator = wordSeparator; 115 } 116 117 /** 118 * Converts the specified {@code String str} from this format to the specified {@code format}. A 119 * "best effort" approach is taken; if {@code str} does not conform to the assumed format, then 120 * the behavior of this method is undefined but we make a reasonable effort at converting anyway. 121 */ 122 public final String to(CaseFormat format, String str) { 123 checkNotNull(format); 124 checkNotNull(str); 125 return (format == this) ? str : convert(format, str); 126 } 127 128 /** 129 * Enum values can override for performance reasons. 130 */ 131 String convert(CaseFormat format, String s) { 132 // deal with camel conversion 133 StringBuilder out = null; 134 int i = 0; 135 int j = -1; 136 while ((j = wordBoundary.indexIn(s, ++j)) != -1) { 137 if (i == 0) { 138 // include some extra space for separators 139 out = new StringBuilder(s.length() + 4 * wordSeparator.length()); 140 out.append(format.normalizeFirstWord(s.substring(i, j))); 141 } else { 142 out.append(format.normalizeWord(s.substring(i, j))); 143 } 144 out.append(format.wordSeparator); 145 i = j + wordSeparator.length(); 146 } 147 return (i == 0) 148 ? format.normalizeFirstWord(s) 149 : out.append(format.normalizeWord(s.substring(i))).toString(); 150 } 151 152 /** 153 * Returns a {@code Converter} that converts strings from this format to {@code targetFormat}. 154 * 155 * @since 16.0 156 */ 157 @Beta 158 public Converter<String, String> converterTo(CaseFormat targetFormat) { 159 return new StringConverter(this, targetFormat); 160 } 161 162 private static final class StringConverter 163 extends Converter<String, String> implements Serializable { 164 165 private final CaseFormat sourceFormat; 166 private final CaseFormat targetFormat; 167 168 StringConverter(CaseFormat sourceFormat, CaseFormat targetFormat) { 169 this.sourceFormat = checkNotNull(sourceFormat); 170 this.targetFormat = checkNotNull(targetFormat); 171 } 172 173 @Override protected String doForward(String s) { 174 // TODO(kevinb): remove null boilerplate (convert() will do it automatically) 175 return s == null ? null : sourceFormat.to(targetFormat, s); 176 } 177 178 @Override protected String doBackward(String s) { 179 // TODO(kevinb): remove null boilerplate (convert() will do it automatically) 180 return s == null ? null : targetFormat.to(sourceFormat, s); 181 } 182 183 @Override public boolean equals(@Nullable Object object) { 184 if (object instanceof StringConverter) { 185 StringConverter that = (StringConverter) object; 186 return sourceFormat.equals(that.sourceFormat) 187 && targetFormat.equals(that.targetFormat); 188 } 189 return false; 190 } 191 192 @Override public int hashCode() { 193 return sourceFormat.hashCode() ^ targetFormat.hashCode(); 194 } 195 196 @Override public String toString() { 197 return sourceFormat + ".converterTo(" + targetFormat + ")"; 198 } 199 200 private static final long serialVersionUID = 0L; 201 } 202 203 abstract String normalizeWord(String word); 204 205 private String normalizeFirstWord(String word) { 206 return (this == LOWER_CAMEL) ? Ascii.toLowerCase(word) : normalizeWord(word); 207 } 208 209 private static String firstCharOnlyToUpper(String word) { 210 return (word.isEmpty()) 211 ? word 212 : new StringBuilder(word.length()) 213 .append(Ascii.toUpperCase(word.charAt(0))) 214 .append(Ascii.toLowerCase(word.substring(1))) 215 .toString(); 216 } 217}