001/*
002 * Configurate
003 * Copyright (C) zml and Configurate contributors
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.spongepowered.configurate.util;
018
019import java.util.regex.Matcher;
020import java.util.regex.Pattern;
021
022/**
023 * Standard naming schemes.
024 *
025 * @since 4.0.0
026 */
027public enum NamingSchemes implements NamingScheme {
028
029    /**
030     * Passes through names unchanged.
031     *
032     * @since 4.0.0
033     */
034    PASSTHROUGH {
035        @Override
036        public String coerce(final String input) {
037            return input;
038        }
039    },
040
041    /**
042     * Reformats names to {@code camelCase} style.
043     *
044     * @since 4.0.0
045     */
046    CAMEL_CASE {
047        @Override
048        @SuppressWarnings("JdkObsolete") // StringBuilder methods not present until JDK 9
049        public String coerce(final String input) {
050            final Matcher match = DASH_UNDERSCORE.matcher(input);
051            if (!match.find()) {
052                return input;
053            }
054            final StringBuffer ret = new StringBuffer(input.length());
055            do {
056                match.appendReplacement(ret, "");
057                ret.appendCodePoint(input.codePointAt(match.start()));
058                ret.appendCodePoint(Character.toUpperCase(input.codePointBefore(match.end())));
059            } while (match.find());
060            match.appendTail(ret);
061            return ret.toString();
062        }
063    },
064
065    /**
066     * Reformats names to {@code snake_case} format.
067     *
068     * @since 4.0.0
069     */
070    SNAKE_CASE {
071        @Override
072        public String coerce(final String input) {
073            return NamingSchemes.enforceLowerCaseSeparatorChar(input, UNDERSCORE, DASH);
074        }
075    },
076
077    /**
078     * Reformats names to {@code lower-case-dashed} format.
079     *
080     * @since 4.0.0
081     */
082    LOWER_CASE_DASHED {
083        @Override
084        public String coerce(final String input) {
085            return NamingSchemes.enforceLowerCaseSeparatorChar(input, DASH, UNDERSCORE);
086        }
087    };
088
089    private static final Pattern DASH_UNDERSCORE = Pattern.compile(".[-_].");
090    private static final char UNDERSCORE = '_';
091    private static final char DASH = '-';
092
093    // Common logic for snake_case and dash-separated
094    private static String enforceLowerCaseSeparatorChar(final String input, final char preferredDelimiter, final char convertDelimiter) {
095        final StringBuilder build = new StringBuilder(input);
096        for (int i = 0; i < build.length(); i++) {
097            final int ch = build.codePointAt(i);
098            if (ch == convertDelimiter) {
099                if (i != 0 && i != build.length() - 1) { // only convert actual separators
100                    build.setCharAt(i, preferredDelimiter);
101                }
102            } else if (Character.isUpperCase(ch)) {
103                build.insert(i++, preferredDelimiter);
104                final int lower = Character.toLowerCase(ch);
105                if (Character.isBmpCodePoint(lower)) {
106                    build.setCharAt(i, (char) lower);
107                } else {
108                    build.setCharAt(i++, Character.highSurrogate(lower));
109                    build.setCharAt(i, Character.lowSurrogate(lower));
110                }
111            }
112        }
113        return build.toString();
114
115    }
116
117}