    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
    017    package com.google.common.base;
    018
    019    import com.google.common.annotations.GwtCompatible;
    020
    021    /**
    022     * Utility class for converting between various ASCII case formats.
    023     *
    024     * @author Mike Bostock
    025     * @since 1.0
    026     */
    027    @GwtCompatible
    028    public enum CaseFormat {
    029      /**
    030       * Hyphenated variable naming convention, e.g., "lower-hyphen".
    031       */
    032      LOWER_HYPHEN(CharMatcher.is('-'), "-"),
    033
    034      /**
    035       * C++ variable naming convention, e.g., "lower_underscore".
    036       */
    037      LOWER_UNDERSCORE(CharMatcher.is('_'), "_"),
    038
    039      /**
    040       * Java variable naming convention, e.g., "lowerCamel".
    041       */
    042      LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), ""),
    043
    044      /**
    045       * Java and C++ class naming convention, e.g., "UpperCamel".
    046       */
    047      UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), ""),
    048
    049      /**
    050       * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE".
    051       */
    052      UPPER_UNDERSCORE(CharMatcher.is('_'), "_");
    053
    054      private final CharMatcher wordBoundary;
    055      private final String wordSeparator;
    056
    057      CaseFormat(CharMatcher wordBoundary, String wordSeparator) {
    058        this.wordBoundary = wordBoundary;
    059        this.wordSeparator = wordSeparator;
    060      }
    061
    062      /**
    063       * Converts the specified {@code String s} from this format to the specified {@code format}. A
    064       * "best effort" approach is taken; if {@code s} does not conform to the assumed format, then the
    065       * behavior of this method is undefined but we make a reasonable effort at converting anyway.
    066       */
    067      public String to(CaseFormat format, String s) {
    068        if (format == null) {
    069          throw new NullPointerException();
    070        }
    071        if (s == null) {
    072          throw new NullPointerException();
    073        }
    074
    075        if (format == this) {
    076          return s;
    077        }
    078
    079        /* optimize cases where no camel conversion is required */
    080        switch (this) {
    081          case LOWER_HYPHEN:
    082            switch (format) {
    083              case LOWER_UNDERSCORE:
    084                return s.replace('-', '_');
    085              case UPPER_UNDERSCORE:
    086                return Ascii.toUpperCase(s.replace('-', '_'));
    087            }
    088            break;
    089          case LOWER_UNDERSCORE:
    090            switch (format) {
    091              case LOWER_HYPHEN:
    092                return s.replace('_', '-');
    093              case UPPER_UNDERSCORE:
    094                return Ascii.toUpperCase(s);
    095            }
    096            break;
    097          case UPPER_UNDERSCORE:
    098            switch (format) {
    099              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    }