001/*
002 * Copyright (C) 2006 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.base;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020
021import com.google.common.annotations.GwtCompatible;
022
023/**
024 * Utility class for converting between various ASCII case formats.
025 *
026 * @author Mike Bostock
027 * @since 1.0
028 */
029@GwtCompatible
030public enum CaseFormat {
031  /**
032   * Hyphenated variable naming convention, e.g., "lower-hyphen".
033   */
034  LOWER_HYPHEN(CharMatcher.is('-'), "-") {
035    @Override String normalizeWord(String word) {
036      return Ascii.toLowerCase(word);
037    }
038    @Override String convert(CaseFormat format, String s) {
039      if (format == LOWER_UNDERSCORE) {
040        return s.replace('-', '_');
041      }
042      if (format == UPPER_UNDERSCORE) {
043        return Ascii.toUpperCase(s.replace('-', '_'));
044      }
045      return super.convert(format, s);
046    }
047  },
048
049  /**
050   * C++ variable naming convention, e.g., "lower_underscore".
051   */
052  LOWER_UNDERSCORE(CharMatcher.is('_'), "_") {
053    @Override String normalizeWord(String word) {
054      return Ascii.toLowerCase(word);
055    }
056    @Override String convert(CaseFormat format, String s) {
057      if (format == LOWER_HYPHEN) {
058        return s.replace('_', '-');
059      }
060      if (format == UPPER_UNDERSCORE) {
061        return Ascii.toUpperCase(s);
062      }
063      return super.convert(format, s);
064    }
065  },
066
067  /**
068   * Java variable naming convention, e.g., "lowerCamel".
069   */
070  LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") {
071    @Override String normalizeWord(String word) {
072      return firstCharOnlyToUpper(word);
073    }
074  },
075
076  /**
077   * Java and C++ class naming convention, e.g., "UpperCamel".
078   */
079  UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") {
080    @Override String normalizeWord(String word) {
081      return firstCharOnlyToUpper(word);
082    }
083  },
084
085  /**
086   * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE".
087   */
088  UPPER_UNDERSCORE(CharMatcher.is('_'), "_") {
089    @Override String normalizeWord(String word) {
090      return Ascii.toUpperCase(word);
091    }
092    @Override String convert(CaseFormat format, String s) {
093      if (format == LOWER_HYPHEN) {
094        return Ascii.toLowerCase(s.replace('_', '-'));
095      }
096      if (format == LOWER_UNDERSCORE) {
097        return Ascii.toLowerCase(s);
098      }
099      return super.convert(format, s);
100    }
101  };
102
103  private final CharMatcher wordBoundary;
104  private final String wordSeparator;
105
106  CaseFormat(CharMatcher wordBoundary, String wordSeparator) {
107    this.wordBoundary = wordBoundary;
108    this.wordSeparator = wordSeparator;
109  }
110
111  /**
112   * Converts the specified {@code String str} from this format to the specified {@code format}. A
113   * "best effort" approach is taken; if {@code str} does not conform to the assumed format, then
114   * the behavior of this method is undefined but we make a reasonable effort at converting anyway.
115   */
116  public final String to(CaseFormat format, String str) {
117    checkNotNull(format);
118    checkNotNull(str);
119    return (format == this) ? str : convert(format, str);
120  }
121
122  /**
123   * Enum values can override for performance reasons.
124   */
125  String convert(CaseFormat format, String s) {
126    // deal with camel conversion
127    StringBuilder out = null;
128    int i = 0;
129    int j = -1;
130    while ((j = wordBoundary.indexIn(s, ++j)) != -1) {
131      if (i == 0) {
132        // include some extra space for separators
133        out = new StringBuilder(s.length() + 4 * wordSeparator.length());
134        out.append(format.normalizeFirstWord(s.substring(i, j)));
135      } else {
136        out.append(format.normalizeWord(s.substring(i, j)));
137      }
138      out.append(format.wordSeparator);
139      i = j + wordSeparator.length();
140    }
141    return (i == 0)
142      ? format.normalizeFirstWord(s)
143      : out.append(format.normalizeWord(s.substring(i))).toString();
144  }
145
146  abstract String normalizeWord(String word);
147
148  private String normalizeFirstWord(String word) {
149    return (this == LOWER_CAMEL) ? Ascii.toLowerCase(word) : normalizeWord(word);
150  }
151
152  private static String firstCharOnlyToUpper(String word) {
153    return (word.isEmpty())
154        ? word
155        : new StringBuilder(word.length())
156            .append(Ascii.toUpperCase(word.charAt(0)))
157            .append(Ascii.toLowerCase(word.substring(1)))
158            .toString();
159  }
160}