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