1bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/* 21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2006 The Guava Authors 3bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 4bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Licensed under the Apache License, Version 2.0 (the "License"); 5bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * you may not use this file except in compliance with the License. 6bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * You may obtain a copy of the License at 7bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 8bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * http://www.apache.org/licenses/LICENSE-2.0 9bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 10bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Unless required by applicable law or agreed to in writing, software 11bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * distributed under the License is distributed on an "AS IS" BASIS, 12bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * See the License for the specific language governing permissions and 14bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * limitations under the License. 15bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 16bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 17bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorpackage com.google.common.base; 18bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.GwtCompatible; 20bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 21bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/** 221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Utility class for converting between various ASCII case formats. 23bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 24bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @author Mike Bostock 251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 1.0 26bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert@GwtCompatible 28bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorpublic enum CaseFormat { 29bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 30bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Hyphenated variable naming convention, e.g., "lower-hyphen". 31bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert LOWER_HYPHEN(CharMatcher.is('-'), "-"), 33bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 34bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 35bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * C++ variable naming convention, e.g., "lower_underscore". 36bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert LOWER_UNDERSCORE(CharMatcher.is('_'), "_"), 38bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 39bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 40bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Java variable naming convention, e.g., "lowerCamel". 41bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), ""), 43bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 44bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 45bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Java and C++ class naming convention, e.g., "UpperCamel". 46bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), ""), 48bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 49bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 50bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE". 51bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert UPPER_UNDERSCORE(CharMatcher.is('_'), "_"); 53bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private final CharMatcher wordBoundary; 55bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor private final String wordSeparator; 56bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert CaseFormat(CharMatcher wordBoundary, String wordSeparator) { 58bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor this.wordBoundary = wordBoundary; 59bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor this.wordSeparator = wordSeparator; 60bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 61bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 62bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Converts the specified {@code String s} from this format to the specified {@code format}. A 641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * "best effort" approach is taken; if {@code s} does not conform to the assumed format, then the 651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * behavior of this method is undefined but we make a reasonable effort at converting anyway. 66bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 67bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor public String to(CaseFormat format, String s) { 68bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (format == null) { 69bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor throw new NullPointerException(); 70bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 71bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (s == null) { 72bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor throw new NullPointerException(); 73bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 74bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 75bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (format == this) { 76bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return s; 77bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 78bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 79bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /* optimize cases where no camel conversion is required */ 80bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor switch (this) { 81bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor case LOWER_HYPHEN: 82bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor switch (format) { 831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case LOWER_UNDERSCORE: 841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return s.replace('-', '_'); 851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case UPPER_UNDERSCORE: 861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Ascii.toUpperCase(s.replace('-', '_')); 87bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 88bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor break; 89bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor case LOWER_UNDERSCORE: 90bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor switch (format) { 911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case LOWER_HYPHEN: 921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return s.replace('_', '-'); 931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case UPPER_UNDERSCORE: 941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Ascii.toUpperCase(s); 95bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 96bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor break; 97bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor case UPPER_UNDERSCORE: 98bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor switch (format) { 991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case LOWER_HYPHEN: 1001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Ascii.toLowerCase(s.replace('_', '-')); 1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case LOWER_UNDERSCORE: 1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Ascii.toLowerCase(s); 103bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 104bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor break; 105bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 106bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 1071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // otherwise, deal with camel conversion 108bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor StringBuilder out = null; 109bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor int i = 0; 1101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert int j = -1; 1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert while ((j = wordBoundary.indexIn(s, ++j)) != -1) { 112bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (i == 0) { 1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // include some extra space for separators 114bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor out = new StringBuilder(s.length() + 4 * wordSeparator.length()); 115bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor out.append(format.normalizeFirstWord(s.substring(i, j))); 116bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } else { 117bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor out.append(format.normalizeWord(s.substring(i, j))); 118bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 119bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor out.append(format.wordSeparator); 120bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor i = j + wordSeparator.length(); 121bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 122bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (i == 0) { 123bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return format.normalizeFirstWord(s); 124bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 125bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor out.append(format.normalizeWord(s.substring(i))); 126bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return out.toString(); 127bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 128bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 129bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor private String normalizeFirstWord(String word) { 130bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor switch (this) { 1311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case LOWER_CAMEL: 1321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Ascii.toLowerCase(word); 1331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert default: 1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return normalizeWord(word); 135bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 136bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 137bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 138bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor private String normalizeWord(String word) { 139bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor switch (this) { 1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case LOWER_HYPHEN: 1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Ascii.toLowerCase(word); 1421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case LOWER_UNDERSCORE: 1431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Ascii.toLowerCase(word); 1441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case LOWER_CAMEL: 1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return firstCharOnlyToUpper(word); 1461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case UPPER_CAMEL: 1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return firstCharOnlyToUpper(word); 1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert case UPPER_UNDERSCORE: 1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return Ascii.toUpperCase(word); 150bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 151bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor throw new RuntimeException("unknown case: " + this); 152bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 153bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private static String firstCharOnlyToUpper(String word) { 1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert int length = word.length(); 1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (length == 0) { 1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return word; 1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return new StringBuilder(length) 1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert .append(Ascii.toUpperCase(word.charAt(0))) 1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert .append(Ascii.toLowerCase(word.substring(1))) 1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert .toString(); 163bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 164bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor} 165