diff --git a/LICENSE b/LICENSE index b14bc8a2dda8..6b3f53421ddf 100644 --- a/LICENSE +++ b/LICENSE @@ -285,6 +285,10 @@ SOURCE/JAVA-CORE This product contains test cases adapted from Test Framework for Apache Drill (https://github.com/apache/drill-test-framework). * sql/src/test/resources/drill/window + This product contains the class copied from https://github.com/FasterXML/jackson-databind + * extensions-core/s3-extensions/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategies.java + + MIT License ================================ diff --git a/extensions-core/s3-extensions/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategies.java b/extensions-core/s3-extensions/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategies.java new file mode 100644 index 000000000000..8d5a255cf6da --- /dev/null +++ b/extensions-core/s3-extensions/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategies.java @@ -0,0 +1,465 @@ +/** + * ******** NOTICE ************ + * https://github.com/FasterXML/jackson-databind/blob/2.16/LICENSE + * Stating the Credits to FasterXML project based on Apache Lic. + * No changes are done in the file other than attaching notice. + * Copyright 2023 Apache Druid + *

+ * 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. + * **************************** + * Container for standard {@link PropertyNamingStrategy} implementations + * and singleton instances. + *

+ * Added in Jackson 2.12 to resolve issue + * databind#2715. + * + * @since 2.12 + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.fasterxml.jackson.databind; + +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; + +import java.io.Serializable; +import java.util.Locale; + + +public abstract class PropertyNamingStrategies implements Serializable +{ + private static final long serialVersionUID = 2L; + + /* + /********************************************************************** + /* Static instances that may be referenced + /********************************************************************** + */ + + /** + * Naming convention used in Java, where words other than first are capitalized + * and no separator is used between words. Since this is the native Java naming convention, + * naming strategy will not do any transformation between names in data (JSON) and + * POJOS. + *

+ * Example external property names would be "numberValue", "namingStrategy", "theDefiniteProof". + */ + public static final PropertyNamingStrategy LOWER_CAMEL_CASE = LowerCamelCaseStrategy.INSTANCE; + + /** + * Naming convention used in languages like Pascal, where all words are capitalized + * and no separator is used between words. + * See {@link UpperCamelCaseStrategy} for details. + *

+ * Example external property names would be "NumberValue", "NamingStrategy", "TheDefiniteProof". + */ + public static final PropertyNamingStrategy UPPER_CAMEL_CASE = UpperCamelCaseStrategy.INSTANCE; + + /** + * Naming convention used in languages like C, where words are in lower-case + * letters, separated by underscores. + * See {@link SnakeCaseStrategy} for details. + *

+ * Example external property names would be "number_value", "naming_strategy", "the_definite_proof". + */ + public static final PropertyNamingStrategy SNAKE_CASE = SnakeCaseStrategy.INSTANCE; + + /** + * Naming convention in which the words are in upper-case letters, separated by underscores. + * See {@link UpperSnakeCaseStrategy} for details. + * + * @since 2.13 + *

+ */ + public static final PropertyNamingStrategy UPPER_SNAKE_CASE = UpperSnakeCaseStrategy.INSTANCE; + + /** + * Naming convention in which all words of the logical name are in lower case, and + * no separator is used between words. + * See {@link LowerCaseStrategy} for details. + *

+ * Example external property names would be "numbervalue", "namingstrategy", "thedefiniteproof". + */ + public static final PropertyNamingStrategy LOWER_CASE = LowerCaseStrategy.INSTANCE; + + /** + * Naming convention used in languages like Lisp, where words are in lower-case + * letters, separated by hyphens. + * See {@link KebabCaseStrategy} for details. + *

+ * Example external property names would be "number-value", "naming-strategy", "the-definite-proof". + */ + public static final PropertyNamingStrategy KEBAB_CASE = KebabCaseStrategy.INSTANCE; + + /** + * Naming convention widely used as configuration properties name, where words are in + * lower-case letters, separated by dots. + * See {@link LowerDotCaseStrategy} for details. + *

+ * Example external property names would be "number.value", "naming.strategy", "the.definite.proof". + */ + public static final PropertyNamingStrategy LOWER_DOT_CASE = LowerDotCaseStrategy.INSTANCE; + + /* + /********************************************************************** + /* Public base class for simple implementations + /********************************************************************** + */ + + /** + * Intermediate base class for simple implementations + */ + public abstract static class NamingBase extends PropertyNamingStrategy + { + private static final long serialVersionUID = 2L; + + @Override + public String nameForField(MapperConfig config, AnnotatedField field, String defaultName) + { + return translate(defaultName); + } + + @Override + public String nameForGetterMethod(MapperConfig config, AnnotatedMethod method, String defaultName) + { + return translate(defaultName); + } + + @Override + public String nameForSetterMethod(MapperConfig config, AnnotatedMethod method, String defaultName) + { + return translate(defaultName); + } + + @Override + public String nameForConstructorParameter(MapperConfig config, AnnotatedParameter ctorParam, String defaultName) + { + return translate(defaultName); + } + + public abstract String translate(String propertyName); + + /** + * Helper method to share implementation between snake and dotted case. + */ + protected String translateLowerCaseWithSeparator(final String input, final char separator) + { + if (input == null || input.isEmpty()) { + return input; + } + + final int length = input.length(); + final StringBuilder result = new StringBuilder(length + (length >> 1)); + int upperCount = 0; + for (int i = 0; i < length; ++i) { + char ch = input.charAt(i); + char lc = Character.toLowerCase(ch); + + if (lc == ch) { // lower-case letter means we can get new word + // but need to check for multi-letter upper-case (acronym), where assumption + // is that the last upper-case char is start of a new word + if (upperCount > 1) { + // so insert hyphen before the last character now + result.insert(result.length() - 1, separator); + } + upperCount = 0; + } else { + // Otherwise starts new word, unless beginning of string + if ((upperCount == 0) && (i > 0)) { + result.append(separator); + } + ++upperCount; + } + result.append(lc); + } + return result.toString(); + } + } + + /* + /********************************************************************** + /* Standard implementations + /********************************************************************** + */ + + /** + * A {@link PropertyNamingStrategy} that translates typical camel case Java + * property names to lower case JSON element names, separated by + * underscores. This implementation is somewhat lenient, in that it + * provides some additional translations beyond strictly translating from + * camel case only. In particular, the following translations are applied + * by this PropertyNamingStrategy. + * + *

+ *

+ * These rules result in the following additional example translations from + * Java property names to JSON element names. + *

+ */ + public static class SnakeCaseStrategy extends NamingBase + { + private static final long serialVersionUID = 2L; + + /** + * @since 2.14 + */ + public static final PropertyNamingStrategies.SnakeCaseStrategy INSTANCE = new PropertyNamingStrategies.SnakeCaseStrategy(); + + @Override + public String translate(String input) + { + if (input == null) { + return input; // garbage in, garbage out + } + int length = input.length(); + StringBuilder result = new StringBuilder(length * 2); + int resultLength = 0; + boolean wasPrevTranslated = false; + for (int i = 0; i < length; i++) { + char c = input.charAt(i); + if (i > 0 || c != '_') { // skip first starting underscore + if (Character.isUpperCase(c)) { + if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_') { + result.append('_'); + resultLength++; + } + c = Character.toLowerCase(c); + wasPrevTranslated = true; + } else { + wasPrevTranslated = false; + } + result.append(c); + resultLength++; + } + } + return resultLength > 0 ? result.toString() : input; + } + } + + /** + * A {@link PropertyNamingStrategy} that translates an input to the equivalent upper case snake + * case. The class extends {@link PropertyNamingStrategies.SnakeCaseStrategy} to retain the + * snake case conversion functionality offered by the strategy. + * + * @since 2.13 + */ + public static class UpperSnakeCaseStrategy extends SnakeCaseStrategy + { + private static final long serialVersionUID = 2L; + + /** + * @since 2.14 + */ + public static final PropertyNamingStrategies.UpperSnakeCaseStrategy INSTANCE = new PropertyNamingStrategies.UpperSnakeCaseStrategy(); + + @Override + public String translate(String input) + { + String output = super.translate(input); + if (output == null) { + return null; + } + return output.toUpperCase(Locale.ENGLISH); + } + } + + /** + * "No-operation" strategy that is equivalent to not specifying any + * strategy: will simply return suggested standard bean naming as-is. + */ + public static class LowerCamelCaseStrategy extends NamingBase + { + private static final long serialVersionUID = 2L; + + /** + * @since 2.14 + */ + public static final PropertyNamingStrategies.LowerCamelCaseStrategy INSTANCE = new PropertyNamingStrategies.LowerCamelCaseStrategy(); + + @Override + public String translate(String input) + { + return input; + } + } + + /** + * A {@link PropertyNamingStrategy} that translates typical camelCase Java + * property names to PascalCase JSON element names (i.e., with a capital + * first letter). In particular, the following translations are applied by + * this PropertyNamingStrategy. + * + * + *

+ * This rules result in the following example translation from + * Java property names to JSON element names. + *

+ */ + public static class UpperCamelCaseStrategy extends NamingBase + { + private static final long serialVersionUID = 2L; + + /** + * @since 2.14 + */ + public static final PropertyNamingStrategies.UpperCamelCaseStrategy INSTANCE = new PropertyNamingStrategies.UpperCamelCaseStrategy(); + + /** + * Converts camelCase to PascalCase + *

+ * For example, "userName" would be converted to + * "UserName". + * + * @param input formatted as camelCase string + * @return input converted to PascalCase format + */ + @Override + public String translate(String input) + { + if (input == null || input.isEmpty()) { + return input; // garbage in, garbage out + } + // Replace first lower-case letter with upper-case equivalent + char c = input.charAt(0); + char uc = Character.toUpperCase(c); + if (c == uc) { + return input; + } + StringBuilder sb = new StringBuilder(input); + sb.setCharAt(0, uc); + return sb.toString(); + } + } + + /** + * Simple strategy where external name simply only uses lower-case characters, + * and no separators. + * Conversion from internal name like "someOtherValue" would be into external name + * if "someothervalue". + */ + public static class LowerCaseStrategy extends NamingBase + { + private static final long serialVersionUID = 2L; + + /** + * @since 2.14 + */ + public static final PropertyNamingStrategies.LowerCaseStrategy INSTANCE = new PropertyNamingStrategies.LowerCaseStrategy(); + + @Override + public String translate(String input) + { + if (input == null || input.isEmpty()) { + return input; + } + return input.toLowerCase(Locale.ENGLISH); + } + } + + /** + * Naming strategy similar to {@link PropertyNamingStrategies.SnakeCaseStrategy}, + * but instead of underscores + * as separators, uses hyphens. Naming convention traditionally used for languages + * like Lisp. + */ + public static class KebabCaseStrategy extends NamingBase + { + private static final long serialVersionUID = 2L; + + /** + * @since 2.14 + */ + public static final PropertyNamingStrategies.KebabCaseStrategy INSTANCE = new PropertyNamingStrategies.KebabCaseStrategy(); + + @Override + public String translate(String input) + { + return translateLowerCaseWithSeparator(input, '-'); + } + } + + /** + * Naming strategy similar to {@link PropertyNamingStrategies.KebabCaseStrategy}, + * but instead of hyphens + * as separators, uses dots. Naming convention widely used as configuration properties name. + */ + public static class LowerDotCaseStrategy extends NamingBase + { + private static final long serialVersionUID = 2L; + + /** + * @since 2.14 + */ + public static final PropertyNamingStrategies.LowerDotCaseStrategy INSTANCE = new PropertyNamingStrategies.LowerDotCaseStrategy(); + + @Override + public String translate(String input) + { + return translateLowerCaseWithSeparator(input, '.'); + } + } +} diff --git a/pom.xml b/pom.xml index 6f667c4fc6ec..fbdc6f83bc09 100644 --- a/pom.xml +++ b/pom.xml @@ -1299,7 +1299,8 @@ org/apache/druid/query/TruncatedResponseContextException.class org/apache/druid/common/aws/AWSCredentials* - + com/fasterxml/jackson/databind/* + @@ -1329,6 +1330,9 @@ codestyle/LICENSE.txt true true + + *com/fasterxml/jackson/databind/* +