1/*
2 * Copyright (C) 2006 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.base;
18
19import com.google.common.annotations.GwtCompatible;
20
21/**
22 * Utility class for converting between various ASCII case formats.
23 *
24 * @author Mike Bostock
25 * @since 1.0
26 */
27@GwtCompatible
28public enum CaseFormat {
29  /**
30   * Hyphenated variable naming convention, e.g., "lower-hyphen".
31   */
32  LOWER_HYPHEN(CharMatcher.is('-'), "-"),
33
34  /**
35   * C++ variable naming convention, e.g., "lower_underscore".
36   */
37  LOWER_UNDERSCORE(CharMatcher.is('_'), "_"),
38
39  /**
40   * Java variable naming convention, e.g., "lowerCamel".
41   */
42  LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), ""),
43
44  /**
45   * Java and C++ class naming convention, e.g., "UpperCamel".
46   */
47  UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), ""),
48
49  /**
50   * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE".
51   */
52  UPPER_UNDERSCORE(CharMatcher.is('_'), "_");
53
54  private final CharMatcher wordBoundary;
55  private final String wordSeparator;
56
57  CaseFormat(CharMatcher wordBoundary, String wordSeparator) {
58    this.wordBoundary = wordBoundary;
59    this.wordSeparator = wordSeparator;
60  }
61
62  /**
63   * Converts the specified {@code String s} from this format to the specified {@code format}. A
64   * "best effort" approach is taken; if {@code s} does not conform to the assumed format, then the
65   * behavior of this method is undefined but we make a reasonable effort at converting anyway.
66   */
67  public String to(CaseFormat format, String s) {
68    if (format == null) {
69      throw new NullPointerException();
70    }
71    if (s == null) {
72      throw new NullPointerException();
73    }
74
75    if (format == this) {
76      return s;
77    }
78
79    /* optimize cases where no camel conversion is required */
80    switch (this) {
81      case LOWER_HYPHEN:
82        switch (format) {
83          case LOWER_UNDERSCORE:
84            return s.replace('-', '_');
85          case UPPER_UNDERSCORE:
86            return Ascii.toUpperCase(s.replace('-', '_'));
87        }
88        break;
89      case LOWER_UNDERSCORE:
90        switch (format) {
91          case LOWER_HYPHEN:
92            return s.replace('_', '-');
93          case UPPER_UNDERSCORE:
94            return Ascii.toUpperCase(s);
95        }
96        break;
97      case UPPER_UNDERSCORE:
98        switch (format) {
99          case LOWER_HYPHEN:
100            return Ascii.toLowerCase(s.replace('_', '-'));
101          case LOWER_UNDERSCORE:
102            return Ascii.toLowerCase(s);
103        }
104        break;
105    }
106
107    // otherwise, deal with camel conversion
108    StringBuilder out = null;
109    int i = 0;
110    int j = -1;
111    while ((j = wordBoundary.indexIn(s, ++j)) != -1) {
112      if (i == 0) {
113        // include some extra space for separators
114        out = new StringBuilder(s.length() + 4 * wordSeparator.length());
115        out.append(format.normalizeFirstWord(s.substring(i, j)));
116      } else {
117        out.append(format.normalizeWord(s.substring(i, j)));
118      }
119      out.append(format.wordSeparator);
120      i = j + wordSeparator.length();
121    }
122    if (i == 0) {
123      return format.normalizeFirstWord(s);
124    }
125    out.append(format.normalizeWord(s.substring(i)));
126    return out.toString();
127  }
128
129  private String normalizeFirstWord(String word) {
130    switch (this) {
131      case LOWER_CAMEL:
132        return Ascii.toLowerCase(word);
133      default:
134        return normalizeWord(word);
135    }
136  }
137
138  private String normalizeWord(String word) {
139    switch (this) {
140      case LOWER_HYPHEN:
141        return Ascii.toLowerCase(word);
142      case LOWER_UNDERSCORE:
143        return Ascii.toLowerCase(word);
144      case LOWER_CAMEL:
145        return firstCharOnlyToUpper(word);
146      case UPPER_CAMEL:
147        return firstCharOnlyToUpper(word);
148      case UPPER_UNDERSCORE:
149        return Ascii.toUpperCase(word);
150    }
151    throw new RuntimeException("unknown case: " + this);
152  }
153
154  private static String firstCharOnlyToUpper(String word) {
155    int length = word.length();
156    if (length == 0) {
157      return word;
158    }
159    return new StringBuilder(length)
160        .append(Ascii.toUpperCase(word.charAt(0)))
161        .append(Ascii.toLowerCase(word.substring(1)))
162        .toString();
163  }
164}
165