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