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 {@code Converter} that converts strings from this format to {@code targetFormat}. 155 * 156 * @since 16.0 157 */ 158 public Converter<String, String> converterTo(CaseFormat targetFormat) { 159 return new StringConverter(this, targetFormat); 160 } 161 162 private static final class StringConverter extends Converter<String, String> 163 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 174 protected String doForward(String s) { 175 return sourceFormat.to(targetFormat, s); 176 } 177 178 @Override 179 protected String doBackward(String s) { 180 return targetFormat.to(sourceFormat, s); 181 } 182 183 @Override 184 public boolean equals(@CheckForNull Object object) { 185 if (object instanceof StringConverter) { 186 StringConverter that = (StringConverter) object; 187 return sourceFormat.equals(that.sourceFormat) && targetFormat.equals(that.targetFormat); 188 } 189 return false; 190 } 191 192 @Override 193 public int hashCode() { 194 return sourceFormat.hashCode() ^ targetFormat.hashCode(); 195 } 196 197 @Override 198 public String toString() { 199 return sourceFormat + ".converterTo(" + targetFormat + ")"; 200 } 201 202 private static final long serialVersionUID = 0L; 203 } 204 205 abstract String normalizeWord(String word); 206 207 String normalizeFirstWord(String word) { 208 return normalizeWord(word); 209 } 210 211 private static String firstCharOnlyToUpper(String word) { 212 return word.isEmpty() 213 ? word 214 : Ascii.toUpperCase(word.charAt(0)) + Ascii.toLowerCase(word.substring(1)); 215 } 216}