/* * Copyright (C) 2012-2015 DataStax Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.datastax.driver.mapping; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.regex.Pattern; /** * Implementations of industry common naming conventions. */ public class NamingConventions { /** * Represents a naming convention where all letters are lower cased, * and words are not separated by any special character. E.g. "myxmlparser". */ public static final NamingConvention LOWER_CASE = new SingleWordNamingConvention(false); /** * Represents a naming convention where all letters are upper cased, * and words are not separated by any special character. E.g. "MYXMLPARSER". */ public static final NamingConvention UPPER_CASE = new SingleWordNamingConvention(true); /** * Represents <a href="https://en.wikipedia.org/wiki/Snake_case">snake case</a> naming convention, meaning all letters are lower cased, * and words are separated by an underscore ("_"). E.g. "my_xml_parser". */ public static final NamingConvention LOWER_SNAKE_CASE = new CharDelimitedNamingConvention("_", false); /** * Represents <a href="https://en.wikipedia.org/wiki/Snake_case">snake case</a> naming convention, meaning all letters are upper cased, * and words are separated by an underscore ("_"). E.g. "MY_XML_PARSER". */ public static final NamingConvention UPPER_SNAKE_CASE = new CharDelimitedNamingConvention("_", true); /** * Represents <a href="https://en.wikipedia.org/wiki/Letter_case#Special_case_styles">Lisp case</a> naming convention, * meaning all letters are lower cased, * and words are separated by a dash sign ("-"). E.g. "my-xml-parser" */ public static final NamingConvention LOWER_LISP_CASE = new CharDelimitedNamingConvention("-", false); /** * Represents <a href="https://en.wikipedia.org/wiki/Letter_case#Special_case_styles">Lisp case</a> naming convention, * meaning all letters are upper cased, * and words are separated by a dash sign ("-"). E.g. "MY-XML-PARSER". */ public static final NamingConvention UPPER_LISP_CASE = new CharDelimitedNamingConvention("-", true); /** * Represents the default <a href="https://en.wikipedia.org/wiki/Camel_case">Camel case</a> naming convention, * with a lower cased first letter. * * @see LowerCamelCase */ public static final NamingConvention LOWER_CAMEL_CASE = new LowerCamelCase(); /** * Represents the default <a href="https://en.wikipedia.org/wiki/Camel_case">Camel case</a> naming convention, * with an upper cased first letter. * * @see UpperCamelCase */ public static final NamingConvention UPPER_CAMEL_CASE = new UpperCamelCase(); /** * Represents <a href="https://en.wikipedia.org/wiki/Camel_case">Camel case</a> * naming convention with a lower cased first letter. * <p/> * E.g. "myXmlParser" and "myXMLParser". Note that both examples are valid * lower camel case forms. The first one takes abbreviations as any other * words where the first letter is upper case, and the rest are lower case * (hence - "Xml"), while the latter upper cases all letters of an abbreviation * (hence - "XML"). * <p/> * Additionally, many different Java naming conventions introduce prefixes * for field naming, some examples: * <ul> * <li><a href="http://source.android.com/source/code-style.html#follow-field-naming-conventions">Android</a></li> * <li><a href="https://en.wikipedia.org/wiki/Hungarian_notation">Hungarian Notation</a></li> * <li><a href="http://stackoverflow.com/questions/1899683/is-there-a-standard-in-java-for-underscore-in-front-of-variable-or-class-nam">Underscore</a></li> * </ul> * Those prefixes can be supported. For example, if this convention is * configured with {@code ignorablePrefixes} set to "_" then a field * named "_myXmlParser" will be split in 3 words only: "my", "Xml", "Parser". */ public static class LowerCamelCase extends CamelCase { private final boolean upperCaseAbbreviations; /** * @param upperCaseAbbreviations {@code true} to uppercase all abbreviations, * {@code false} to treat abbreviations as any other word * @param ignorablePrefixes string prefixes to trim if constant field name prefixes are used */ public LowerCamelCase(boolean upperCaseAbbreviations, String... ignorablePrefixes) { super(ignorablePrefixes); this.upperCaseAbbreviations = upperCaseAbbreviations; } /** * @param ignorablePrefixes string prefixes to trim if constant field name prefixes are used */ public LowerCamelCase(String... ignorablePrefixes) { this(false, ignorablePrefixes); } /** * @param upperCaseAbbreviations {@code true} to uppercase all abbreviations, * {@code false} to treat abbreviations as any other word */ public LowerCamelCase(boolean upperCaseAbbreviations) { this(upperCaseAbbreviations, new String[0]); } @Override public String join(List<Word> input) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < input.size(); i++) { Word word = input.get(i); String value; if (i == 0) { value = word.getValue().toLowerCase(); } else if (upperCaseAbbreviations && word.isAbbreviation()) { value = word.getValue().toUpperCase(); } else { value = word.getValue().substring(0, 1).toUpperCase() + word.getValue().substring(1).toLowerCase(); } builder.append(value); } return builder.toString(); } } /** * Represents <a href="https://en.wikipedia.org/wiki/Camel_case">Camel case</a> * naming convention with an upper cased first letter. * <p/> * E.g. "MyXmlParser" and "MyXMLParser". Note that both examples are valid * upper camel case forms. The first one takes abbreviations as any other * words where the first letter is upper case, and the rest are lower case * (hence - "Xml"), while the latter upper cases all letters of an abbreviation * (hence - "XML"). * <p/> * Additionally, many different Java naming conventions introduce prefixes * for field naming, some examples: * <ul> * <li><a href="http://source.android.com/source/code-style.html#follow-field-naming-conventions">Android</a></li> * <li><a href="https://en.wikipedia.org/wiki/Hungarian_notation">Hungarian Notation</a></li> * <li><a href="http://stackoverflow.com/questions/1899683/is-there-a-standard-in-java-for-underscore-in-front-of-variable-or-class-nam">Underscore</a></li> * </ul> * Those prefixes can be supported. For example, if this convention is * configured with {@code ignorablePrefixes} set to "_" then a field * named "_MyXmlParser" will be split in 3 words only: "My", "Xml", "Parser". */ public static class UpperCamelCase extends CamelCase { private final boolean upperCaseAbbreviations; /** * @param upperCaseAbbreviations {@code true} to uppercase all abbreviations, * {@code false} to treat abbreviations as any other word * @param ignorablePrefixes string prefixes to trim if constant field name prefixes are used */ public UpperCamelCase(boolean upperCaseAbbreviations, String... ignorablePrefixes) { super(ignorablePrefixes); this.upperCaseAbbreviations = upperCaseAbbreviations; } /** * @param ignorablePrefixes string prefixes to trim if constant field name prefixes are used */ public UpperCamelCase(String... ignorablePrefixes) { this(false, ignorablePrefixes); } /** * @param upperCaseAbbreviations {@code true} to uppercase all abbreviations, * {@code false} to treat abbreviations as any other word */ public UpperCamelCase(boolean upperCaseAbbreviations) { this(upperCaseAbbreviations, new String[0]); } @Override public String join(List<Word> input) { StringBuilder builder = new StringBuilder(); for (Word word : input) { String value; if (upperCaseAbbreviations && word.isAbbreviation()) { value = word.getValue().toUpperCase(); } else { value = word.getValue().substring(0, 1).toUpperCase() + word.getValue().substring(1).toLowerCase(); } builder.append(value); } return builder.toString(); } } public abstract static class CamelCase implements NamingConvention { private static final Pattern SPLIT_PATTERN = Pattern.compile(String.format("%s|%s|%s", "(?<=[A-Z])(?=[A-Z][a-z])", "(?<=[^A-Z])(?=[A-Z])", "(?<=[A-Za-z])(?=[^A-Za-z])" )); private static final Pattern ALL_UPPERCASE_PATTERN = Pattern.compile("([A-Z])*"); private static final Comparator<String> STRING_LENGTH_COMPARATOR = new Comparator<String>() { @Override public int compare(String o1, String o2) { return o2.length() - o1.length(); } }; private final Pattern ignorablePrefixPattern; protected CamelCase(String... ignorablePrefixes) { Arrays.sort(ignorablePrefixes, STRING_LENGTH_COMPARATOR); StringBuilder builder = new StringBuilder(); for (int i = 0; i < ignorablePrefixes.length; i++) { if (i > 0) { builder.append("|"); } builder.append("^"); builder.append(ignorablePrefixes[i]); } ignorablePrefixPattern = Pattern.compile(builder.toString()); } @Override public List<Word> split(String input) { List<Word> result = new LinkedList<Word>(); // slice all ignorable prefixes, then split for (String value : SPLIT_PATTERN.split(ignorablePrefixPattern.matcher(input).replaceAll(""))) { // if all uppercase, mark as abbreviation (e.g. MyXMLParser) boolean isAbbreviation = ALL_UPPERCASE_PATTERN.matcher(value).matches(); Word word = new Word(value, isAbbreviation); result.add(word); } return result; } } public static class CharDelimitedNamingConvention implements NamingConvention { private final String delimiter; private final boolean isUpperCase; protected CharDelimitedNamingConvention(String delimiter, boolean isUpperCase) { this.delimiter = delimiter; this.isUpperCase = isUpperCase; } @Override public List<Word> split(String input) { List<Word> result = new LinkedList<Word>(); for (String value : input.split(delimiter)) { result.add(new Word(value)); } return result; } @Override public String join(List<Word> input) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < input.size(); i++) { if (i > 0) { builder.append(delimiter); } builder.append(input.get(i).getValue()); } String result = builder.toString(); return isUpperCase ? result.toUpperCase() : result.toLowerCase(); } } public static class SingleWordNamingConvention implements NamingConvention { private final boolean isUpperCase; protected SingleWordNamingConvention(boolean isUpperCase) { this.isUpperCase = isUpperCase; } @Override public List<Word> split(String input) { List<Word> result = new LinkedList<Word>(); result.add(new Word(input)); return result; } @Override public String join(List<Word> input) { StringBuilder builder = new StringBuilder(); for (Word word : input) { builder.append(word.getValue()); } String result = builder.toString(); return isUpperCase ? result.toUpperCase() : result.toLowerCase(); } } }