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