diff --git a/Java/commons-lang-EnumUtils_113/Dockerfile b/Java/commons-lang-EnumUtils_113/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-EnumUtils_113/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-EnumUtils_113/buggy.java b/Java/commons-lang-EnumUtils_113/buggy.java new file mode 100644 index 000000000..7d5e025f2 --- /dev/null +++ b/Java/commons-lang-EnumUtils_113/buggy.java @@ -0,0 +1,324 @@ +/* + * 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 org.apache.commons.lang3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + *

Utility library to provide helper methods for Java enums.

+ * + *

#ThreadSafe#

+ * + * @since 3.0 + */ +public class EnumUtils { + + private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted"; + private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits"; + private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type"; + private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined."; + + /** + * This constructor is public to permit tools that require a JavaBean + * instance to operate. + */ + public EnumUtils() { + } + + /** + *

Gets the {@code Map} of enums by name.

+ * + *

This method is useful when you need a map of enums by name.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @return the modifiable map of enum names to enums, never null + */ + public static > Map getEnumMap(final Class enumClass) { + final Map map = new LinkedHashMap(); + for (final E e: enumClass.getEnumConstants()) { + map.put(e.name(), e); + } + return map; + } + + /** + *

Gets the {@code List} of enums.

+ * + *

This method is useful when you need a list of enums rather than an array.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @return the modifiable list of enums, never null + */ + public static > List getEnumList(final Class enumClass) { + return new ArrayList(Arrays.asList(enumClass.getEnumConstants())); + } + + /** + *

Checks if the specified name is a valid enum for the class.

+ * + *

This method differs from {@link Enum#valueOf} in that checks if the name is + * a valid enum without needing to catch the exception.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @param enumName the enum name, null returns false + * @return true if the enum name is valid, otherwise false + */ + public static > boolean isValidEnum(final Class enumClass, final String enumName) { + if (enumName == null) { + return false; + } + try { + Enum.valueOf(enumClass, enumName); + return true; + } catch (final IllegalArgumentException ex) { + return false; + } + } + + /** + *

Gets the enum for the class, returning {@code null} if not found.

+ * + *

This method differs from {@link Enum#valueOf} in that it does not throw an exception + * for an invalid enum name.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @param enumName the enum name, null returns null + * @return the enum, null if not found + */ +/** + *

Gets the enum for the class, returning {@code null} if not found.

+ * + *

This method differs from {@link Enum#valueOf} in that it does not throw an exception + * for an invalid enum name.

+ * + * @param + * the type of the enumeration + * @param enumClass + * the class of the enum to query, not null + * @param enumName + * the enum name, null returns null + * @return the enum, null if not found + */ +public static > E getEnum(final java.lang.Class enumClass, final java.lang.String enumName) { + { + try { + return java.lang.Enum.valueOf(enumClass, /* NPEX_NULL_EXP */ + enumName); + } catch (final java.lang.IllegalArgumentException ex) { + return null; + } + } +} + + /** + *

Creates a long bit vector representation of the given subset of an Enum.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVector}.

+ * + *

Do not use this method if you have more than 64 values in your Enum, as this + * would create a value greater than a long can hold.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null}, neither containing {@code null} + * @param the type of the enumeration + * @return a long whose value provides a binary representation of the given set of enum values. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values, + * or if any {@code values} {@code null} + * @since 3.0.1 + * @see #generateBitVectors(Class, Iterable) + */ + public static > long generateBitVector(final Class enumClass, final Iterable values) { + checkBitVectorable(enumClass); + Validate.notNull(values); + long total = 0; + for (final E constant : values) { + Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED); + total |= 1L << constant.ordinal(); + } + return total; + } + + /** + *

Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVectors}.

+ * + *

Use this method if you have more than 64 values in your Enum.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null}, neither containing {@code null} + * @param the type of the enumeration + * @return a long[] whose values provide a binary representation of the given set of enum values + * with least significant digits rightmost. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null} + * @since 3.2 + */ + public static > long[] generateBitVectors(final Class enumClass, final Iterable values) { + asEnum(enumClass); + Validate.notNull(values); + final EnumSet condensed = EnumSet.noneOf(enumClass); + for (final E constant : values) { + Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED); + condensed.add(constant); + } + final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1]; + for (final E value : condensed) { + result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE); + } + ArrayUtils.reverse(result); + return result; + } + + /** + *

Creates a long bit vector representation of the given array of Enum values.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVector}.

+ * + *

Do not use this method if you have more than 64 values in your Enum, as this + * would create a value greater than a long can hold.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null} + * @param the type of the enumeration + * @return a long whose value provides a binary representation of the given set of enum values. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + * @see #generateBitVectors(Class, Iterable) + */ + public static > long generateBitVector(final Class enumClass, final E... values) { + Validate.noNullElements(values); + return generateBitVector(enumClass, Arrays. asList(values)); + } + + /** + *

Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVectors}.

+ * + *

Use this method if you have more than 64 values in your Enum.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null}, neither containing {@code null} + * @param the type of the enumeration + * @return a long[] whose values provide a binary representation of the given set of enum values + * with least significant digits rightmost. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null} + * @since 3.2 + */ + public static > long[] generateBitVectors(final Class enumClass, final E... values) { + asEnum(enumClass); + Validate.noNullElements(values); + final EnumSet condensed = EnumSet.noneOf(enumClass); + Collections.addAll(condensed, values); + final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1]; + for (final E value : condensed) { + result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE); + } + ArrayUtils.reverse(result); + return result; + } + + /** + *

Convert a long value created by {@link EnumUtils#generateBitVector} into the set of + * enum values that it represents.

+ * + *

If you store this value, beware any changes to the enum that would affect ordinal values.

+ * @param enumClass the class of the enum we are working with, not {@code null} + * @param value the long value representation of a set of enum values + * @param the type of the enumeration + * @return a set of enum values + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + */ + public static > EnumSet processBitVector(final Class enumClass, final long value) { + checkBitVectorable(enumClass).getEnumConstants(); + return processBitVectors(enumClass, value); + } + + /** + *

Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of + * enum values that it represents.

+ * + *

If you store this value, beware any changes to the enum that would affect ordinal values.

+ * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the long[] bearing the representation of a set of enum values, least significant digits rightmost, not {@code null} + * @param the type of the enumeration + * @return a set of enum values + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class + * @since 3.2 + */ + public static > EnumSet processBitVectors(final Class enumClass, final long... values) { + final EnumSet results = EnumSet.noneOf(asEnum(enumClass)); + final long[] lvalues = ArrayUtils.clone(Validate.notNull(values)); + ArrayUtils.reverse(lvalues); + for (final E constant : enumClass.getEnumConstants()) { + final int block = constant.ordinal() / Long.SIZE; + if (block < lvalues.length && (lvalues[block] & 1L << (constant.ordinal() % Long.SIZE)) != 0) { + results.add(constant); + } + } + return results; + } + + /** + * Validate that {@code enumClass} is compatible with representation in a {@code long}. + * @param the type of the enumeration + * @param enumClass to check + * @return {@code enumClass} + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + */ + private static > Class checkBitVectorable(final Class enumClass) { + final E[] constants = asEnum(enumClass).getEnumConstants(); + Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, + Integer.valueOf(constants.length), enumClass.getSimpleName(), Integer.valueOf(Long.SIZE)); + + return enumClass; + } + + /** + * Validate {@code enumClass}. + * @param the type of the enumeration + * @param enumClass to check + * @return {@code enumClass} + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class + * @since 3.2 + */ + private static > Class asEnum(final Class enumClass) { + Validate.notNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED); + Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass); + return enumClass; + } +} diff --git a/Java/commons-lang-EnumUtils_113/metadata.json b/Java/commons-lang-EnumUtils_113/metadata.json new file mode 100644 index 000000000..a35cd4554 --- /dev/null +++ b/Java/commons-lang-EnumUtils_113/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-EnumUtils_113", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/EnumUtils.java", + "line": 130, + "npe_method": "getEnum", + "deref_field": "enumName", + "npe_class": "EnumUtils", + "repo": "commons-lang", + "bug_id": "EnumUtils_113" + } +} diff --git a/Java/commons-lang-EnumUtils_113/npe.json b/Java/commons-lang-EnumUtils_113/npe.json new file mode 100644 index 000000000..d59ab6643 --- /dev/null +++ b/Java/commons-lang-EnumUtils_113/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/EnumUtils.java", + "line": 130, + "npe_method": "getEnum", + "deref_field": "enumName", + "npe_class": "EnumUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-EnumUtils_90/Dockerfile b/Java/commons-lang-EnumUtils_90/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-EnumUtils_90/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-EnumUtils_90/buggy.java b/Java/commons-lang-EnumUtils_90/buggy.java new file mode 100644 index 000000000..26a406b36 --- /dev/null +++ b/Java/commons-lang-EnumUtils_90/buggy.java @@ -0,0 +1,324 @@ +/* + * 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 org.apache.commons.lang3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + *

Utility library to provide helper methods for Java enums.

+ * + *

#ThreadSafe#

+ * + * @since 3.0 + */ +public class EnumUtils { + + private static final String NULL_ELEMENTS_NOT_PERMITTED = "null elements not permitted"; + private static final String CANNOT_STORE_S_S_VALUES_IN_S_BITS = "Cannot store %s %s values in %s bits"; + private static final String S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE = "%s does not seem to be an Enum type"; + private static final String ENUM_CLASS_MUST_BE_DEFINED = "EnumClass must be defined."; + + /** + * This constructor is public to permit tools that require a JavaBean + * instance to operate. + */ + public EnumUtils() { + } + + /** + *

Gets the {@code Map} of enums by name.

+ * + *

This method is useful when you need a map of enums by name.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @return the modifiable map of enum names to enums, never null + */ + public static > Map getEnumMap(final Class enumClass) { + final Map map = new LinkedHashMap(); + for (final E e: enumClass.getEnumConstants()) { + map.put(e.name(), e); + } + return map; + } + + /** + *

Gets the {@code List} of enums.

+ * + *

This method is useful when you need a list of enums rather than an array.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @return the modifiable list of enums, never null + */ + public static > List getEnumList(final Class enumClass) { + return new ArrayList(Arrays.asList(enumClass.getEnumConstants())); + } + + /** + *

Checks if the specified name is a valid enum for the class.

+ * + *

This method differs from {@link Enum#valueOf} in that checks if the name is + * a valid enum without needing to catch the exception.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @param enumName the enum name, null returns false + * @return true if the enum name is valid, otherwise false + */ +/** + *

Checks if the specified name is a valid enum for the class.

+ * + *

This method differs from {@link Enum#valueOf} in that checks if the name is + * a valid enum without needing to catch the exception.

+ * + * @param + * the type of the enumeration + * @param enumClass + * the class of the enum to query, not null + * @param enumName + * the enum name, null returns false + * @return true if the enum name is valid, otherwise false + */ +public static > boolean isValidEnum(final java.lang.Class enumClass, final java.lang.String enumName) { + { + try { + java.lang.Enum.valueOf(enumClass, /* NPEX_NULL_EXP */ + enumName); + return true; + } catch (final java.lang.IllegalArgumentException ex) { + return false; + } + } +} + + /** + *

Gets the enum for the class, returning {@code null} if not found.

+ * + *

This method differs from {@link Enum#valueOf} in that it does not throw an exception + * for an invalid enum name.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @param enumName the enum name, null returns null + * @return the enum, null if not found + */ + public static > E getEnum(final Class enumClass, final String enumName) { + if (enumName == null) { + return null; + } + try { + return Enum.valueOf(enumClass, enumName); + } catch (final IllegalArgumentException ex) { + return null; + } + } + + /** + *

Creates a long bit vector representation of the given subset of an Enum.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVector}.

+ * + *

Do not use this method if you have more than 64 values in your Enum, as this + * would create a value greater than a long can hold.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null}, neither containing {@code null} + * @param the type of the enumeration + * @return a long whose value provides a binary representation of the given set of enum values. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values, + * or if any {@code values} {@code null} + * @since 3.0.1 + * @see #generateBitVectors(Class, Iterable) + */ + public static > long generateBitVector(final Class enumClass, final Iterable values) { + checkBitVectorable(enumClass); + Validate.notNull(values); + long total = 0; + for (final E constant : values) { + Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED); + total |= 1L << constant.ordinal(); + } + return total; + } + + /** + *

Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVectors}.

+ * + *

Use this method if you have more than 64 values in your Enum.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null}, neither containing {@code null} + * @param the type of the enumeration + * @return a long[] whose values provide a binary representation of the given set of enum values + * with least significant digits rightmost. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null} + * @since 3.2 + */ + public static > long[] generateBitVectors(final Class enumClass, final Iterable values) { + asEnum(enumClass); + Validate.notNull(values); + final EnumSet condensed = EnumSet.noneOf(enumClass); + for (final E constant : values) { + Validate.isTrue(constant != null, NULL_ELEMENTS_NOT_PERMITTED); + condensed.add(constant); + } + final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1]; + for (final E value : condensed) { + result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE); + } + ArrayUtils.reverse(result); + return result; + } + + /** + *

Creates a long bit vector representation of the given array of Enum values.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVector}.

+ * + *

Do not use this method if you have more than 64 values in your Enum, as this + * would create a value greater than a long can hold.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null} + * @param the type of the enumeration + * @return a long whose value provides a binary representation of the given set of enum values. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + * @see #generateBitVectors(Class, Iterable) + */ + public static > long generateBitVector(final Class enumClass, final E... values) { + Validate.noNullElements(values); + return generateBitVector(enumClass, Arrays. asList(values)); + } + + /** + *

Creates a bit vector representation of the given subset of an Enum using as many {@code long}s as needed.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVectors}.

+ * + *

Use this method if you have more than 64 values in your Enum.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null}, neither containing {@code null} + * @param the type of the enumeration + * @return a long[] whose values provide a binary representation of the given set of enum values + * with least significant digits rightmost. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class, or if any {@code values} {@code null} + * @since 3.2 + */ + public static > long[] generateBitVectors(final Class enumClass, final E... values) { + asEnum(enumClass); + Validate.noNullElements(values); + final EnumSet condensed = EnumSet.noneOf(enumClass); + Collections.addAll(condensed, values); + final long[] result = new long[(enumClass.getEnumConstants().length - 1) / Long.SIZE + 1]; + for (final E value : condensed) { + result[value.ordinal() / Long.SIZE] |= 1L << (value.ordinal() % Long.SIZE); + } + ArrayUtils.reverse(result); + return result; + } + + /** + *

Convert a long value created by {@link EnumUtils#generateBitVector} into the set of + * enum values that it represents.

+ * + *

If you store this value, beware any changes to the enum that would affect ordinal values.

+ * @param enumClass the class of the enum we are working with, not {@code null} + * @param value the long value representation of a set of enum values + * @param the type of the enumeration + * @return a set of enum values + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + */ + public static > EnumSet processBitVector(final Class enumClass, final long value) { + checkBitVectorable(enumClass).getEnumConstants(); + return processBitVectors(enumClass, value); + } + + /** + *

Convert a {@code long[]} created by {@link EnumUtils#generateBitVectors} into the set of + * enum values that it represents.

+ * + *

If you store this value, beware any changes to the enum that would affect ordinal values.

+ * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the long[] bearing the representation of a set of enum values, least significant digits rightmost, not {@code null} + * @param the type of the enumeration + * @return a set of enum values + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class + * @since 3.2 + */ + public static > EnumSet processBitVectors(final Class enumClass, final long... values) { + final EnumSet results = EnumSet.noneOf(asEnum(enumClass)); + final long[] lvalues = ArrayUtils.clone(Validate.notNull(values)); + ArrayUtils.reverse(lvalues); + for (final E constant : enumClass.getEnumConstants()) { + final int block = constant.ordinal() / Long.SIZE; + if (block < lvalues.length && (lvalues[block] & 1L << (constant.ordinal() % Long.SIZE)) != 0) { + results.add(constant); + } + } + return results; + } + + /** + * Validate that {@code enumClass} is compatible with representation in a {@code long}. + * @param the type of the enumeration + * @param enumClass to check + * @return {@code enumClass} + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + */ + private static > Class checkBitVectorable(final Class enumClass) { + final E[] constants = asEnum(enumClass).getEnumConstants(); + Validate.isTrue(constants.length <= Long.SIZE, CANNOT_STORE_S_S_VALUES_IN_S_BITS, + Integer.valueOf(constants.length), enumClass.getSimpleName(), Integer.valueOf(Long.SIZE)); + + return enumClass; + } + + /** + * Validate {@code enumClass}. + * @param the type of the enumeration + * @param enumClass to check + * @return {@code enumClass} + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class + * @since 3.2 + */ + private static > Class asEnum(final Class enumClass) { + Validate.notNull(enumClass, ENUM_CLASS_MUST_BE_DEFINED); + Validate.isTrue(enumClass.isEnum(), S_DOES_NOT_SEEM_TO_BE_AN_ENUM_TYPE, enumClass); + return enumClass; + } +} diff --git a/Java/commons-lang-EnumUtils_90/metadata.json b/Java/commons-lang-EnumUtils_90/metadata.json new file mode 100644 index 000000000..175cc1eaa --- /dev/null +++ b/Java/commons-lang-EnumUtils_90/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-EnumUtils_90", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/EnumUtils.java", + "line": 107, + "npe_method": "isValidEnum", + "deref_field": "enumName", + "npe_class": "EnumUtils", + "repo": "commons-lang", + "bug_id": "EnumUtils_90" + } +} diff --git a/Java/commons-lang-EnumUtils_90/npe.json b/Java/commons-lang-EnumUtils_90/npe.json new file mode 100644 index 000000000..45b19b944 --- /dev/null +++ b/Java/commons-lang-EnumUtils_90/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/EnumUtils.java", + "line": 107, + "npe_method": "isValidEnum", + "deref_field": "enumName", + "npe_class": "EnumUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-EventUtilsTest_215/Dockerfile b/Java/commons-lang-EventUtilsTest_215/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-EventUtilsTest_215/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-EventUtilsTest_215/buggy.java b/Java/commons-lang-EventUtilsTest_215/buggy.java new file mode 100644 index 000000000..722858d76 --- /dev/null +++ b/Java/commons-lang-EventUtilsTest_215/buggy.java @@ -0,0 +1,280 @@ +/* + * 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 org.apache.commons.lang3.event; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.VetoableChangeListener; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.Date; +import java.util.Map; +import java.util.TreeMap; + +import javax.naming.event.ObjectChangeListener; + +import org.junit.Test; + +/** + * @since 3.0 + */ +public class EventUtilsTest +{ + + @Test + public void testConstructor() { + assertNotNull(new EventUtils()); + final Constructor[] cons = EventUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(EventUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(EventUtils.class.getModifiers())); + } + + @Test + public void testAddEventListener() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + final PropertyChangeListener listener = handler.createListener(PropertyChangeListener.class); + assertEquals(0, handler.getEventCount("propertyChange")); + EventUtils.addEventListener(src, PropertyChangeListener.class, listener); + assertEquals(0, handler.getEventCount("propertyChange")); + src.setProperty("newValue"); + assertEquals(1, handler.getEventCount("propertyChange")); + } + + @Test + public void testAddEventListenerWithNoAddMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + final ObjectChangeListener listener = handler.createListener(ObjectChangeListener.class); + try + { + EventUtils.addEventListener(src, ObjectChangeListener.class, listener); + fail("Should not be allowed to add a listener to an object that doesn't support it."); + } + catch (final IllegalArgumentException e) + { + assertEquals("Class " + src.getClass().getName() + " does not have a public add" + ObjectChangeListener.class.getSimpleName() + " method which takes a parameter of type " + ObjectChangeListener.class.getName() + ".", e.getMessage()); + } + } + + @Test + public void testAddEventListenerThrowsException() + { + final ExceptionEventSource src = new ExceptionEventSource(); + try + { + EventUtils.addEventListener(src, PropertyChangeListener.class, new PropertyChangeListener() + { + @Override + public void propertyChange(final PropertyChangeEvent e) + { + // Do nothing! + } + }); + fail("Add method should have thrown an exception, so method should fail."); + } + catch (final RuntimeException e) + { + + } + } + + @Test + public void testAddEventListenerWithPrivateAddMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + final VetoableChangeListener listener = handler.createListener(VetoableChangeListener.class); + try + { + EventUtils.addEventListener(src, VetoableChangeListener.class, listener); + fail("Should not be allowed to add a listener to an object that doesn't support it."); + } + catch (final IllegalArgumentException e) + { + assertEquals("Class " + src.getClass().getName() + " does not have a public add" + VetoableChangeListener.class.getSimpleName() + " method which takes a parameter of type " + VetoableChangeListener.class.getName() + ".", e.getMessage()); + } + } + + @Test + public void testBindEventsToMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCounter counter = new EventCounter(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class); + assertEquals(0, counter.getCount()); + src.setProperty("newValue"); + assertEquals(1, counter.getCount()); + } + + + @Test + public void testBindEventsToMethodWithEvent() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCounterWithEvent counter = new EventCounterWithEvent(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class); + assertEquals(0, counter.getCount()); + src.setProperty("newValue"); + assertEquals(1, counter.getCount()); + } + + + @Test + public void testBindFilteredEventsToMethod() + { + final MultipleEventSource src = new MultipleEventSource(); + final EventCounter counter = new EventCounter(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, MultipleEventListener.class, "event1"); + assertEquals(0, counter.getCount()); + src.listeners.fire().event1(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(0), Integer.valueOf(1))); + assertEquals(1, counter.getCount()); + src.listeners.fire().event2(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(1), Integer.valueOf(2))); + assertEquals(1, counter.getCount()); + } + + public static interface MultipleEventListener + { + public void event1(PropertyChangeEvent e); + + public void event2(PropertyChangeEvent e); + } + + public static class EventCounter + { + private int count; + + public void eventOccurred() + { + count++; + } + + public int getCount() + { + return count; + } + } + + public static class EventCounterWithEvent + { + private int count; + + public void eventOccurred(final PropertyChangeEvent e) + { + count++; + } + + public int getCount() + { + return count; + } + } + + + private static class EventCountingInvociationHandler implements InvocationHandler + { + private final Map eventCounts = new TreeMap(); + + public L createListener(final Class listenerType) + { + return listenerType.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + new Class[]{listenerType}, + this)); + } + +public int getEventCount(final java.lang.String eventName) { + final java.lang.Integer count = eventCounts.get(eventName); + return /* NPEX_NULL_EXP */ + count.intValue(); +} + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable + { + final Integer count = eventCounts.get(method.getName()); + if (count == null) + { + eventCounts.put(method.getName(), Integer.valueOf(1)); + } + else + { + eventCounts.put(method.getName(), Integer.valueOf(count.intValue() + 1)); + } + return null; + } + } + + public static class MultipleEventSource + { + private final EventListenerSupport listeners = EventListenerSupport.create(MultipleEventListener.class); + + public void addMultipleEventListener(final MultipleEventListener listener) + { + listeners.addListener(listener); + } + } + + public static class ExceptionEventSource + { + public void addPropertyChangeListener(final PropertyChangeListener listener) + { + throw new RuntimeException(); + } + } + + public static class PropertyChangeSource + { + private final EventListenerSupport listeners = EventListenerSupport.create(PropertyChangeListener.class); + + private String property; + + public void setProperty(final String property) + { + final String oldValue = this.property; + this.property = property; + listeners.fire().propertyChange(new PropertyChangeEvent(this, "property", oldValue, property)); + } + + protected void addVetoableChangeListener(final VetoableChangeListener listener) + { + // Do nothing! + } + + public void addPropertyChangeListener(final PropertyChangeListener listener) + { + listeners.addListener(listener); + } + + public void removePropertyChangeListener(final PropertyChangeListener listener) + { + listeners.removeListener(listener); + } + } +} diff --git a/Java/commons-lang-EventUtilsTest_215/metadata.json b/Java/commons-lang-EventUtilsTest_215/metadata.json new file mode 100644 index 000000000..08855a1cf --- /dev/null +++ b/Java/commons-lang-EventUtilsTest_215/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-EventUtilsTest_215", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/commons/lang3/event/EventUtilsTest.java", + "line": 215, + "npe_method": "getEventCount", + "deref_field": "count", + "npe_class": "EventCountingInvociationHandler", + "repo": "commons-lang", + "bug_id": "EventUtilsTest_215" + } +} diff --git a/Java/commons-lang-EventUtilsTest_215/npe.json b/Java/commons-lang-EventUtilsTest_215/npe.json new file mode 100644 index 000000000..677958fb4 --- /dev/null +++ b/Java/commons-lang-EventUtilsTest_215/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/commons/lang3/event/EventUtilsTest.java", + "line": 215, + "npe_method": "getEventCount", + "deref_field": "count", + "npe_class": "EventCountingInvociationHandler" +} \ No newline at end of file diff --git a/Java/commons-lang-EventUtilsTest_222/Dockerfile b/Java/commons-lang-EventUtilsTest_222/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-EventUtilsTest_222/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-EventUtilsTest_222/buggy.java b/Java/commons-lang-EventUtilsTest_222/buggy.java new file mode 100644 index 000000000..fc0cb0fce --- /dev/null +++ b/Java/commons-lang-EventUtilsTest_222/buggy.java @@ -0,0 +1,275 @@ +/* + * 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 org.apache.commons.lang3.event; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.VetoableChangeListener; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.Date; +import java.util.Map; +import java.util.TreeMap; + +import javax.naming.event.ObjectChangeListener; + +import org.junit.Test; + +/** + * @since 3.0 + */ +public class EventUtilsTest +{ + + @Test + public void testConstructor() { + assertNotNull(new EventUtils()); + final Constructor[] cons = EventUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(EventUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(EventUtils.class.getModifiers())); + } + + @Test + public void testAddEventListener() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + final PropertyChangeListener listener = handler.createListener(PropertyChangeListener.class); + assertEquals(0, handler.getEventCount("propertyChange")); + EventUtils.addEventListener(src, PropertyChangeListener.class, listener); + assertEquals(0, handler.getEventCount("propertyChange")); + src.setProperty("newValue"); + assertEquals(1, handler.getEventCount("propertyChange")); + } + + @Test + public void testAddEventListenerWithNoAddMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + final ObjectChangeListener listener = handler.createListener(ObjectChangeListener.class); + try + { + EventUtils.addEventListener(src, ObjectChangeListener.class, listener); + fail("Should not be allowed to add a listener to an object that doesn't support it."); + } + catch (final IllegalArgumentException e) + { + assertEquals("Class " + src.getClass().getName() + " does not have a public add" + ObjectChangeListener.class.getSimpleName() + " method which takes a parameter of type " + ObjectChangeListener.class.getName() + ".", e.getMessage()); + } + } + + @Test + public void testAddEventListenerThrowsException() + { + final ExceptionEventSource src = new ExceptionEventSource(); + try + { + EventUtils.addEventListener(src, PropertyChangeListener.class, new PropertyChangeListener() + { + @Override + public void propertyChange(final PropertyChangeEvent e) + { + // Do nothing! + } + }); + fail("Add method should have thrown an exception, so method should fail."); + } + catch (final RuntimeException e) + { + + } + } + + @Test + public void testAddEventListenerWithPrivateAddMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + final VetoableChangeListener listener = handler.createListener(VetoableChangeListener.class); + try + { + EventUtils.addEventListener(src, VetoableChangeListener.class, listener); + fail("Should not be allowed to add a listener to an object that doesn't support it."); + } + catch (final IllegalArgumentException e) + { + assertEquals("Class " + src.getClass().getName() + " does not have a public add" + VetoableChangeListener.class.getSimpleName() + " method which takes a parameter of type " + VetoableChangeListener.class.getName() + ".", e.getMessage()); + } + } + + @Test + public void testBindEventsToMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCounter counter = new EventCounter(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class); + assertEquals(0, counter.getCount()); + src.setProperty("newValue"); + assertEquals(1, counter.getCount()); + } + + + @Test + public void testBindEventsToMethodWithEvent() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCounterWithEvent counter = new EventCounterWithEvent(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class); + assertEquals(0, counter.getCount()); + src.setProperty("newValue"); + assertEquals(1, counter.getCount()); + } + + + @Test + public void testBindFilteredEventsToMethod() + { + final MultipleEventSource src = new MultipleEventSource(); + final EventCounter counter = new EventCounter(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, MultipleEventListener.class, "event1"); + assertEquals(0, counter.getCount()); + src.listeners.fire().event1(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(0), Integer.valueOf(1))); + assertEquals(1, counter.getCount()); + src.listeners.fire().event2(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(1), Integer.valueOf(2))); + assertEquals(1, counter.getCount()); + } + + public static interface MultipleEventListener + { + public void event1(PropertyChangeEvent e); + + public void event2(PropertyChangeEvent e); + } + + public static class EventCounter + { + private int count; + + public void eventOccurred() + { + count++; + } + + public int getCount() + { + return count; + } + } + + public static class EventCounterWithEvent + { + private int count; + + public void eventOccurred(final PropertyChangeEvent e) + { + count++; + } + + public int getCount() + { + return count; + } + } + + + private static class EventCountingInvociationHandler implements InvocationHandler + { + private final Map eventCounts = new TreeMap(); + + public L createListener(final Class listenerType) + { + return listenerType.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + new Class[]{listenerType}, + this)); + } + + public int getEventCount(final String eventName) + { + final Integer count = eventCounts.get(eventName); + return count == null ? 0 : count.intValue(); + } + + @Override +public java.lang.Object invoke(final java.lang.Object proxy, final java.lang.reflect.Method method, final java.lang.Object[] args) throws java.lang.Throwable { + final java.lang.Integer count = eventCounts.get(method.getName()); + { + eventCounts.put(method.getName(), java.lang.Integer.valueOf(/* NPEX_NULL_EXP */ + count.intValue() + 1)); + } + return null; +} + } + + public static class MultipleEventSource + { + private final EventListenerSupport listeners = EventListenerSupport.create(MultipleEventListener.class); + + public void addMultipleEventListener(final MultipleEventListener listener) + { + listeners.addListener(listener); + } + } + + public static class ExceptionEventSource + { + public void addPropertyChangeListener(final PropertyChangeListener listener) + { + throw new RuntimeException(); + } + } + + public static class PropertyChangeSource + { + private final EventListenerSupport listeners = EventListenerSupport.create(PropertyChangeListener.class); + + private String property; + + public void setProperty(final String property) + { + final String oldValue = this.property; + this.property = property; + listeners.fire().propertyChange(new PropertyChangeEvent(this, "property", oldValue, property)); + } + + protected void addVetoableChangeListener(final VetoableChangeListener listener) + { + // Do nothing! + } + + public void addPropertyChangeListener(final PropertyChangeListener listener) + { + listeners.addListener(listener); + } + + public void removePropertyChangeListener(final PropertyChangeListener listener) + { + listeners.removeListener(listener); + } + } +} diff --git a/Java/commons-lang-EventUtilsTest_222/metadata.json b/Java/commons-lang-EventUtilsTest_222/metadata.json new file mode 100644 index 000000000..4b39d4ef1 --- /dev/null +++ b/Java/commons-lang-EventUtilsTest_222/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-EventUtilsTest_222", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/commons/lang3/event/EventUtilsTest.java", + "line": 223, + "npe_method": "invoke", + "deref_field": "count", + "npe_class": "EventCountingInvociationHandler", + "repo": "commons-lang", + "bug_id": "EventUtilsTest_222" + } +} diff --git a/Java/commons-lang-EventUtilsTest_222/npe.json b/Java/commons-lang-EventUtilsTest_222/npe.json new file mode 100644 index 000000000..68dfcbca9 --- /dev/null +++ b/Java/commons-lang-EventUtilsTest_222/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/commons/lang3/event/EventUtilsTest.java", + "line": 223, + "npe_method": "invoke", + "deref_field": "count", + "npe_class": "EventCountingInvociationHandler" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_142/Dockerfile b/Java/commons-lang-ExceptionUtils_142/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_142/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_142/buggy.java b/Java/commons-lang-ExceptionUtils_142/buggy.java new file mode 100644 index 000000000..04d6b8809 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_142/buggy.java @@ -0,0 +1,835 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated +/** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable + * the throwable to introspect for a cause, may be null + * @param methodNames + * the method names, null treated as default set + * @return the cause of the Throwable, +null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ +public static java.lang.Throwable getCause(final java.lang.Throwable throwable, java.lang.String[] methodNames) { + { + if (methodNames == null) { + final java.lang.Throwable cause = /* NPEX_NULL_EXP */ + throwable.getCause(); + if (cause != null) { + return cause; + } + methodNames = org.apache.commons.lang3.exception.ExceptionUtils.CAUSE_METHOD_NAMES; + } + for (final java.lang.String methodName : methodNames) { + if (methodName != null) { + final java.lang.Throwable legacyCause = org.apache.commons.lang3.exception.ExceptionUtils.getCauseUsingMethodName(throwable, methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + return null; + } +} + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_142/metadata.json b/Java/commons-lang-ExceptionUtils_142/metadata.json new file mode 100644 index 000000000..304e29e96 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_142/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_142", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 160, + "npe_method": "getCause", + "deref_field": "throwable", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_142" + } +} diff --git a/Java/commons-lang-ExceptionUtils_142/npe.json b/Java/commons-lang-ExceptionUtils_142/npe.json new file mode 100644 index 000000000..2db7b87fc --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_142/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 160, + "npe_method": "getCause", + "deref_field": "throwable", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_156/Dockerfile b/Java/commons-lang-ExceptionUtils_156/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_156/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_156/buggy.java b/Java/commons-lang-ExceptionUtils_156/buggy.java new file mode 100644 index 000000000..e4c0b6770 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_156/buggy.java @@ -0,0 +1,836 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated +/** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable + * the throwable to introspect for a cause, may be null + * @param methodNames + * the method names, null treated as default set + * @return the cause of the Throwable, +null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ +public static java.lang.Throwable getCause(final java.lang.Throwable throwable, java.lang.String[] methodNames) { + if (throwable == null) { + return null; + } + if (methodNames == null) { + final java.lang.Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + methodNames = org.apache.commons.lang3.exception.ExceptionUtils.CAUSE_METHOD_NAMES; + } + for (final java.lang.String methodName : methodNames) { + { + final java.lang.Throwable legacyCause = org.apache.commons.lang3.exception.ExceptionUtils.getCauseUsingMethodName(throwable, /* NPEX_NULL_EXP */ + methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + return null; +} + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_156/metadata.json b/Java/commons-lang-ExceptionUtils_156/metadata.json new file mode 100644 index 000000000..2a5b483fc --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_156/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_156", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 170, + "npe_method": "getCause", + "deref_field": "methodName", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_156" + } +} diff --git a/Java/commons-lang-ExceptionUtils_156/npe.json b/Java/commons-lang-ExceptionUtils_156/npe.json new file mode 100644 index 000000000..9654d3df5 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_156/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 170, + "npe_method": "getCause", + "deref_field": "methodName", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_158/Dockerfile b/Java/commons-lang-ExceptionUtils_158/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_158/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_158/buggy.java b/Java/commons-lang-ExceptionUtils_158/buggy.java new file mode 100644 index 000000000..5d1e16c5e --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_158/buggy.java @@ -0,0 +1,836 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated +/** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable + * the throwable to introspect for a cause, may be null + * @param methodNames + * the method names, null treated as default set + * @return the cause of the Throwable, +null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ +public static java.lang.Throwable getCause(final java.lang.Throwable throwable, java.lang.String[] methodNames) { + if (throwable == null) { + return null; + } + if (methodNames == null) { + final java.lang.Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + methodNames = org.apache.commons.lang3.exception.ExceptionUtils.CAUSE_METHOD_NAMES; + } + for (final java.lang.String methodName : methodNames) { + { + final java.lang.Throwable legacyCause = org.apache.commons.lang3.exception.ExceptionUtils.getCauseUsingMethodName(throwable, methodName); + { + return /* NPEX_NULL_EXP */ + legacyCause; + } + } + } + return null; +} + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_158/metadata.json b/Java/commons-lang-ExceptionUtils_158/metadata.json new file mode 100644 index 000000000..a3c50db6d --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_158/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_158", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 172, + "npe_method": "getCause", + "deref_field": "legacyCause", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_158" + } +} diff --git a/Java/commons-lang-ExceptionUtils_158/npe.json b/Java/commons-lang-ExceptionUtils_158/npe.json new file mode 100644 index 000000000..c5df43b27 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_158/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 172, + "npe_method": "getCause", + "deref_field": "legacyCause", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_461/Dockerfile b/Java/commons-lang-ExceptionUtils_461/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_461/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_461/buggy.java b/Java/commons-lang-ExceptionUtils_461/buggy.java new file mode 100644 index 000000000..05b6da58e --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_461/buggy.java @@ -0,0 +1,846 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + final Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + + methodNames = CAUSE_METHOD_NAMES; + } + + for (final String methodName : methodNames) { + if (methodName != null) { + final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + + return null; + } + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ +/** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable + * the throwable to output, may be null + * @param stream + * the stream to output to, may not be null + * @throws IllegalArgumentException + * if the stream is null + * @since 2.0 + */ +public static void printRootCauseStackTrace(final java.lang.Throwable throwable, final java.io.PrintStream stream) { + { + if (stream == null) { + throw new java.lang.IllegalArgumentException("The PrintStream must not be null"); + } + final java.lang.String[] trace = org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseStackTrace(/* NPEX_NULL_EXP */ + throwable); + for (final java.lang.String element : trace) { + stream.println(element); + } + stream.flush(); + } +} + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_461/metadata.json b/Java/commons-lang-ExceptionUtils_461/metadata.json new file mode 100644 index 000000000..9b909f5bf --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_461/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_461", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 488, + "npe_method": "printRootCauseStackTrace", + "deref_field": "throwable", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_461" + } +} diff --git a/Java/commons-lang-ExceptionUtils_461/npe.json b/Java/commons-lang-ExceptionUtils_461/npe.json new file mode 100644 index 000000000..ddf391d81 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_461/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 488, + "npe_method": "printRootCauseStackTrace", + "deref_field": "throwable", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_464/Dockerfile b/Java/commons-lang-ExceptionUtils_464/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_464/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_464/buggy.java b/Java/commons-lang-ExceptionUtils_464/buggy.java new file mode 100644 index 000000000..82766571b --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_464/buggy.java @@ -0,0 +1,846 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + final Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + + methodNames = CAUSE_METHOD_NAMES; + } + + for (final String methodName : methodNames) { + if (methodName != null) { + final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + + return null; + } + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ +/** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable + * the throwable to output, may be null + * @param stream + * the stream to output to, may not be null + * @throws IllegalArgumentException + * if the stream is null + * @since 2.0 + */ +public static void printRootCauseStackTrace(final java.lang.Throwable throwable, final java.io.PrintStream stream) { + if (throwable == null) { + return; + } + { + final java.lang.String[] trace = org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseStackTrace(throwable); + for (final java.lang.String element : trace) { + /* NPEX_NULL_EXP */ + stream.println(element); + } + stream.flush(); + } +} + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_464/metadata.json b/Java/commons-lang-ExceptionUtils_464/metadata.json new file mode 100644 index 000000000..be9b770e9 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_464/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_464", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 490, + "npe_method": "printRootCauseStackTrace", + "deref_field": "stream", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_464" + } +} diff --git a/Java/commons-lang-ExceptionUtils_464/npe.json b/Java/commons-lang-ExceptionUtils_464/npe.json new file mode 100644 index 000000000..ff3d1954d --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_464/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 490, + "npe_method": "printRootCauseStackTrace", + "deref_field": "stream", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_494/Dockerfile b/Java/commons-lang-ExceptionUtils_494/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_494/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_494/buggy.java b/Java/commons-lang-ExceptionUtils_494/buggy.java new file mode 100644 index 000000000..08dd4cccb --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_494/buggy.java @@ -0,0 +1,846 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + final Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + + methodNames = CAUSE_METHOD_NAMES; + } + + for (final String methodName : methodNames) { + if (methodName != null) { + final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + + return null; + } + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ +/** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable + * the throwable to output, may be null + * @param writer + * the writer to output to, may not be null + * @throws IllegalArgumentException + * if the writer is null + * @since 2.0 + */ +public static void printRootCauseStackTrace(final java.lang.Throwable throwable, final java.io.PrintWriter writer) { + { + if (writer == null) { + throw new java.lang.IllegalArgumentException("The PrintWriter must not be null"); + } + final java.lang.String[] trace = org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseStackTrace(/* NPEX_NULL_EXP */ + throwable); + for (final java.lang.String element : trace) { + writer.println(element); + } + writer.flush(); + } +} + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_494/metadata.json b/Java/commons-lang-ExceptionUtils_494/metadata.json new file mode 100644 index 000000000..037e3f580 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_494/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_494", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 521, + "npe_method": "printRootCauseStackTrace", + "deref_field": "throwable", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_494" + } +} diff --git a/Java/commons-lang-ExceptionUtils_494/npe.json b/Java/commons-lang-ExceptionUtils_494/npe.json new file mode 100644 index 000000000..3fe322575 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_494/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 521, + "npe_method": "printRootCauseStackTrace", + "deref_field": "throwable", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_497/Dockerfile b/Java/commons-lang-ExceptionUtils_497/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_497/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_497/buggy.java b/Java/commons-lang-ExceptionUtils_497/buggy.java new file mode 100644 index 000000000..1825cb76f --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_497/buggy.java @@ -0,0 +1,846 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + final Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + + methodNames = CAUSE_METHOD_NAMES; + } + + for (final String methodName : methodNames) { + if (methodName != null) { + final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + + return null; + } + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ +/** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable + * the throwable to output, may be null + * @param writer + * the writer to output to, may not be null + * @throws IllegalArgumentException + * if the writer is null + * @since 2.0 + */ +public static void printRootCauseStackTrace(final java.lang.Throwable throwable, final java.io.PrintWriter writer) { + if (throwable == null) { + return; + } + { + final java.lang.String[] trace = org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseStackTrace(throwable); + for (final java.lang.String element : trace) { + /* NPEX_NULL_EXP */ + writer.println(element); + } + writer.flush(); + } +} + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_497/metadata.json b/Java/commons-lang-ExceptionUtils_497/metadata.json new file mode 100644 index 000000000..60b171857 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_497/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_497", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 523, + "npe_method": "printRootCauseStackTrace", + "deref_field": "writer", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_497" + } +} diff --git a/Java/commons-lang-ExceptionUtils_497/npe.json b/Java/commons-lang-ExceptionUtils_497/npe.json new file mode 100644 index 000000000..0798c5248 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_497/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 523, + "npe_method": "printRootCauseStackTrace", + "deref_field": "writer", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_522/Dockerfile b/Java/commons-lang-ExceptionUtils_522/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_522/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_522/buggy.java b/Java/commons-lang-ExceptionUtils_522/buggy.java new file mode 100644 index 000000000..b3dcdc01b --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_522/buggy.java @@ -0,0 +1,839 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + final Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + + methodNames = CAUSE_METHOD_NAMES; + } + + for (final String methodName : methodNames) { + if (methodName != null) { + final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + + return null; + } + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ +// ----------------------------------------------------------------------- +/** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable + * the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ +public static java.lang.String[] getRootCauseStackTrace(final java.lang.Throwable throwable) { + { + final java.lang.Throwable[] throwables = org.apache.commons.lang3.exception.ExceptionUtils.getThrowables(/* NPEX_NULL_EXP */ + throwable); + final int count = throwables.length; + final java.util.List frames = new java.util.ArrayList(); + java.util.List nextTrace = org.apache.commons.lang3.exception.ExceptionUtils.getStackFrameList(throwables[count - 1]); + for (int i = count; (--i) >= 0;) { + final java.util.List trace = nextTrace; + if (i != 0) { + nextTrace = org.apache.commons.lang3.exception.ExceptionUtils.getStackFrameList(throwables[i - 1]); + org.apache.commons.lang3.exception.ExceptionUtils.removeCommonFrames(trace, nextTrace); + } + if (i == (count - 1)) { + frames.add(throwables[i].toString()); + } else { + frames.add(org.apache.commons.lang3.exception.ExceptionUtils.WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new java.lang.String[frames.size()]); + } +} + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_522/metadata.json b/Java/commons-lang-ExceptionUtils_522/metadata.json new file mode 100644 index 000000000..ea85f8f73 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_522/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_522", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 539, + "npe_method": "getRootCauseStackTrace", + "deref_field": "throwable", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_522" + } +} diff --git a/Java/commons-lang-ExceptionUtils_522/npe.json b/Java/commons-lang-ExceptionUtils_522/npe.json new file mode 100644 index 000000000..7f8365de6 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_522/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 539, + "npe_method": "getRootCauseStackTrace", + "deref_field": "throwable", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_677/Dockerfile b/Java/commons-lang-ExceptionUtils_677/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_677/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_677/buggy.java b/Java/commons-lang-ExceptionUtils_677/buggy.java new file mode 100644 index 000000000..0677180f3 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_677/buggy.java @@ -0,0 +1,836 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + final Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + + methodNames = CAUSE_METHOD_NAMES; + } + + for (final String methodName : methodNames) { + if (methodName != null) { + final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + + return null; + } + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ +// ----------------------------------------------------------------------- +/** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th + * the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ +public static java.lang.String getMessage(final java.lang.Throwable th) { + { + final java.lang.String clsName = org.apache.commons.lang3.ClassUtils.getShortClassName(/* NPEX_NULL_EXP */ + th, null); + final java.lang.String msg = th.getMessage(); + return (clsName + ": ") + org.apache.commons.lang3.StringUtils.defaultString(msg); + } +} + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(final Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_677/metadata.json b/Java/commons-lang-ExceptionUtils_677/metadata.json new file mode 100644 index 000000000..143f7cc9c --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_677/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_677", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 691, + "npe_method": "getMessage", + "deref_field": "th", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_677" + } +} diff --git a/Java/commons-lang-ExceptionUtils_677/npe.json b/Java/commons-lang-ExceptionUtils_677/npe.json new file mode 100644 index 000000000..c7629421f --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_677/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 691, + "npe_method": "getMessage", + "deref_field": "th", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExceptionUtils_698/Dockerfile b/Java/commons-lang-ExceptionUtils_698/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_698/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExceptionUtils_698/buggy.java b/Java/commons-lang-ExceptionUtils_698/buggy.java new file mode 100644 index 000000000..ce9fb4501 --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_698/buggy.java @@ -0,0 +1,837 @@ +/* + * 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 org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, null); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0, use {@link Throwable#getCause} instead + */ + @Deprecated + public static Throwable getCause(final Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + final Throwable cause = throwable.getCause(); + if (cause != null) { + return cause; + } + + methodNames = CAUSE_METHOD_NAMES; + } + + for (final String methodName : methodNames) { + if (methodName != null) { + final Throwable legacyCause = getCauseUsingMethodName(throwable, methodName); + if (legacyCause != null) { + return legacyCause; + } + } + } + + return null; + } + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(final Throwable throwable, final String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (final NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (final SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (final IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (final IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (final InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(final Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(final Throwable throwable) { + final List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + final List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(final Throwable throwable, final Class clazz, final int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(final Throwable throwable, final Class type, final int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested within the specified throwable + */ + private static int indexOf(final Throwable throwable, final Class type, int fromIndex, final boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + final Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(final Throwable throwable, final PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + final String trace[] = getRootCauseStackTrace(throwable); + for (final String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + final Throwable throwables[] = getThrowables(throwable); + final int count = throwables.length; + final List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + final List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(final List causeFrames, final List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + final String causeFrame = causeFrames.get(causeFrameIndex); + final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(final Throwable throwable) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(final Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(final String stackTrace) { + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(final Throwable t) { + final String stackTrace = getStackTrace(t); + final String linebreak = SystemUtils.LINE_SEPARATOR; + final StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + final List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + final String token = frames.nextToken(); + // Determine if the line starts with at + final int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(final Throwable th) { + if (th == null) { + return StringUtils.EMPTY; + } + final String clsName = ClassUtils.getShortClassName(th, null); + final String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ +// ----------------------------------------------------------------------- +/** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th + * the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ +public static java.lang.String getRootCauseMessage(final java.lang.Throwable th) { + java.lang.Throwable root = org.apache.commons.lang3.exception.ExceptionUtils.getRootCause(th); + root = /* NPEX_NULL_EXP */ + root; + return org.apache.commons.lang3.exception.ExceptionUtils.getMessage(root); +} + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. This method prevents throws clause + * pollution and reduces the clutter of "Caused by" exceptions in the + * stacktrace. + *

+ * The use of this technique may be controversial, but exceedingly useful to + * library developers. + * + * public int propagateExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Exception e) { + * return ExceptionUtils.rethrow(e); // propagates a checked exception + * } + * } + * + *

+ * This is an alternative to the more conservative approach of wrapping the + * checked exception in a RuntimeException: + * + * public int wrapExample { // note that there is no throws clause + * try { + * return invocation(); // throws IOException + * } catch (Error e) { + * throw e; + * } catch (RuntimeException e) { + * throw e; // wraps a checked exception + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); // wraps a checked exception + * } + * } + * + *

+ * One downside to using this approach is that the java compiler will not + * allow invoking code to specify a checked exception in a catch clause + * unless there is some code path within the try block that has invoked a + * method declared with that checked exception. If the invoking site wishes + * to catch the shaded checked exception, it must either invoke the shaded + * code through a method re-declaring the desired checked exception, or + * catch Exception and use the instanceof operator. Either of these + * techniques are required when interacting with non-java jvm code such as + * Jyton, Scala, or Groovy, since these languages do not consider any + * exceptions as checked. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method, as done in the propagateExample above, will satisfy the + * java compiler requirement that all code paths return a value. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static R rethrow(Throwable throwable) { + // claim that the typeErasure invocation throws a RuntimeException + return ExceptionUtils. typeErasure(throwable); + } + + /** + * Claim a Throwable is another Exception type using type erasure. This + * hides a checked exception from the java compiler, allowing a checked + * exception to be thrown without having the exception in the method's throw + * clause. + */ + @SuppressWarnings("unchecked") + private static R typeErasure(Throwable throwable) throws T { + throw (T) throwable; + } + + /** + * Throw a checked exception without adding the exception to the throws + * clause of the calling method. For checked exceptions, this method throws + * an UndeclaredThrowableException wrapping the checked exception. For + * Errors and RuntimeExceptions, the original exception is rethrown. + *

+ * The downside to using this approach is that invoking code which needs to + * handle specific checked exceptions must sniff up the exception chain to + * determine if the caught exception was caused by the checked exception. + * + * @param throwable + * The throwable to rethrow. + * @param The type of the returned value. + * @return Never actually returned, this generic type matches any type + * which the calling site requires. "Returning" the results of this + * method will satisfy the java compiler requirement that all code + * paths return a value. + * @since 3.5 + * @see #rethrow(Throwable) + * @see #hasCause(Throwable, Class) + */ + public static R wrapAndThrow(Throwable throwable) { + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + throw new UndeclaredThrowableException(throwable); + } + + /** + * Does the throwable's causal chain have an immediate or wrapped exception + * of the given type? + * + * @param chain + * The root of a Throwable causal chain. + * @param type + * The exception type to test. + * @return true, if chain is an instance of type or is an + * UndeclaredThrowableException wrapping a cause. + * @since 3.5 + * @see #wrapAndThrow(Throwable) + */ + public static boolean hasCause(Throwable chain, + Class type) { + if (chain instanceof UndeclaredThrowableException) { + chain = chain.getCause(); + } + return type.isInstance(chain); + } +} diff --git a/Java/commons-lang-ExceptionUtils_698/metadata.json b/Java/commons-lang-ExceptionUtils_698/metadata.json new file mode 100644 index 000000000..91b49778a --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_698/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExceptionUtils_698", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 711, + "npe_method": "getRootCauseMessage", + "deref_field": "root", + "npe_class": "ExceptionUtils", + "repo": "commons-lang", + "bug_id": "ExceptionUtils_698" + } +} diff --git a/Java/commons-lang-ExceptionUtils_698/npe.json b/Java/commons-lang-ExceptionUtils_698/npe.json new file mode 100644 index 000000000..e6f17689b --- /dev/null +++ b/Java/commons-lang-ExceptionUtils_698/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/exception/ExceptionUtils.java", + "line": 711, + "npe_method": "getRootCauseMessage", + "deref_field": "root", + "npe_class": "ExceptionUtils" +} \ No newline at end of file diff --git a/Java/commons-lang-ExtendedMessageFormatTest_130/Dockerfile b/Java/commons-lang-ExtendedMessageFormatTest_130/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_130/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExtendedMessageFormatTest_130/buggy.java b/Java/commons-lang-ExtendedMessageFormatTest_130/buggy.java new file mode 100644 index 000000000..02fa56590 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_130/buggy.java @@ -0,0 +1,482 @@ +/* + * 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 org.apache.commons.lang3.text; + +import org.junit.Test; +import org.junit.Before; +import static org.junit.Assert.*; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_4; + +import java.text.ChoiceFormat; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.SystemUtils; + +/** + * Test case for {@link ExtendedMessageFormat}. + * + * @since 2.4 + */ +public class ExtendedMessageFormatTest { + + private final Map registry = new HashMap(); + + @Before + public void setUp() throws Exception { + registry.put("lower", new LowerCaseFormatFactory()); + registry.put("upper", new UpperCaseFormatFactory()); + } + + /** + * Test extended formats. + */ + @Test + public void testExtendedFormats() { + final String pattern = "Lower: {0,lower} Upper: {1,upper}"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertPatternsEqual("TOPATTERN", pattern, emf.toPattern()); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"Foo", "Bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "BAR"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "BAR"})); + } + + /** + * Test Bug LANG-477 - out of memory error with escaped quote + */ + @Test + public void testEscapedQuote_LANG_477() { + final String pattern = "it''s a {0,lower} 'test'!"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("it's a dummy test!", emf.format(new Object[] {"DUMMY"})); + } + + /** + * Test Bug LANG-917 - IndexOutOfBoundsException and/or infinite loop when using a choice pattern + */ + @Test + public void testEmbeddedPatternInChoice() { + final String pattern = "Hi {0,lower}, got {1,choice,0#none|1#one|1<{1,number}}, {2,upper}!"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals(emf.format(new Object[] {"there", 3, "great"}), "Hi there, got 3, GREAT!"); + } + + /** + * Test Bug LANG-948 - Exception while using ExtendedMessageFormat and escaping braces + */ + @Test + public void testEscapedBraces_LANG_948() { + // message without placeholder because braces are escaped by quotes + final String pattern = "Message without placeholders '{}'"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("Message without placeholders {}", emf.format(new Object[] {"DUMMY"})); + + // message with placeholder because quotes are escaped by quotes + final String pattern2 = "Message with placeholder ''{0}''"; + final ExtendedMessageFormat emf2 = new ExtendedMessageFormat(pattern2, registry); + assertEquals("Message with placeholder 'DUMMY'", emf2.format(new Object[] {"DUMMY"})); + } + + /** + * Test extended and built in formats. + */ + @Test +/** + * Test extended and built in formats. + */ +public void testExtendedAndBuiltInFormats() { + final java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.set(2007, java.util.Calendar.JANUARY, 23, 18, 33, 05); + final java.lang.Object[] args = new java.lang.Object[]{ "John Doe", cal.getTime(), java.lang.Double.valueOf("12345.67") }; + final java.lang.String builtinsPattern = "DOB: {1,date,short} Salary: {2,number,currency}"; + final java.lang.String extendedPattern = "Name: {0,upper} "; + final java.lang.String pattern = extendedPattern + builtinsPattern; + final java.util.HashSet testLocales = new java.util.HashSet(); + testLocales.addAll(java.util.Arrays.asList(java.text.DateFormat.getAvailableLocales())); + testLocales.retainAll(java.util.Arrays.asList(java.text.NumberFormat.getAvailableLocales())); + testLocales.add(null); + for (final java.util.Locale locale : testLocales) { + final java.text.MessageFormat builtins = createMessageFormat(builtinsPattern, locale); + final java.lang.String expectedPattern = extendedPattern + builtins.toPattern(); + java.text.DateFormat df = null; + java.text.NumberFormat nf = null; + org.apache.commons.lang3.text.ExtendedMessageFormat emf = null; + { + df = java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT, /* NPEX_NULL_EXP */ + locale); + nf = java.text.NumberFormat.getCurrencyInstance(locale); + emf = new org.apache.commons.lang3.text.ExtendedMessageFormat(pattern, locale, registry); + } + final java.lang.StringBuilder expected = new java.lang.StringBuilder(); + expected.append("Name: "); + expected.append(args[0].toString().toUpperCase()); + expected.append(" DOB: "); + expected.append(df.format(args[1])); + expected.append(" Salary: "); + expected.append(nf.format(args[2])); + assertPatternsEqual("pattern comparison for locale " + locale, expectedPattern, emf.toPattern()); + assertEquals(java.lang.String.valueOf(locale), expected.toString(), emf.format(args)); + } +} + +// /** +// * Test extended formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{1,lower}|2.0#{1,upper}}"; +// ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// assertEquals("one", emf.format(new Object[] {Integer.valueOf(1), "ONE"})); +// assertEquals("TWO", emf.format(new Object[] {Integer.valueOf(2), "two"})); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } + +// /** +// * Test mixed extended and built-in formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedAndBuiltInWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{0} {1,lower} {2,number}|2.0#{0} {1,upper} {2,number,currency}}"; +// Object[] lowArgs = new Object[] {Integer.valueOf(1), "Low", Double.valueOf("1234.56")}; +// Object[] highArgs = new Object[] {Integer.valueOf(2), "High", Double.valueOf("9876.54")}; +// Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); +// Locale[] testLocales = new Locale[availableLocales.length + 1]; +// testLocales[0] = null; +// System.arraycopy(availableLocales, 0, testLocales, 1, availableLocales.length); +// for (int i = 0; i < testLocales.length; i++) { +// NumberFormat nf = null; +// NumberFormat cf = null; +// ExtendedMessageFormat emf = null; +// if (testLocales[i] == null) { +// nf = NumberFormat.getNumberInstance(); +// cf = NumberFormat.getCurrencyInstance(); +// emf = new ExtendedMessageFormat(pattern, registry); +// } else { +// nf = NumberFormat.getNumberInstance(testLocales[i]); +// cf = NumberFormat.getCurrencyInstance(testLocales[i]); +// emf = new ExtendedMessageFormat(pattern, testLocales[i], registry); +// } +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// String lowExpected = lowArgs[0] + " low " + nf.format(lowArgs[2]); +// String highExpected = highArgs[0] + " HIGH " + cf.format(highArgs[2]); +// assertEquals(lowExpected, emf.format(lowArgs)); +// assertEquals(highExpected, emf.format(highArgs)); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } +// } + + /** + * Test the built in choice format. + */ + @Test + public void testBuiltInChoiceFormat() { + final Object[] values = new Number[] {Integer.valueOf(1), Double.valueOf("2.2"), Double.valueOf("1234.5")}; + String choicePattern = null; + final Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); + + choicePattern = "{0,choice,1#One|2#Two|3#Many {0,number}}"; + for (final Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + + choicePattern = "{0,choice,1#''One''|2#\"Two\"|3#''{Many}'' {0,number}}"; + for (final Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + } + + /** + * Test the built in date/time formats + */ + @Test + public void testBuiltInDateTimeFormat() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + final Object[] args = new Object[] {cal.getTime()}; + final Locale[] availableLocales = DateFormat.getAvailableLocales(); + + checkBuiltInFormat("1: {0,date,short}", args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", args, availableLocales); + checkBuiltInFormat("6: {0,time,short}", args, availableLocales); + checkBuiltInFormat("7: {0,time,medium}", args, availableLocales); + checkBuiltInFormat("8: {0,time,long}", args, availableLocales); + checkBuiltInFormat("9: {0,time,full}", args, availableLocales); + checkBuiltInFormat("10: {0,time,HH:mm}", args, availableLocales); + checkBuiltInFormat("11: {0,date}", args, availableLocales); + checkBuiltInFormat("12: {0,time}", args, availableLocales); + } + + @Test + public void testOverriddenBuiltinFormat() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23); + final Object[] args = new Object[] {cal.getTime()}; + final Locale[] availableLocales = DateFormat.getAvailableLocales(); + final Map dateRegistry = Collections.singletonMap("date", new OverrideShortDateFormatFactory()); + + //check the non-overridden builtins: + checkBuiltInFormat("1: {0,date}", dateRegistry, args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", dateRegistry, args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", dateRegistry, args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", dateRegistry, args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", dateRegistry, args, availableLocales); + + //check the overridden format: + for (int i = -1; i < availableLocales.length; i++) { + final Locale locale = i < 0 ? null : availableLocales[i]; + final MessageFormat dateDefault = createMessageFormat("{0,date}", locale); + final String pattern = "{0,date,short}"; + final ExtendedMessageFormat dateShort = new ExtendedMessageFormat(pattern, locale, dateRegistry); + assertEquals("overridden date,short format", dateDefault.format(args), dateShort.format(args)); + assertEquals("overridden date,short pattern", pattern, dateShort.toPattern()); + } + } + + /** + * Test the built in number formats. + */ + @Test + public void testBuiltInNumberFormat() { + final Object[] args = new Object[] {Double.valueOf("6543.21")}; + final Locale[] availableLocales = NumberFormat.getAvailableLocales(); + checkBuiltInFormat("1: {0,number}", args, availableLocales); + checkBuiltInFormat("2: {0,number,integer}", args, availableLocales); + checkBuiltInFormat("3: {0,number,currency}", args, availableLocales); + checkBuiltInFormat("4: {0,number,percent}", args, availableLocales); + checkBuiltInFormat("5: {0,number,00000.000}", args, availableLocales); + } + + /** + * Test equals() and hashcode. + */ + @Test + public void testEqualsHashcode() { + final Map fmtRegistry = Collections.singletonMap("testfmt", new LowerCaseFormatFactory()); + final Map otherRegitry = Collections.singletonMap("testfmt", new UpperCaseFormatFactory()); + + final String pattern = "Pattern: {0,testfmt}"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + + ExtendedMessageFormat other = null; + + // Same object + assertTrue("same, equals()", emf.equals(emf)); + assertTrue("same, hashcode()", emf.hashCode() == emf.hashCode()); + + // Equal Object + other = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + assertTrue("equal, equals()", emf.equals(other)); + assertTrue("equal, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Class + other = new OtherExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + assertFalse("class, equals()", emf.equals(other)); + assertTrue("class, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + + // Different pattern + other = new ExtendedMessageFormat("X" + pattern, Locale.US, fmtRegistry); + assertFalse("pattern, equals()", emf.equals(other)); + assertFalse("pattern, hashcode()", emf.hashCode() == other.hashCode()); + + // Different registry + other = new ExtendedMessageFormat(pattern, Locale.US, otherRegitry); + assertFalse("registry, equals()", emf.equals(other)); + assertFalse("registry, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Locale + other = new ExtendedMessageFormat(pattern, Locale.FRANCE, fmtRegistry); + assertFalse("locale, equals()", emf.equals(other)); + assertTrue("locale, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(final String pattern, final Object[] args, final Locale[] locales) { + checkBuiltInFormat(pattern, null, args, locales); + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param fmtRegistry FormatFactory registry to use + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(final String pattern, final Map fmtRegistry, final Object[] args, final Locale[] locales) { + checkBuiltInFormat(pattern, fmtRegistry, args, (Locale) null); + for (final Locale locale : locales) { + checkBuiltInFormat(pattern, fmtRegistry, args, locale); + } + } + + /** + * Create an ExtendedMessageFormat for the specified pattern and locale and check the + * formated output matches the expected result for the parameters. + * @param pattern string + * @param registryUnused map (currently unused) + * @param args Object[] + * @param locale Locale + */ + private void checkBuiltInFormat(final String pattern, final Map registryUnused, final Object[] args, final Locale locale) { + final StringBuilder buffer = new StringBuilder(); + buffer.append("Pattern=["); + buffer.append(pattern); + buffer.append("], locale=["); + buffer.append(locale); + buffer.append("]"); + final MessageFormat mf = createMessageFormat(pattern, locale); + // System.out.println(buffer + ", result=[" + mf.format(args) +"]"); + ExtendedMessageFormat emf = null; + if (locale == null) { + emf = new ExtendedMessageFormat(pattern); + } else { + emf = new ExtendedMessageFormat(pattern, locale); + } + assertEquals("format " + buffer.toString(), mf.format(args), emf.format(args)); + assertPatternsEqual("toPattern " + buffer.toString(), mf.toPattern(), emf.toPattern()); + } + + //can't trust what MessageFormat does with toPattern() pre 1.4: + private void assertPatternsEqual(final String message, final String expected, final String actual) { + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_4)) { + assertEquals(message, expected, actual); + } + } + + /** + * Replace MessageFormat(String, Locale) constructor (not available until JDK 1.4). + * @param pattern string + * @param locale Locale + * @return MessageFormat + */ + private MessageFormat createMessageFormat(final String pattern, final Locale locale) { + final MessageFormat result = new MessageFormat(pattern); + if (locale != null) { + result.setLocale(locale); + result.applyPattern(pattern); + } + return result; + } + + // ------------------------ Test Formats ------------------------ + + /** + * {@link Format} implementation which converts to lower case. + */ + private static class LowerCaseFormat extends Format { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { + return toAppendTo.append(((String)obj).toLowerCase()); + } + @Override + public Object parseObject(final String source, final ParsePosition pos) {throw new UnsupportedOperationException();} + } + + /** + * {@link Format} implementation which converts to upper case. + */ + private static class UpperCaseFormat extends Format { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { + return toAppendTo.append(((String)obj).toUpperCase()); + } + @Override + public Object parseObject(final String source, final ParsePosition pos) {throw new UnsupportedOperationException();} + } + + + // ------------------------ Test Format Factories --------------- + /** + * {@link FormatFactory} implementation for lower case format. + */ + private static class LowerCaseFormatFactory implements FormatFactory { + private static final Format LOWER_INSTANCE = new LowerCaseFormat(); + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return LOWER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation for upper case format. + */ + private static class UpperCaseFormatFactory implements FormatFactory { + private static final Format UPPER_INSTANCE = new UpperCaseFormat(); + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return UPPER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation to override date format "short" to "default". + */ + private static class OverrideShortDateFormatFactory implements FormatFactory { + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return !"short".equals(arguments) ? null + : locale == null ? DateFormat + .getDateInstance(DateFormat.DEFAULT) : DateFormat + .getDateInstance(DateFormat.DEFAULT, locale); + } + } + + /** + * Alternative ExtendedMessageFormat impl. + */ + private static class OtherExtendedMessageFormat extends ExtendedMessageFormat { + private static final long serialVersionUID = 1L; + + public OtherExtendedMessageFormat(final String pattern, final Locale locale, + final Map registry) { + super(pattern, locale, registry); + } + + } + +} diff --git a/Java/commons-lang-ExtendedMessageFormatTest_130/metadata.json b/Java/commons-lang-ExtendedMessageFormatTest_130/metadata.json new file mode 100644 index 000000000..e4bc252e5 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_130/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExtendedMessageFormatTest_130", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java", + "line": 133, + "npe_method": "testExtendedAndBuiltInFormats", + "deref_field": "locale", + "npe_class": "ExtendedMessageFormatTest", + "repo": "commons-lang", + "bug_id": "ExtendedMessageFormatTest_130" + } +} diff --git a/Java/commons-lang-ExtendedMessageFormatTest_130/npe.json b/Java/commons-lang-ExtendedMessageFormatTest_130/npe.json new file mode 100644 index 000000000..1c73e686f --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_130/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java", + "line": 133, + "npe_method": "testExtendedAndBuiltInFormats", + "deref_field": "locale", + "npe_class": "ExtendedMessageFormatTest" +} \ No newline at end of file diff --git a/Java/commons-lang-ExtendedMessageFormatTest_375/Dockerfile b/Java/commons-lang-ExtendedMessageFormatTest_375/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_375/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExtendedMessageFormatTest_375/buggy.java b/Java/commons-lang-ExtendedMessageFormatTest_375/buggy.java new file mode 100644 index 000000000..e00b42fbe --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_375/buggy.java @@ -0,0 +1,496 @@ +/* + * 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 org.apache.commons.lang3.text; + +import org.junit.Test; +import org.junit.Before; +import static org.junit.Assert.*; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_4; + +import java.text.ChoiceFormat; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.SystemUtils; + +/** + * Test case for {@link ExtendedMessageFormat}. + * + * @since 2.4 + */ +public class ExtendedMessageFormatTest { + + private final Map registry = new HashMap(); + + @Before + public void setUp() throws Exception { + registry.put("lower", new LowerCaseFormatFactory()); + registry.put("upper", new UpperCaseFormatFactory()); + } + + /** + * Test extended formats. + */ + @Test + public void testExtendedFormats() { + final String pattern = "Lower: {0,lower} Upper: {1,upper}"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertPatternsEqual("TOPATTERN", pattern, emf.toPattern()); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"Foo", "Bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "BAR"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "BAR"})); + } + + /** + * Test Bug LANG-477 - out of memory error with escaped quote + */ + @Test + public void testEscapedQuote_LANG_477() { + final String pattern = "it''s a {0,lower} 'test'!"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("it's a dummy test!", emf.format(new Object[] {"DUMMY"})); + } + + /** + * Test Bug LANG-917 - IndexOutOfBoundsException and/or infinite loop when using a choice pattern + */ + @Test + public void testEmbeddedPatternInChoice() { + final String pattern = "Hi {0,lower}, got {1,choice,0#none|1#one|1<{1,number}}, {2,upper}!"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals(emf.format(new Object[] {"there", 3, "great"}), "Hi there, got 3, GREAT!"); + } + + /** + * Test Bug LANG-948 - Exception while using ExtendedMessageFormat and escaping braces + */ + @Test + public void testEscapedBraces_LANG_948() { + // message without placeholder because braces are escaped by quotes + final String pattern = "Message without placeholders '{}'"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("Message without placeholders {}", emf.format(new Object[] {"DUMMY"})); + + // message with placeholder because quotes are escaped by quotes + final String pattern2 = "Message with placeholder ''{0}''"; + final ExtendedMessageFormat emf2 = new ExtendedMessageFormat(pattern2, registry); + assertEquals("Message with placeholder 'DUMMY'", emf2.format(new Object[] {"DUMMY"})); + } + + /** + * Test extended and built in formats. + */ + @Test + public void testExtendedAndBuiltInFormats() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + final Object[] args = new Object[] {"John Doe", cal.getTime(), Double.valueOf("12345.67")}; + final String builtinsPattern = "DOB: {1,date,short} Salary: {2,number,currency}"; + final String extendedPattern = "Name: {0,upper} "; + final String pattern = extendedPattern + builtinsPattern; + + final HashSet testLocales = new HashSet(); + testLocales.addAll(Arrays.asList(DateFormat.getAvailableLocales())); + testLocales.retainAll(Arrays.asList(NumberFormat.getAvailableLocales())); + testLocales.add(null); + + for (final Locale locale : testLocales) { + final MessageFormat builtins = createMessageFormat(builtinsPattern, locale); + final String expectedPattern = extendedPattern + builtins.toPattern(); + DateFormat df = null; + NumberFormat nf = null; + ExtendedMessageFormat emf = null; + if (locale == null) { + df = DateFormat.getDateInstance(DateFormat.SHORT); + nf = NumberFormat.getCurrencyInstance(); + emf = new ExtendedMessageFormat(pattern, registry); + } else { + df = DateFormat.getDateInstance(DateFormat.SHORT, locale); + nf = NumberFormat.getCurrencyInstance(locale); + emf = new ExtendedMessageFormat(pattern, locale, registry); + } + final StringBuilder expected = new StringBuilder(); + expected.append("Name: "); + expected.append(args[0].toString().toUpperCase()); + expected.append(" DOB: "); + expected.append(df.format(args[1])); + expected.append(" Salary: "); + expected.append(nf.format(args[2])); + assertPatternsEqual("pattern comparison for locale " + locale, expectedPattern, emf.toPattern()); + assertEquals(String.valueOf(locale), expected.toString(), emf.format(args)); + } + } + +// /** +// * Test extended formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{1,lower}|2.0#{1,upper}}"; +// ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// assertEquals("one", emf.format(new Object[] {Integer.valueOf(1), "ONE"})); +// assertEquals("TWO", emf.format(new Object[] {Integer.valueOf(2), "two"})); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } + +// /** +// * Test mixed extended and built-in formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedAndBuiltInWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{0} {1,lower} {2,number}|2.0#{0} {1,upper} {2,number,currency}}"; +// Object[] lowArgs = new Object[] {Integer.valueOf(1), "Low", Double.valueOf("1234.56")}; +// Object[] highArgs = new Object[] {Integer.valueOf(2), "High", Double.valueOf("9876.54")}; +// Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); +// Locale[] testLocales = new Locale[availableLocales.length + 1]; +// testLocales[0] = null; +// System.arraycopy(availableLocales, 0, testLocales, 1, availableLocales.length); +// for (int i = 0; i < testLocales.length; i++) { +// NumberFormat nf = null; +// NumberFormat cf = null; +// ExtendedMessageFormat emf = null; +// if (testLocales[i] == null) { +// nf = NumberFormat.getNumberInstance(); +// cf = NumberFormat.getCurrencyInstance(); +// emf = new ExtendedMessageFormat(pattern, registry); +// } else { +// nf = NumberFormat.getNumberInstance(testLocales[i]); +// cf = NumberFormat.getCurrencyInstance(testLocales[i]); +// emf = new ExtendedMessageFormat(pattern, testLocales[i], registry); +// } +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// String lowExpected = lowArgs[0] + " low " + nf.format(lowArgs[2]); +// String highExpected = highArgs[0] + " HIGH " + cf.format(highArgs[2]); +// assertEquals(lowExpected, emf.format(lowArgs)); +// assertEquals(highExpected, emf.format(highArgs)); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } +// } + + /** + * Test the built in choice format. + */ + @Test + public void testBuiltInChoiceFormat() { + final Object[] values = new Number[] {Integer.valueOf(1), Double.valueOf("2.2"), Double.valueOf("1234.5")}; + String choicePattern = null; + final Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); + + choicePattern = "{0,choice,1#One|2#Two|3#Many {0,number}}"; + for (final Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + + choicePattern = "{0,choice,1#''One''|2#\"Two\"|3#''{Many}'' {0,number}}"; + for (final Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + } + + /** + * Test the built in date/time formats + */ + @Test + public void testBuiltInDateTimeFormat() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + final Object[] args = new Object[] {cal.getTime()}; + final Locale[] availableLocales = DateFormat.getAvailableLocales(); + + checkBuiltInFormat("1: {0,date,short}", args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", args, availableLocales); + checkBuiltInFormat("6: {0,time,short}", args, availableLocales); + checkBuiltInFormat("7: {0,time,medium}", args, availableLocales); + checkBuiltInFormat("8: {0,time,long}", args, availableLocales); + checkBuiltInFormat("9: {0,time,full}", args, availableLocales); + checkBuiltInFormat("10: {0,time,HH:mm}", args, availableLocales); + checkBuiltInFormat("11: {0,date}", args, availableLocales); + checkBuiltInFormat("12: {0,time}", args, availableLocales); + } + + @Test + public void testOverriddenBuiltinFormat() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23); + final Object[] args = new Object[] {cal.getTime()}; + final Locale[] availableLocales = DateFormat.getAvailableLocales(); + final Map dateRegistry = Collections.singletonMap("date", new OverrideShortDateFormatFactory()); + + //check the non-overridden builtins: + checkBuiltInFormat("1: {0,date}", dateRegistry, args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", dateRegistry, args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", dateRegistry, args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", dateRegistry, args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", dateRegistry, args, availableLocales); + + //check the overridden format: + for (int i = -1; i < availableLocales.length; i++) { + final Locale locale = i < 0 ? null : availableLocales[i]; + final MessageFormat dateDefault = createMessageFormat("{0,date}", locale); + final String pattern = "{0,date,short}"; + final ExtendedMessageFormat dateShort = new ExtendedMessageFormat(pattern, locale, dateRegistry); + assertEquals("overridden date,short format", dateDefault.format(args), dateShort.format(args)); + assertEquals("overridden date,short pattern", pattern, dateShort.toPattern()); + } + } + + /** + * Test the built in number formats. + */ + @Test + public void testBuiltInNumberFormat() { + final Object[] args = new Object[] {Double.valueOf("6543.21")}; + final Locale[] availableLocales = NumberFormat.getAvailableLocales(); + checkBuiltInFormat("1: {0,number}", args, availableLocales); + checkBuiltInFormat("2: {0,number,integer}", args, availableLocales); + checkBuiltInFormat("3: {0,number,currency}", args, availableLocales); + checkBuiltInFormat("4: {0,number,percent}", args, availableLocales); + checkBuiltInFormat("5: {0,number,00000.000}", args, availableLocales); + } + + /** + * Test equals() and hashcode. + */ + @Test + public void testEqualsHashcode() { + final Map fmtRegistry = Collections.singletonMap("testfmt", new LowerCaseFormatFactory()); + final Map otherRegitry = Collections.singletonMap("testfmt", new UpperCaseFormatFactory()); + + final String pattern = "Pattern: {0,testfmt}"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + + ExtendedMessageFormat other = null; + + // Same object + assertTrue("same, equals()", emf.equals(emf)); + assertTrue("same, hashcode()", emf.hashCode() == emf.hashCode()); + + // Equal Object + other = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + assertTrue("equal, equals()", emf.equals(other)); + assertTrue("equal, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Class + other = new OtherExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + assertFalse("class, equals()", emf.equals(other)); + assertTrue("class, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + + // Different pattern + other = new ExtendedMessageFormat("X" + pattern, Locale.US, fmtRegistry); + assertFalse("pattern, equals()", emf.equals(other)); + assertFalse("pattern, hashcode()", emf.hashCode() == other.hashCode()); + + // Different registry + other = new ExtendedMessageFormat(pattern, Locale.US, otherRegitry); + assertFalse("registry, equals()", emf.equals(other)); + assertFalse("registry, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Locale + other = new ExtendedMessageFormat(pattern, Locale.FRANCE, fmtRegistry); + assertFalse("locale, equals()", emf.equals(other)); + assertTrue("locale, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(final String pattern, final Object[] args, final Locale[] locales) { + checkBuiltInFormat(pattern, null, args, locales); + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param fmtRegistry FormatFactory registry to use + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(final String pattern, final Map fmtRegistry, final Object[] args, final Locale[] locales) { + checkBuiltInFormat(pattern, fmtRegistry, args, (Locale) null); + for (final Locale locale : locales) { + checkBuiltInFormat(pattern, fmtRegistry, args, locale); + } + } + + /** + * Create an ExtendedMessageFormat for the specified pattern and locale and check the + * formated output matches the expected result for the parameters. + * @param pattern string + * @param registryUnused map (currently unused) + * @param args Object[] + * @param locale Locale + */ +/** + * Create an ExtendedMessageFormat for the specified pattern and locale and check the + * formated output matches the expected result for the parameters. + * + * @param pattern + * string + * @param registryUnused + * map (currently unused) + * @param args + * Object[] + * @param locale + * Locale + */ +private void checkBuiltInFormat(final java.lang.String pattern, final java.util.Map registryUnused, final java.lang.Object[] args, final java.util.Locale locale) { + final java.lang.StringBuilder buffer = new java.lang.StringBuilder(); + buffer.append("Pattern=["); + buffer.append(pattern); + buffer.append("], locale=["); + buffer.append(locale); + buffer.append("]"); + final java.text.MessageFormat mf = createMessageFormat(pattern, locale); + // System.out.println(buffer + ", result=[" + mf.format(args) +"]"); + org.apache.commons.lang3.text.ExtendedMessageFormat emf = null; + { + emf = new org.apache.commons.lang3.text.ExtendedMessageFormat(pattern, /* NPEX_NULL_EXP */ + locale); + } + assertEquals("format " + buffer.toString(), mf.format(args), emf.format(args)); + assertPatternsEqual("toPattern " + buffer.toString(), mf.toPattern(), emf.toPattern()); +} + + //can't trust what MessageFormat does with toPattern() pre 1.4: + private void assertPatternsEqual(final String message, final String expected, final String actual) { + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_4)) { + assertEquals(message, expected, actual); + } + } + + /** + * Replace MessageFormat(String, Locale) constructor (not available until JDK 1.4). + * @param pattern string + * @param locale Locale + * @return MessageFormat + */ + private MessageFormat createMessageFormat(final String pattern, final Locale locale) { + final MessageFormat result = new MessageFormat(pattern); + if (locale != null) { + result.setLocale(locale); + result.applyPattern(pattern); + } + return result; + } + + // ------------------------ Test Formats ------------------------ + + /** + * {@link Format} implementation which converts to lower case. + */ + private static class LowerCaseFormat extends Format { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { + return toAppendTo.append(((String)obj).toLowerCase()); + } + @Override + public Object parseObject(final String source, final ParsePosition pos) {throw new UnsupportedOperationException();} + } + + /** + * {@link Format} implementation which converts to upper case. + */ + private static class UpperCaseFormat extends Format { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { + return toAppendTo.append(((String)obj).toUpperCase()); + } + @Override + public Object parseObject(final String source, final ParsePosition pos) {throw new UnsupportedOperationException();} + } + + + // ------------------------ Test Format Factories --------------- + /** + * {@link FormatFactory} implementation for lower case format. + */ + private static class LowerCaseFormatFactory implements FormatFactory { + private static final Format LOWER_INSTANCE = new LowerCaseFormat(); + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return LOWER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation for upper case format. + */ + private static class UpperCaseFormatFactory implements FormatFactory { + private static final Format UPPER_INSTANCE = new UpperCaseFormat(); + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return UPPER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation to override date format "short" to "default". + */ + private static class OverrideShortDateFormatFactory implements FormatFactory { + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return !"short".equals(arguments) ? null + : locale == null ? DateFormat + .getDateInstance(DateFormat.DEFAULT) : DateFormat + .getDateInstance(DateFormat.DEFAULT, locale); + } + } + + /** + * Alternative ExtendedMessageFormat impl. + */ + private static class OtherExtendedMessageFormat extends ExtendedMessageFormat { + private static final long serialVersionUID = 1L; + + public OtherExtendedMessageFormat(final String pattern, final Locale locale, + final Map registry) { + super(pattern, locale, registry); + } + + } + +} diff --git a/Java/commons-lang-ExtendedMessageFormatTest_375/metadata.json b/Java/commons-lang-ExtendedMessageFormatTest_375/metadata.json new file mode 100644 index 000000000..52441ba21 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_375/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExtendedMessageFormatTest_375", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java", + "line": 390, + "npe_method": "checkBuiltInFormat", + "deref_field": "locale", + "npe_class": "ExtendedMessageFormatTest", + "repo": "commons-lang", + "bug_id": "ExtendedMessageFormatTest_375" + } +} diff --git a/Java/commons-lang-ExtendedMessageFormatTest_375/npe.json b/Java/commons-lang-ExtendedMessageFormatTest_375/npe.json new file mode 100644 index 000000000..91e739891 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_375/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java", + "line": 390, + "npe_method": "checkBuiltInFormat", + "deref_field": "locale", + "npe_class": "ExtendedMessageFormatTest" +} \ No newline at end of file diff --git a/Java/commons-lang-ExtendedMessageFormatTest_399/Dockerfile b/Java/commons-lang-ExtendedMessageFormatTest_399/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_399/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExtendedMessageFormatTest_399/buggy.java b/Java/commons-lang-ExtendedMessageFormatTest_399/buggy.java new file mode 100644 index 000000000..89b95f3ec --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_399/buggy.java @@ -0,0 +1,494 @@ +/* + * 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 org.apache.commons.lang3.text; + +import org.junit.Test; +import org.junit.Before; +import static org.junit.Assert.*; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_4; + +import java.text.ChoiceFormat; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.SystemUtils; + +/** + * Test case for {@link ExtendedMessageFormat}. + * + * @since 2.4 + */ +public class ExtendedMessageFormatTest { + + private final Map registry = new HashMap(); + + @Before + public void setUp() throws Exception { + registry.put("lower", new LowerCaseFormatFactory()); + registry.put("upper", new UpperCaseFormatFactory()); + } + + /** + * Test extended formats. + */ + @Test + public void testExtendedFormats() { + final String pattern = "Lower: {0,lower} Upper: {1,upper}"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertPatternsEqual("TOPATTERN", pattern, emf.toPattern()); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"Foo", "Bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "BAR"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "BAR"})); + } + + /** + * Test Bug LANG-477 - out of memory error with escaped quote + */ + @Test + public void testEscapedQuote_LANG_477() { + final String pattern = "it''s a {0,lower} 'test'!"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("it's a dummy test!", emf.format(new Object[] {"DUMMY"})); + } + + /** + * Test Bug LANG-917 - IndexOutOfBoundsException and/or infinite loop when using a choice pattern + */ + @Test + public void testEmbeddedPatternInChoice() { + final String pattern = "Hi {0,lower}, got {1,choice,0#none|1#one|1<{1,number}}, {2,upper}!"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals(emf.format(new Object[] {"there", 3, "great"}), "Hi there, got 3, GREAT!"); + } + + /** + * Test Bug LANG-948 - Exception while using ExtendedMessageFormat and escaping braces + */ + @Test + public void testEscapedBraces_LANG_948() { + // message without placeholder because braces are escaped by quotes + final String pattern = "Message without placeholders '{}'"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("Message without placeholders {}", emf.format(new Object[] {"DUMMY"})); + + // message with placeholder because quotes are escaped by quotes + final String pattern2 = "Message with placeholder ''{0}''"; + final ExtendedMessageFormat emf2 = new ExtendedMessageFormat(pattern2, registry); + assertEquals("Message with placeholder 'DUMMY'", emf2.format(new Object[] {"DUMMY"})); + } + + /** + * Test extended and built in formats. + */ + @Test + public void testExtendedAndBuiltInFormats() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + final Object[] args = new Object[] {"John Doe", cal.getTime(), Double.valueOf("12345.67")}; + final String builtinsPattern = "DOB: {1,date,short} Salary: {2,number,currency}"; + final String extendedPattern = "Name: {0,upper} "; + final String pattern = extendedPattern + builtinsPattern; + + final HashSet testLocales = new HashSet(); + testLocales.addAll(Arrays.asList(DateFormat.getAvailableLocales())); + testLocales.retainAll(Arrays.asList(NumberFormat.getAvailableLocales())); + testLocales.add(null); + + for (final Locale locale : testLocales) { + final MessageFormat builtins = createMessageFormat(builtinsPattern, locale); + final String expectedPattern = extendedPattern + builtins.toPattern(); + DateFormat df = null; + NumberFormat nf = null; + ExtendedMessageFormat emf = null; + if (locale == null) { + df = DateFormat.getDateInstance(DateFormat.SHORT); + nf = NumberFormat.getCurrencyInstance(); + emf = new ExtendedMessageFormat(pattern, registry); + } else { + df = DateFormat.getDateInstance(DateFormat.SHORT, locale); + nf = NumberFormat.getCurrencyInstance(locale); + emf = new ExtendedMessageFormat(pattern, locale, registry); + } + final StringBuilder expected = new StringBuilder(); + expected.append("Name: "); + expected.append(args[0].toString().toUpperCase()); + expected.append(" DOB: "); + expected.append(df.format(args[1])); + expected.append(" Salary: "); + expected.append(nf.format(args[2])); + assertPatternsEqual("pattern comparison for locale " + locale, expectedPattern, emf.toPattern()); + assertEquals(String.valueOf(locale), expected.toString(), emf.format(args)); + } + } + +// /** +// * Test extended formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{1,lower}|2.0#{1,upper}}"; +// ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// assertEquals("one", emf.format(new Object[] {Integer.valueOf(1), "ONE"})); +// assertEquals("TWO", emf.format(new Object[] {Integer.valueOf(2), "two"})); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } + +// /** +// * Test mixed extended and built-in formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedAndBuiltInWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{0} {1,lower} {2,number}|2.0#{0} {1,upper} {2,number,currency}}"; +// Object[] lowArgs = new Object[] {Integer.valueOf(1), "Low", Double.valueOf("1234.56")}; +// Object[] highArgs = new Object[] {Integer.valueOf(2), "High", Double.valueOf("9876.54")}; +// Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); +// Locale[] testLocales = new Locale[availableLocales.length + 1]; +// testLocales[0] = null; +// System.arraycopy(availableLocales, 0, testLocales, 1, availableLocales.length); +// for (int i = 0; i < testLocales.length; i++) { +// NumberFormat nf = null; +// NumberFormat cf = null; +// ExtendedMessageFormat emf = null; +// if (testLocales[i] == null) { +// nf = NumberFormat.getNumberInstance(); +// cf = NumberFormat.getCurrencyInstance(); +// emf = new ExtendedMessageFormat(pattern, registry); +// } else { +// nf = NumberFormat.getNumberInstance(testLocales[i]); +// cf = NumberFormat.getCurrencyInstance(testLocales[i]); +// emf = new ExtendedMessageFormat(pattern, testLocales[i], registry); +// } +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// String lowExpected = lowArgs[0] + " low " + nf.format(lowArgs[2]); +// String highExpected = highArgs[0] + " HIGH " + cf.format(highArgs[2]); +// assertEquals(lowExpected, emf.format(lowArgs)); +// assertEquals(highExpected, emf.format(highArgs)); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } +// } + + /** + * Test the built in choice format. + */ + @Test + public void testBuiltInChoiceFormat() { + final Object[] values = new Number[] {Integer.valueOf(1), Double.valueOf("2.2"), Double.valueOf("1234.5")}; + String choicePattern = null; + final Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); + + choicePattern = "{0,choice,1#One|2#Two|3#Many {0,number}}"; + for (final Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + + choicePattern = "{0,choice,1#''One''|2#\"Two\"|3#''{Many}'' {0,number}}"; + for (final Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + } + + /** + * Test the built in date/time formats + */ + @Test + public void testBuiltInDateTimeFormat() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + final Object[] args = new Object[] {cal.getTime()}; + final Locale[] availableLocales = DateFormat.getAvailableLocales(); + + checkBuiltInFormat("1: {0,date,short}", args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", args, availableLocales); + checkBuiltInFormat("6: {0,time,short}", args, availableLocales); + checkBuiltInFormat("7: {0,time,medium}", args, availableLocales); + checkBuiltInFormat("8: {0,time,long}", args, availableLocales); + checkBuiltInFormat("9: {0,time,full}", args, availableLocales); + checkBuiltInFormat("10: {0,time,HH:mm}", args, availableLocales); + checkBuiltInFormat("11: {0,date}", args, availableLocales); + checkBuiltInFormat("12: {0,time}", args, availableLocales); + } + + @Test + public void testOverriddenBuiltinFormat() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23); + final Object[] args = new Object[] {cal.getTime()}; + final Locale[] availableLocales = DateFormat.getAvailableLocales(); + final Map dateRegistry = Collections.singletonMap("date", new OverrideShortDateFormatFactory()); + + //check the non-overridden builtins: + checkBuiltInFormat("1: {0,date}", dateRegistry, args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", dateRegistry, args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", dateRegistry, args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", dateRegistry, args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", dateRegistry, args, availableLocales); + + //check the overridden format: + for (int i = -1; i < availableLocales.length; i++) { + final Locale locale = i < 0 ? null : availableLocales[i]; + final MessageFormat dateDefault = createMessageFormat("{0,date}", locale); + final String pattern = "{0,date,short}"; + final ExtendedMessageFormat dateShort = new ExtendedMessageFormat(pattern, locale, dateRegistry); + assertEquals("overridden date,short format", dateDefault.format(args), dateShort.format(args)); + assertEquals("overridden date,short pattern", pattern, dateShort.toPattern()); + } + } + + /** + * Test the built in number formats. + */ + @Test + public void testBuiltInNumberFormat() { + final Object[] args = new Object[] {Double.valueOf("6543.21")}; + final Locale[] availableLocales = NumberFormat.getAvailableLocales(); + checkBuiltInFormat("1: {0,number}", args, availableLocales); + checkBuiltInFormat("2: {0,number,integer}", args, availableLocales); + checkBuiltInFormat("3: {0,number,currency}", args, availableLocales); + checkBuiltInFormat("4: {0,number,percent}", args, availableLocales); + checkBuiltInFormat("5: {0,number,00000.000}", args, availableLocales); + } + + /** + * Test equals() and hashcode. + */ + @Test + public void testEqualsHashcode() { + final Map fmtRegistry = Collections.singletonMap("testfmt", new LowerCaseFormatFactory()); + final Map otherRegitry = Collections.singletonMap("testfmt", new UpperCaseFormatFactory()); + + final String pattern = "Pattern: {0,testfmt}"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + + ExtendedMessageFormat other = null; + + // Same object + assertTrue("same, equals()", emf.equals(emf)); + assertTrue("same, hashcode()", emf.hashCode() == emf.hashCode()); + + // Equal Object + other = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + assertTrue("equal, equals()", emf.equals(other)); + assertTrue("equal, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Class + other = new OtherExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + assertFalse("class, equals()", emf.equals(other)); + assertTrue("class, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + + // Different pattern + other = new ExtendedMessageFormat("X" + pattern, Locale.US, fmtRegistry); + assertFalse("pattern, equals()", emf.equals(other)); + assertFalse("pattern, hashcode()", emf.hashCode() == other.hashCode()); + + // Different registry + other = new ExtendedMessageFormat(pattern, Locale.US, otherRegitry); + assertFalse("registry, equals()", emf.equals(other)); + assertFalse("registry, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Locale + other = new ExtendedMessageFormat(pattern, Locale.FRANCE, fmtRegistry); + assertFalse("locale, equals()", emf.equals(other)); + assertTrue("locale, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(final String pattern, final Object[] args, final Locale[] locales) { + checkBuiltInFormat(pattern, null, args, locales); + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param fmtRegistry FormatFactory registry to use + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(final String pattern, final Map fmtRegistry, final Object[] args, final Locale[] locales) { + checkBuiltInFormat(pattern, fmtRegistry, args, (Locale) null); + for (final Locale locale : locales) { + checkBuiltInFormat(pattern, fmtRegistry, args, locale); + } + } + + /** + * Create an ExtendedMessageFormat for the specified pattern and locale and check the + * formated output matches the expected result for the parameters. + * @param pattern string + * @param registryUnused map (currently unused) + * @param args Object[] + * @param locale Locale + */ + private void checkBuiltInFormat(final String pattern, final Map registryUnused, final Object[] args, final Locale locale) { + final StringBuilder buffer = new StringBuilder(); + buffer.append("Pattern=["); + buffer.append(pattern); + buffer.append("], locale=["); + buffer.append(locale); + buffer.append("]"); + final MessageFormat mf = createMessageFormat(pattern, locale); + // System.out.println(buffer + ", result=[" + mf.format(args) +"]"); + ExtendedMessageFormat emf = null; + if (locale == null) { + emf = new ExtendedMessageFormat(pattern); + } else { + emf = new ExtendedMessageFormat(pattern, locale); + } + assertEquals("format " + buffer.toString(), mf.format(args), emf.format(args)); + assertPatternsEqual("toPattern " + buffer.toString(), mf.toPattern(), emf.toPattern()); + } + + //can't trust what MessageFormat does with toPattern() pre 1.4: + private void assertPatternsEqual(final String message, final String expected, final String actual) { + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_4)) { + assertEquals(message, expected, actual); + } + } + + /** + * Replace MessageFormat(String, Locale) constructor (not available until JDK 1.4). + * @param pattern string + * @param locale Locale + * @return MessageFormat + */ +/** + * Replace MessageFormat(String, Locale) constructor (not available until JDK 1.4). + * + * @param pattern + * string + * @param locale + * Locale + * @return MessageFormat + */ +private java.text.MessageFormat createMessageFormat(final java.lang.String pattern, final java.util.Locale locale) { + final java.text.MessageFormat result = new java.text.MessageFormat(pattern); + { + result.setLocale(/* NPEX_NULL_EXP */ + locale); + result.applyPattern(pattern); + } + return result; +} + + // ------------------------ Test Formats ------------------------ + + /** + * {@link Format} implementation which converts to lower case. + */ + private static class LowerCaseFormat extends Format { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { + return toAppendTo.append(((String)obj).toLowerCase()); + } + @Override + public Object parseObject(final String source, final ParsePosition pos) {throw new UnsupportedOperationException();} + } + + /** + * {@link Format} implementation which converts to upper case. + */ + private static class UpperCaseFormat extends Format { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { + return toAppendTo.append(((String)obj).toUpperCase()); + } + @Override + public Object parseObject(final String source, final ParsePosition pos) {throw new UnsupportedOperationException();} + } + + + // ------------------------ Test Format Factories --------------- + /** + * {@link FormatFactory} implementation for lower case format. + */ + private static class LowerCaseFormatFactory implements FormatFactory { + private static final Format LOWER_INSTANCE = new LowerCaseFormat(); + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return LOWER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation for upper case format. + */ + private static class UpperCaseFormatFactory implements FormatFactory { + private static final Format UPPER_INSTANCE = new UpperCaseFormat(); + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return UPPER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation to override date format "short" to "default". + */ + private static class OverrideShortDateFormatFactory implements FormatFactory { + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return !"short".equals(arguments) ? null + : locale == null ? DateFormat + .getDateInstance(DateFormat.DEFAULT) : DateFormat + .getDateInstance(DateFormat.DEFAULT, locale); + } + } + + /** + * Alternative ExtendedMessageFormat impl. + */ + private static class OtherExtendedMessageFormat extends ExtendedMessageFormat { + private static final long serialVersionUID = 1L; + + public OtherExtendedMessageFormat(final String pattern, final Locale locale, + final Map registry) { + super(pattern, locale, registry); + } + + } + +} diff --git a/Java/commons-lang-ExtendedMessageFormatTest_399/metadata.json b/Java/commons-lang-ExtendedMessageFormatTest_399/metadata.json new file mode 100644 index 000000000..e72918513 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_399/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExtendedMessageFormatTest_399", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java", + "line": 410, + "npe_method": "createMessageFormat", + "deref_field": "locale", + "npe_class": "ExtendedMessageFormatTest", + "repo": "commons-lang", + "bug_id": "ExtendedMessageFormatTest_399" + } +} diff --git a/Java/commons-lang-ExtendedMessageFormatTest_399/npe.json b/Java/commons-lang-ExtendedMessageFormatTest_399/npe.json new file mode 100644 index 000000000..a0f1f21bf --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_399/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java", + "line": 410, + "npe_method": "createMessageFormat", + "deref_field": "locale", + "npe_class": "ExtendedMessageFormatTest" +} \ No newline at end of file diff --git a/Java/commons-lang-ExtendedMessageFormatTest_464/Dockerfile b/Java/commons-lang-ExtendedMessageFormatTest_464/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_464/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExtendedMessageFormatTest_464/buggy.java b/Java/commons-lang-ExtendedMessageFormatTest_464/buggy.java new file mode 100644 index 000000000..33997f284 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_464/buggy.java @@ -0,0 +1,482 @@ +/* + * 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 org.apache.commons.lang3.text; + +import org.junit.Test; +import org.junit.Before; +import static org.junit.Assert.*; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_4; + +import java.text.ChoiceFormat; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.SystemUtils; + +/** + * Test case for {@link ExtendedMessageFormat}. + * + * @since 2.4 + */ +public class ExtendedMessageFormatTest { + + private final Map registry = new HashMap(); + + @Before + public void setUp() throws Exception { + registry.put("lower", new LowerCaseFormatFactory()); + registry.put("upper", new UpperCaseFormatFactory()); + } + + /** + * Test extended formats. + */ + @Test + public void testExtendedFormats() { + final String pattern = "Lower: {0,lower} Upper: {1,upper}"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertPatternsEqual("TOPATTERN", pattern, emf.toPattern()); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"Foo", "Bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "BAR"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "BAR"})); + } + + /** + * Test Bug LANG-477 - out of memory error with escaped quote + */ + @Test + public void testEscapedQuote_LANG_477() { + final String pattern = "it''s a {0,lower} 'test'!"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("it's a dummy test!", emf.format(new Object[] {"DUMMY"})); + } + + /** + * Test Bug LANG-917 - IndexOutOfBoundsException and/or infinite loop when using a choice pattern + */ + @Test + public void testEmbeddedPatternInChoice() { + final String pattern = "Hi {0,lower}, got {1,choice,0#none|1#one|1<{1,number}}, {2,upper}!"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals(emf.format(new Object[] {"there", 3, "great"}), "Hi there, got 3, GREAT!"); + } + + /** + * Test Bug LANG-948 - Exception while using ExtendedMessageFormat and escaping braces + */ + @Test + public void testEscapedBraces_LANG_948() { + // message without placeholder because braces are escaped by quotes + final String pattern = "Message without placeholders '{}'"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("Message without placeholders {}", emf.format(new Object[] {"DUMMY"})); + + // message with placeholder because quotes are escaped by quotes + final String pattern2 = "Message with placeholder ''{0}''"; + final ExtendedMessageFormat emf2 = new ExtendedMessageFormat(pattern2, registry); + assertEquals("Message with placeholder 'DUMMY'", emf2.format(new Object[] {"DUMMY"})); + } + + /** + * Test extended and built in formats. + */ + @Test + public void testExtendedAndBuiltInFormats() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + final Object[] args = new Object[] {"John Doe", cal.getTime(), Double.valueOf("12345.67")}; + final String builtinsPattern = "DOB: {1,date,short} Salary: {2,number,currency}"; + final String extendedPattern = "Name: {0,upper} "; + final String pattern = extendedPattern + builtinsPattern; + + final HashSet testLocales = new HashSet(); + testLocales.addAll(Arrays.asList(DateFormat.getAvailableLocales())); + testLocales.retainAll(Arrays.asList(NumberFormat.getAvailableLocales())); + testLocales.add(null); + + for (final Locale locale : testLocales) { + final MessageFormat builtins = createMessageFormat(builtinsPattern, locale); + final String expectedPattern = extendedPattern + builtins.toPattern(); + DateFormat df = null; + NumberFormat nf = null; + ExtendedMessageFormat emf = null; + if (locale == null) { + df = DateFormat.getDateInstance(DateFormat.SHORT); + nf = NumberFormat.getCurrencyInstance(); + emf = new ExtendedMessageFormat(pattern, registry); + } else { + df = DateFormat.getDateInstance(DateFormat.SHORT, locale); + nf = NumberFormat.getCurrencyInstance(locale); + emf = new ExtendedMessageFormat(pattern, locale, registry); + } + final StringBuilder expected = new StringBuilder(); + expected.append("Name: "); + expected.append(args[0].toString().toUpperCase()); + expected.append(" DOB: "); + expected.append(df.format(args[1])); + expected.append(" Salary: "); + expected.append(nf.format(args[2])); + assertPatternsEqual("pattern comparison for locale " + locale, expectedPattern, emf.toPattern()); + assertEquals(String.valueOf(locale), expected.toString(), emf.format(args)); + } + } + +// /** +// * Test extended formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{1,lower}|2.0#{1,upper}}"; +// ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// assertEquals("one", emf.format(new Object[] {Integer.valueOf(1), "ONE"})); +// assertEquals("TWO", emf.format(new Object[] {Integer.valueOf(2), "two"})); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } + +// /** +// * Test mixed extended and built-in formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedAndBuiltInWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{0} {1,lower} {2,number}|2.0#{0} {1,upper} {2,number,currency}}"; +// Object[] lowArgs = new Object[] {Integer.valueOf(1), "Low", Double.valueOf("1234.56")}; +// Object[] highArgs = new Object[] {Integer.valueOf(2), "High", Double.valueOf("9876.54")}; +// Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); +// Locale[] testLocales = new Locale[availableLocales.length + 1]; +// testLocales[0] = null; +// System.arraycopy(availableLocales, 0, testLocales, 1, availableLocales.length); +// for (int i = 0; i < testLocales.length; i++) { +// NumberFormat nf = null; +// NumberFormat cf = null; +// ExtendedMessageFormat emf = null; +// if (testLocales[i] == null) { +// nf = NumberFormat.getNumberInstance(); +// cf = NumberFormat.getCurrencyInstance(); +// emf = new ExtendedMessageFormat(pattern, registry); +// } else { +// nf = NumberFormat.getNumberInstance(testLocales[i]); +// cf = NumberFormat.getCurrencyInstance(testLocales[i]); +// emf = new ExtendedMessageFormat(pattern, testLocales[i], registry); +// } +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// String lowExpected = lowArgs[0] + " low " + nf.format(lowArgs[2]); +// String highExpected = highArgs[0] + " HIGH " + cf.format(highArgs[2]); +// assertEquals(lowExpected, emf.format(lowArgs)); +// assertEquals(highExpected, emf.format(highArgs)); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } +// } + + /** + * Test the built in choice format. + */ + @Test + public void testBuiltInChoiceFormat() { + final Object[] values = new Number[] {Integer.valueOf(1), Double.valueOf("2.2"), Double.valueOf("1234.5")}; + String choicePattern = null; + final Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); + + choicePattern = "{0,choice,1#One|2#Two|3#Many {0,number}}"; + for (final Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + + choicePattern = "{0,choice,1#''One''|2#\"Two\"|3#''{Many}'' {0,number}}"; + for (final Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + } + + /** + * Test the built in date/time formats + */ + @Test + public void testBuiltInDateTimeFormat() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + final Object[] args = new Object[] {cal.getTime()}; + final Locale[] availableLocales = DateFormat.getAvailableLocales(); + + checkBuiltInFormat("1: {0,date,short}", args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", args, availableLocales); + checkBuiltInFormat("6: {0,time,short}", args, availableLocales); + checkBuiltInFormat("7: {0,time,medium}", args, availableLocales); + checkBuiltInFormat("8: {0,time,long}", args, availableLocales); + checkBuiltInFormat("9: {0,time,full}", args, availableLocales); + checkBuiltInFormat("10: {0,time,HH:mm}", args, availableLocales); + checkBuiltInFormat("11: {0,date}", args, availableLocales); + checkBuiltInFormat("12: {0,time}", args, availableLocales); + } + + @Test + public void testOverriddenBuiltinFormat() { + final Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23); + final Object[] args = new Object[] {cal.getTime()}; + final Locale[] availableLocales = DateFormat.getAvailableLocales(); + final Map dateRegistry = Collections.singletonMap("date", new OverrideShortDateFormatFactory()); + + //check the non-overridden builtins: + checkBuiltInFormat("1: {0,date}", dateRegistry, args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", dateRegistry, args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", dateRegistry, args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", dateRegistry, args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", dateRegistry, args, availableLocales); + + //check the overridden format: + for (int i = -1; i < availableLocales.length; i++) { + final Locale locale = i < 0 ? null : availableLocales[i]; + final MessageFormat dateDefault = createMessageFormat("{0,date}", locale); + final String pattern = "{0,date,short}"; + final ExtendedMessageFormat dateShort = new ExtendedMessageFormat(pattern, locale, dateRegistry); + assertEquals("overridden date,short format", dateDefault.format(args), dateShort.format(args)); + assertEquals("overridden date,short pattern", pattern, dateShort.toPattern()); + } + } + + /** + * Test the built in number formats. + */ + @Test + public void testBuiltInNumberFormat() { + final Object[] args = new Object[] {Double.valueOf("6543.21")}; + final Locale[] availableLocales = NumberFormat.getAvailableLocales(); + checkBuiltInFormat("1: {0,number}", args, availableLocales); + checkBuiltInFormat("2: {0,number,integer}", args, availableLocales); + checkBuiltInFormat("3: {0,number,currency}", args, availableLocales); + checkBuiltInFormat("4: {0,number,percent}", args, availableLocales); + checkBuiltInFormat("5: {0,number,00000.000}", args, availableLocales); + } + + /** + * Test equals() and hashcode. + */ + @Test + public void testEqualsHashcode() { + final Map fmtRegistry = Collections.singletonMap("testfmt", new LowerCaseFormatFactory()); + final Map otherRegitry = Collections.singletonMap("testfmt", new UpperCaseFormatFactory()); + + final String pattern = "Pattern: {0,testfmt}"; + final ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + + ExtendedMessageFormat other = null; + + // Same object + assertTrue("same, equals()", emf.equals(emf)); + assertTrue("same, hashcode()", emf.hashCode() == emf.hashCode()); + + // Equal Object + other = new ExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + assertTrue("equal, equals()", emf.equals(other)); + assertTrue("equal, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Class + other = new OtherExtendedMessageFormat(pattern, Locale.US, fmtRegistry); + assertFalse("class, equals()", emf.equals(other)); + assertTrue("class, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + + // Different pattern + other = new ExtendedMessageFormat("X" + pattern, Locale.US, fmtRegistry); + assertFalse("pattern, equals()", emf.equals(other)); + assertFalse("pattern, hashcode()", emf.hashCode() == other.hashCode()); + + // Different registry + other = new ExtendedMessageFormat(pattern, Locale.US, otherRegitry); + assertFalse("registry, equals()", emf.equals(other)); + assertFalse("registry, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Locale + other = new ExtendedMessageFormat(pattern, Locale.FRANCE, fmtRegistry); + assertFalse("locale, equals()", emf.equals(other)); + assertTrue("locale, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(final String pattern, final Object[] args, final Locale[] locales) { + checkBuiltInFormat(pattern, null, args, locales); + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param fmtRegistry FormatFactory registry to use + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(final String pattern, final Map fmtRegistry, final Object[] args, final Locale[] locales) { + checkBuiltInFormat(pattern, fmtRegistry, args, (Locale) null); + for (final Locale locale : locales) { + checkBuiltInFormat(pattern, fmtRegistry, args, locale); + } + } + + /** + * Create an ExtendedMessageFormat for the specified pattern and locale and check the + * formated output matches the expected result for the parameters. + * @param pattern string + * @param registryUnused map (currently unused) + * @param args Object[] + * @param locale Locale + */ + private void checkBuiltInFormat(final String pattern, final Map registryUnused, final Object[] args, final Locale locale) { + final StringBuilder buffer = new StringBuilder(); + buffer.append("Pattern=["); + buffer.append(pattern); + buffer.append("], locale=["); + buffer.append(locale); + buffer.append("]"); + final MessageFormat mf = createMessageFormat(pattern, locale); + // System.out.println(buffer + ", result=[" + mf.format(args) +"]"); + ExtendedMessageFormat emf = null; + if (locale == null) { + emf = new ExtendedMessageFormat(pattern); + } else { + emf = new ExtendedMessageFormat(pattern, locale); + } + assertEquals("format " + buffer.toString(), mf.format(args), emf.format(args)); + assertPatternsEqual("toPattern " + buffer.toString(), mf.toPattern(), emf.toPattern()); + } + + //can't trust what MessageFormat does with toPattern() pre 1.4: + private void assertPatternsEqual(final String message, final String expected, final String actual) { + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_4)) { + assertEquals(message, expected, actual); + } + } + + /** + * Replace MessageFormat(String, Locale) constructor (not available until JDK 1.4). + * @param pattern string + * @param locale Locale + * @return MessageFormat + */ + private MessageFormat createMessageFormat(final String pattern, final Locale locale) { + final MessageFormat result = new MessageFormat(pattern); + if (locale != null) { + result.setLocale(locale); + result.applyPattern(pattern); + } + return result; + } + + // ------------------------ Test Formats ------------------------ + + /** + * {@link Format} implementation which converts to lower case. + */ + private static class LowerCaseFormat extends Format { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { + return toAppendTo.append(((String)obj).toLowerCase()); + } + @Override + public Object parseObject(final String source, final ParsePosition pos) {throw new UnsupportedOperationException();} + } + + /** + * {@link Format} implementation which converts to upper case. + */ + private static class UpperCaseFormat extends Format { + private static final long serialVersionUID = 1L; + + @Override + public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) { + return toAppendTo.append(((String)obj).toUpperCase()); + } + @Override + public Object parseObject(final String source, final ParsePosition pos) {throw new UnsupportedOperationException();} + } + + + // ------------------------ Test Format Factories --------------- + /** + * {@link FormatFactory} implementation for lower case format. + */ + private static class LowerCaseFormatFactory implements FormatFactory { + private static final Format LOWER_INSTANCE = new LowerCaseFormat(); + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return LOWER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation for upper case format. + */ + private static class UpperCaseFormatFactory implements FormatFactory { + private static final Format UPPER_INSTANCE = new UpperCaseFormat(); + @Override + public Format getFormat(final String name, final String arguments, final Locale locale) { + return UPPER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation to override date format "short" to "default". + */ + private static class OverrideShortDateFormatFactory implements FormatFactory { + @Override +public java.text.Format getFormat(final java.lang.String name, final java.lang.String arguments, final java.util.Locale locale) { + return !"short".equals(arguments) ? null : java.text.DateFormat.getDateInstance(java.text.DateFormat.DEFAULT, /* NPEX_NULL_EXP */ + locale); +} + } + + /** + * Alternative ExtendedMessageFormat impl. + */ + private static class OtherExtendedMessageFormat extends ExtendedMessageFormat { + private static final long serialVersionUID = 1L; + + public OtherExtendedMessageFormat(final String pattern, final Locale locale, + final Map registry) { + super(pattern, locale, registry); + } + + } + +} diff --git a/Java/commons-lang-ExtendedMessageFormatTest_464/metadata.json b/Java/commons-lang-ExtendedMessageFormatTest_464/metadata.json new file mode 100644 index 000000000..fd13161c9 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_464/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExtendedMessageFormatTest_464", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn clean test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java", + "line": 465, + "npe_method": "getFormat", + "deref_field": "locale", + "npe_class": "OverrideShortDateFormatFactory", + "repo": "commons-lang", + "bug_id": "ExtendedMessageFormatTest_464" + } +} diff --git a/Java/commons-lang-ExtendedMessageFormatTest_464/npe.json b/Java/commons-lang-ExtendedMessageFormatTest_464/npe.json new file mode 100644 index 000000000..05cb0f3f1 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormatTest_464/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java", + "line": 465, + "npe_method": "getFormat", + "deref_field": "locale", + "npe_class": "OverrideShortDateFormatFactory" +} \ No newline at end of file diff --git a/Java/commons-lang-ExtendedMessageFormat_178/Dockerfile b/Java/commons-lang-ExtendedMessageFormat_178/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_178/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExtendedMessageFormat_178/buggy.java b/Java/commons-lang-ExtendedMessageFormat_178/buggy.java new file mode 100644 index 000000000..eda96bbee --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_178/buggy.java @@ -0,0 +1,532 @@ +/* + * 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 org.apache.commons.lang3.text; + +import java.text.Format; +import java.text.MessageFormat; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.Validate; + +/** + * Extends java.text.MessageFormat to allow pluggable/additional formatting + * options for embedded format elements. Client code should specify a registry + * of FormatFactory instances associated with String + * format names. This registry will be consulted when the format elements are + * parsed from the message pattern. In this way custom patterns can be specified, + * and the formats supported by java.text.MessageFormat can be overridden + * at the format and/or format style level (see MessageFormat). A "format element" + * embedded in the message pattern is specified (()? signifies optionality):
+ * {argument-number(,format-name + * (,format-style)?)?} + * + *

+ * format-name and format-style values are trimmed of surrounding whitespace + * in the manner of java.text.MessageFormat. If format-name denotes + * FormatFactory formatFactoryInstance in registry, a Format + * matching format-name and format-style is requested from + * formatFactoryInstance. If this is successful, the Format + * found is used for this format element. + *

+ * + *

NOTICE: The various subformat mutator methods are considered unnecessary; they exist on the parent + * class to allow the type of customization which it is the job of this class to provide in + * a configurable fashion. These methods have thus been disabled and will throw + * UnsupportedOperationException if called. + *

+ * + *

Limitations inherited from java.text.MessageFormat:

+ *
    + *
  • When using "choice" subformats, support for nested formatting instructions is limited + * to that provided by the base class.
  • + *
  • Thread-safety of Formats, including MessageFormat and thus + * ExtendedMessageFormat, is not guaranteed.
  • + *
+ * + * @since 2.4 + */ +public class ExtendedMessageFormat extends MessageFormat { + private static final long serialVersionUID = -2362048321261811743L; + private static final int HASH_SEED = 31; + + private static final String DUMMY_PATTERN = ""; + private static final char START_FMT = ','; + private static final char END_FE = '}'; + private static final char START_FE = '{'; + private static final char QUOTE = '\''; + + private String toPattern; + private final Map registry; + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern) { + this(pattern, Locale.getDefault()); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Locale locale) { + this(pattern, locale, null); + } + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Map registry) { + this(pattern, Locale.getDefault(), registry); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Locale locale, final Map registry) { + super(DUMMY_PATTERN); + setLocale(locale); + this.registry = registry; + applyPattern(pattern); + } + + /** + * {@inheritDoc} + */ + @Override + public String toPattern() { + return toPattern; + } + + /** + * Apply the specified pattern. + * + * @param pattern String + */ + @Override +/** + * Apply the specified pattern. + * + * @param pattern + * String + */ +public final void applyPattern(final java.lang.String pattern) { + if (registry == null) { + super.applyPattern(pattern); + toPattern = super.toPattern(); + return; + } + final java.util.ArrayList foundFormats = new java.util.ArrayList(); + final java.util.ArrayList foundDescriptions = new java.util.ArrayList(); + final java.lang.StringBuilder stripCustom = new java.lang.StringBuilder(pattern.length()); + final java.text.ParsePosition pos = new java.text.ParsePosition(0); + final char[] c = pattern.toCharArray(); + int fmtCount = 0; + while (pos.getIndex() < pattern.length()) { + switch (c[pos.getIndex()]) { + case org.apache.commons.lang3.text.ExtendedMessageFormat.QUOTE : + appendQuotedString(pattern, pos, stripCustom); + break; + case org.apache.commons.lang3.text.ExtendedMessageFormat.START_FE : + fmtCount++; + seekNonWs(pattern, pos); + final int start = pos.getIndex(); + final int index = readArgumentIndex(pattern, next(pos)); + stripCustom.append(org.apache.commons.lang3.text.ExtendedMessageFormat.START_FE).append(index); + seekNonWs(pattern, pos); + java.text.Format format = null; + java.lang.String formatDescription = null; + if (c[pos.getIndex()] == org.apache.commons.lang3.text.ExtendedMessageFormat.START_FMT) { + formatDescription = parseFormatDescription(pattern, next(pos)); + format = getFormat(formatDescription); + if (/* NPEX_NULL_EXP */ + format == null) { + stripCustom.append(org.apache.commons.lang3.text.ExtendedMessageFormat.START_FMT).append(formatDescription); + } + } + foundFormats.add(format); + foundDescriptions.add(formatDescription); + org.apache.commons.lang3.Validate.isTrue(foundFormats.size() == fmtCount); + org.apache.commons.lang3.Validate.isTrue(foundDescriptions.size() == fmtCount); + if (c[pos.getIndex()] != org.apache.commons.lang3.text.ExtendedMessageFormat.END_FE) { + throw new java.lang.IllegalArgumentException("Unreadable format element at position " + start); + } + // $FALL-THROUGH$ + default : + stripCustom.append(c[pos.getIndex()]); + next(pos); + } + } + super.applyPattern(stripCustom.toString()); + toPattern = insertFormats(super.toPattern(), foundDescriptions); + if (containsElements(foundFormats)) { + final java.text.Format[] origFormats = getFormats(); + // only loop over what we know we have, as MessageFormat on Java 1.3 + // seems to provide an extra format element: + int i = 0; + for (final java.util.Iterator it = foundFormats.iterator(); it.hasNext(); i++) { + final java.text.Format f = it.next(); + if (f != null) { + origFormats[i] = f; + } + } + super.setFormats(origFormats); + } +} + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param formatElementIndex format element index + * @param newFormat the new format + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormat(final int formatElementIndex, final Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param argumentIndex argument index + * @param newFormat the new format + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormatByArgumentIndex(final int argumentIndex, final Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormats(final Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormatsByArgumentIndex(final Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Check if this extended message format is equal to another object. + * + * @param obj the object to compare to + * @return true if this object equals the other, otherwise false + */ + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (obj == null) { + return false; + } + if (!super.equals(obj)) { + return false; + } + if (ObjectUtils.notEqual(getClass(), obj.getClass())) { + return false; + } + final ExtendedMessageFormat rhs = (ExtendedMessageFormat)obj; + if (ObjectUtils.notEqual(toPattern, rhs.toPattern)) { + return false; + } + if (ObjectUtils.notEqual(registry, rhs.registry)) { + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings( "deprecation" ) // ObjectUtils.hashCode(Object) has been deprecated in 3.2 + @Override + public int hashCode() { + int result = super.hashCode(); + result = HASH_SEED * result + ObjectUtils.hashCode(registry); + result = HASH_SEED * result + ObjectUtils.hashCode(toPattern); + return result; + } + + /** + * Get a custom format from a format description. + * + * @param desc String + * @return Format + */ + private Format getFormat(final String desc) { + if (registry != null) { + String name = desc; + String args = null; + final int i = desc.indexOf(START_FMT); + if (i > 0) { + name = desc.substring(0, i).trim(); + args = desc.substring(i + 1).trim(); + } + final FormatFactory factory = registry.get(name); + if (factory != null) { + return factory.getFormat(name, args, getLocale()); + } + } + return null; + } + + /** + * Read the argument index from the current format element + * + * @param pattern pattern to parse + * @param pos current parse position + * @return argument index + */ + private int readArgumentIndex(final String pattern, final ParsePosition pos) { + final int start = pos.getIndex(); + seekNonWs(pattern, pos); + final StringBuilder result = new StringBuilder(); + boolean error = false; + for (; !error && pos.getIndex() < pattern.length(); next(pos)) { + char c = pattern.charAt(pos.getIndex()); + if (Character.isWhitespace(c)) { + seekNonWs(pattern, pos); + c = pattern.charAt(pos.getIndex()); + if (c != START_FMT && c != END_FE) { + error = true; + continue; + } + } + if ((c == START_FMT || c == END_FE) && result.length() > 0) { + try { + return Integer.parseInt(result.toString()); + } catch (final NumberFormatException e) { // NOPMD + // we've already ensured only digits, so unless something + // outlandishly large was specified we should be okay. + } + } + error = !Character.isDigit(c); + result.append(c); + } + if (error) { + throw new IllegalArgumentException( + "Invalid format argument index at position " + start + ": " + + pattern.substring(start, pos.getIndex())); + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Parse the format component of a format element. + * + * @param pattern string to parse + * @param pos current parse position + * @return Format description String + */ + private String parseFormatDescription(final String pattern, final ParsePosition pos) { + final int start = pos.getIndex(); + seekNonWs(pattern, pos); + final int text = pos.getIndex(); + int depth = 1; + for (; pos.getIndex() < pattern.length(); next(pos)) { + switch (pattern.charAt(pos.getIndex())) { + case START_FE: + depth++; + break; + case END_FE: + depth--; + if (depth == 0) { + return pattern.substring(text, pos.getIndex()); + } + break; + case QUOTE: + getQuotedString(pattern, pos); + break; + default: + break; + } + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Insert formats back into the pattern for toPattern() support. + * + * @param pattern source + * @param customPatterns The custom patterns to re-insert, if any + * @return full pattern + */ + private String insertFormats(final String pattern, final ArrayList customPatterns) { + if (!containsElements(customPatterns)) { + return pattern; + } + final StringBuilder sb = new StringBuilder(pattern.length() * 2); + final ParsePosition pos = new ParsePosition(0); + int fe = -1; + int depth = 0; + while (pos.getIndex() < pattern.length()) { + final char c = pattern.charAt(pos.getIndex()); + switch (c) { + case QUOTE: + appendQuotedString(pattern, pos, sb); + break; + case START_FE: + depth++; + sb.append(START_FE).append(readArgumentIndex(pattern, next(pos))); + // do not look for custom patterns when they are embedded, e.g. in a choice + if (depth == 1) { + fe++; + final String customPattern = customPatterns.get(fe); + if (customPattern != null) { + sb.append(START_FMT).append(customPattern); + } + } + break; + case END_FE: + depth--; + //$FALL-THROUGH$ + default: + sb.append(c); + next(pos); + } + } + return sb.toString(); + } + + /** + * Consume whitespace from the current parse position. + * + * @param pattern String to read + * @param pos current position + */ + private void seekNonWs(final String pattern, final ParsePosition pos) { + int len = 0; + final char[] buffer = pattern.toCharArray(); + do { + len = StrMatcher.splitMatcher().isMatch(buffer, pos.getIndex()); + pos.setIndex(pos.getIndex() + len); + } while (len > 0 && pos.getIndex() < pattern.length()); + } + + /** + * Convenience method to advance parse position by 1 + * + * @param pos ParsePosition + * @return pos + */ + private ParsePosition next(final ParsePosition pos) { + pos.setIndex(pos.getIndex() + 1); + return pos; + } + + /** + * Consume a quoted string, adding it to appendTo if + * specified. + * + * @param pattern pattern to parse + * @param pos current parse position + * @param appendTo optional StringBuilder to append + * @return appendTo + */ + private StringBuilder appendQuotedString(final String pattern, final ParsePosition pos, + final StringBuilder appendTo) { + assert pattern.toCharArray()[pos.getIndex()] == QUOTE : + "Quoted string must start with quote character"; + + // handle quote character at the beginning of the string + if(appendTo != null) { + appendTo.append(QUOTE); + } + next(pos); + + final int start = pos.getIndex(); + final char[] c = pattern.toCharArray(); + int lastHold = start; + for (int i = pos.getIndex(); i < pattern.length(); i++) { + switch (c[pos.getIndex()]) { + case QUOTE: + next(pos); + return appendTo == null ? null : appendTo.append(c, lastHold, + pos.getIndex() - lastHold); + default: + next(pos); + } + } + throw new IllegalArgumentException( + "Unterminated quoted string at position " + start); + } + + /** + * Consume quoted string only + * + * @param pattern pattern to parse + * @param pos current parse position + */ + private void getQuotedString(final String pattern, final ParsePosition pos) { + appendQuotedString(pattern, pos, null); + } + + /** + * Learn whether the specified Collection contains non-null elements. + * @param coll to check + * @return true if some Object was found, false otherwise. + */ + private boolean containsElements(final Collection coll) { + if (coll == null || coll.isEmpty()) { + return false; + } + for (final Object name : coll) { + if (name != null) { + return true; + } + } + return false; + } +} diff --git a/Java/commons-lang-ExtendedMessageFormat_178/metadata.json b/Java/commons-lang-ExtendedMessageFormat_178/metadata.json new file mode 100644 index 000000000..e0c451c69 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_178/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExtendedMessageFormat_178", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java", + "line": 178, + "npe_method": "applyPattern", + "deref_field": "format", + "npe_class": "ExtendedMessageFormat", + "repo": "commons-lang", + "bug_id": "ExtendedMessageFormat_178" + } +} diff --git a/Java/commons-lang-ExtendedMessageFormat_178/npe.json b/Java/commons-lang-ExtendedMessageFormat_178/npe.json new file mode 100644 index 000000000..23ae1df1e --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_178/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java", + "line": 178, + "npe_method": "applyPattern", + "deref_field": "format", + "npe_class": "ExtendedMessageFormat" +} \ No newline at end of file diff --git a/Java/commons-lang-ExtendedMessageFormat_200/Dockerfile b/Java/commons-lang-ExtendedMessageFormat_200/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_200/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExtendedMessageFormat_200/buggy.java b/Java/commons-lang-ExtendedMessageFormat_200/buggy.java new file mode 100644 index 000000000..5e51777b7 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_200/buggy.java @@ -0,0 +1,532 @@ +/* + * 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 org.apache.commons.lang3.text; + +import java.text.Format; +import java.text.MessageFormat; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.Validate; + +/** + * Extends java.text.MessageFormat to allow pluggable/additional formatting + * options for embedded format elements. Client code should specify a registry + * of FormatFactory instances associated with String + * format names. This registry will be consulted when the format elements are + * parsed from the message pattern. In this way custom patterns can be specified, + * and the formats supported by java.text.MessageFormat can be overridden + * at the format and/or format style level (see MessageFormat). A "format element" + * embedded in the message pattern is specified (()? signifies optionality):
+ * {argument-number(,format-name + * (,format-style)?)?} + * + *

+ * format-name and format-style values are trimmed of surrounding whitespace + * in the manner of java.text.MessageFormat. If format-name denotes + * FormatFactory formatFactoryInstance in registry, a Format + * matching format-name and format-style is requested from + * formatFactoryInstance. If this is successful, the Format + * found is used for this format element. + *

+ * + *

NOTICE: The various subformat mutator methods are considered unnecessary; they exist on the parent + * class to allow the type of customization which it is the job of this class to provide in + * a configurable fashion. These methods have thus been disabled and will throw + * UnsupportedOperationException if called. + *

+ * + *

Limitations inherited from java.text.MessageFormat:

+ *
    + *
  • When using "choice" subformats, support for nested formatting instructions is limited + * to that provided by the base class.
  • + *
  • Thread-safety of Formats, including MessageFormat and thus + * ExtendedMessageFormat, is not guaranteed.
  • + *
+ * + * @since 2.4 + */ +public class ExtendedMessageFormat extends MessageFormat { + private static final long serialVersionUID = -2362048321261811743L; + private static final int HASH_SEED = 31; + + private static final String DUMMY_PATTERN = ""; + private static final char START_FMT = ','; + private static final char END_FE = '}'; + private static final char START_FE = '{'; + private static final char QUOTE = '\''; + + private String toPattern; + private final Map registry; + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern) { + this(pattern, Locale.getDefault()); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Locale locale) { + this(pattern, locale, null); + } + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Map registry) { + this(pattern, Locale.getDefault(), registry); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Locale locale, final Map registry) { + super(DUMMY_PATTERN); + setLocale(locale); + this.registry = registry; + applyPattern(pattern); + } + + /** + * {@inheritDoc} + */ + @Override + public String toPattern() { + return toPattern; + } + + /** + * Apply the specified pattern. + * + * @param pattern String + */ + @Override +/** + * Apply the specified pattern. + * + * @param pattern + * String + */ +public final void applyPattern(final java.lang.String pattern) { + if (registry == null) { + super.applyPattern(pattern); + toPattern = super.toPattern(); + return; + } + final java.util.ArrayList foundFormats = new java.util.ArrayList(); + final java.util.ArrayList foundDescriptions = new java.util.ArrayList(); + final java.lang.StringBuilder stripCustom = new java.lang.StringBuilder(pattern.length()); + final java.text.ParsePosition pos = new java.text.ParsePosition(0); + final char[] c = pattern.toCharArray(); + int fmtCount = 0; + while (pos.getIndex() < pattern.length()) { + switch (c[pos.getIndex()]) { + case org.apache.commons.lang3.text.ExtendedMessageFormat.QUOTE : + appendQuotedString(pattern, pos, stripCustom); + break; + case org.apache.commons.lang3.text.ExtendedMessageFormat.START_FE : + fmtCount++; + seekNonWs(pattern, pos); + final int start = pos.getIndex(); + final int index = readArgumentIndex(pattern, next(pos)); + stripCustom.append(org.apache.commons.lang3.text.ExtendedMessageFormat.START_FE).append(index); + seekNonWs(pattern, pos); + java.text.Format format = null; + java.lang.String formatDescription = null; + if (c[pos.getIndex()] == org.apache.commons.lang3.text.ExtendedMessageFormat.START_FMT) { + formatDescription = parseFormatDescription(pattern, next(pos)); + format = getFormat(formatDescription); + if (format == null) { + stripCustom.append(org.apache.commons.lang3.text.ExtendedMessageFormat.START_FMT).append(formatDescription); + } + } + foundFormats.add(format); + foundDescriptions.add(format == null ? null : formatDescription); + org.apache.commons.lang3.Validate.isTrue(foundFormats.size() == fmtCount); + org.apache.commons.lang3.Validate.isTrue(foundDescriptions.size() == fmtCount); + if (c[pos.getIndex()] != org.apache.commons.lang3.text.ExtendedMessageFormat.END_FE) { + throw new java.lang.IllegalArgumentException("Unreadable format element at position " + start); + } + // $FALL-THROUGH$ + default : + stripCustom.append(c[pos.getIndex()]); + next(pos); + } + } + super.applyPattern(stripCustom.toString()); + toPattern = insertFormats(super.toPattern(), foundDescriptions); + if (containsElements(foundFormats)) { + final java.text.Format[] origFormats = getFormats(); + // only loop over what we know we have, as MessageFormat on Java 1.3 + // seems to provide an extra format element: + int i = 0; + for (final java.util.Iterator it = foundFormats.iterator(); it.hasNext(); i++) { + final java.text.Format f = it.next(); + { + origFormats[i] = /* NPEX_NULL_EXP */ + f; + } + } + super.setFormats(origFormats); + } +} + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param formatElementIndex format element index + * @param newFormat the new format + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormat(final int formatElementIndex, final Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param argumentIndex argument index + * @param newFormat the new format + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormatByArgumentIndex(final int argumentIndex, final Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormats(final Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormatsByArgumentIndex(final Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Check if this extended message format is equal to another object. + * + * @param obj the object to compare to + * @return true if this object equals the other, otherwise false + */ + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (obj == null) { + return false; + } + if (!super.equals(obj)) { + return false; + } + if (ObjectUtils.notEqual(getClass(), obj.getClass())) { + return false; + } + final ExtendedMessageFormat rhs = (ExtendedMessageFormat)obj; + if (ObjectUtils.notEqual(toPattern, rhs.toPattern)) { + return false; + } + if (ObjectUtils.notEqual(registry, rhs.registry)) { + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings( "deprecation" ) // ObjectUtils.hashCode(Object) has been deprecated in 3.2 + @Override + public int hashCode() { + int result = super.hashCode(); + result = HASH_SEED * result + ObjectUtils.hashCode(registry); + result = HASH_SEED * result + ObjectUtils.hashCode(toPattern); + return result; + } + + /** + * Get a custom format from a format description. + * + * @param desc String + * @return Format + */ + private Format getFormat(final String desc) { + if (registry != null) { + String name = desc; + String args = null; + final int i = desc.indexOf(START_FMT); + if (i > 0) { + name = desc.substring(0, i).trim(); + args = desc.substring(i + 1).trim(); + } + final FormatFactory factory = registry.get(name); + if (factory != null) { + return factory.getFormat(name, args, getLocale()); + } + } + return null; + } + + /** + * Read the argument index from the current format element + * + * @param pattern pattern to parse + * @param pos current parse position + * @return argument index + */ + private int readArgumentIndex(final String pattern, final ParsePosition pos) { + final int start = pos.getIndex(); + seekNonWs(pattern, pos); + final StringBuilder result = new StringBuilder(); + boolean error = false; + for (; !error && pos.getIndex() < pattern.length(); next(pos)) { + char c = pattern.charAt(pos.getIndex()); + if (Character.isWhitespace(c)) { + seekNonWs(pattern, pos); + c = pattern.charAt(pos.getIndex()); + if (c != START_FMT && c != END_FE) { + error = true; + continue; + } + } + if ((c == START_FMT || c == END_FE) && result.length() > 0) { + try { + return Integer.parseInt(result.toString()); + } catch (final NumberFormatException e) { // NOPMD + // we've already ensured only digits, so unless something + // outlandishly large was specified we should be okay. + } + } + error = !Character.isDigit(c); + result.append(c); + } + if (error) { + throw new IllegalArgumentException( + "Invalid format argument index at position " + start + ": " + + pattern.substring(start, pos.getIndex())); + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Parse the format component of a format element. + * + * @param pattern string to parse + * @param pos current parse position + * @return Format description String + */ + private String parseFormatDescription(final String pattern, final ParsePosition pos) { + final int start = pos.getIndex(); + seekNonWs(pattern, pos); + final int text = pos.getIndex(); + int depth = 1; + for (; pos.getIndex() < pattern.length(); next(pos)) { + switch (pattern.charAt(pos.getIndex())) { + case START_FE: + depth++; + break; + case END_FE: + depth--; + if (depth == 0) { + return pattern.substring(text, pos.getIndex()); + } + break; + case QUOTE: + getQuotedString(pattern, pos); + break; + default: + break; + } + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Insert formats back into the pattern for toPattern() support. + * + * @param pattern source + * @param customPatterns The custom patterns to re-insert, if any + * @return full pattern + */ + private String insertFormats(final String pattern, final ArrayList customPatterns) { + if (!containsElements(customPatterns)) { + return pattern; + } + final StringBuilder sb = new StringBuilder(pattern.length() * 2); + final ParsePosition pos = new ParsePosition(0); + int fe = -1; + int depth = 0; + while (pos.getIndex() < pattern.length()) { + final char c = pattern.charAt(pos.getIndex()); + switch (c) { + case QUOTE: + appendQuotedString(pattern, pos, sb); + break; + case START_FE: + depth++; + sb.append(START_FE).append(readArgumentIndex(pattern, next(pos))); + // do not look for custom patterns when they are embedded, e.g. in a choice + if (depth == 1) { + fe++; + final String customPattern = customPatterns.get(fe); + if (customPattern != null) { + sb.append(START_FMT).append(customPattern); + } + } + break; + case END_FE: + depth--; + //$FALL-THROUGH$ + default: + sb.append(c); + next(pos); + } + } + return sb.toString(); + } + + /** + * Consume whitespace from the current parse position. + * + * @param pattern String to read + * @param pos current position + */ + private void seekNonWs(final String pattern, final ParsePosition pos) { + int len = 0; + final char[] buffer = pattern.toCharArray(); + do { + len = StrMatcher.splitMatcher().isMatch(buffer, pos.getIndex()); + pos.setIndex(pos.getIndex() + len); + } while (len > 0 && pos.getIndex() < pattern.length()); + } + + /** + * Convenience method to advance parse position by 1 + * + * @param pos ParsePosition + * @return pos + */ + private ParsePosition next(final ParsePosition pos) { + pos.setIndex(pos.getIndex() + 1); + return pos; + } + + /** + * Consume a quoted string, adding it to appendTo if + * specified. + * + * @param pattern pattern to parse + * @param pos current parse position + * @param appendTo optional StringBuilder to append + * @return appendTo + */ + private StringBuilder appendQuotedString(final String pattern, final ParsePosition pos, + final StringBuilder appendTo) { + assert pattern.toCharArray()[pos.getIndex()] == QUOTE : + "Quoted string must start with quote character"; + + // handle quote character at the beginning of the string + if(appendTo != null) { + appendTo.append(QUOTE); + } + next(pos); + + final int start = pos.getIndex(); + final char[] c = pattern.toCharArray(); + int lastHold = start; + for (int i = pos.getIndex(); i < pattern.length(); i++) { + switch (c[pos.getIndex()]) { + case QUOTE: + next(pos); + return appendTo == null ? null : appendTo.append(c, lastHold, + pos.getIndex() - lastHold); + default: + next(pos); + } + } + throw new IllegalArgumentException( + "Unterminated quoted string at position " + start); + } + + /** + * Consume quoted string only + * + * @param pattern pattern to parse + * @param pos current parse position + */ + private void getQuotedString(final String pattern, final ParsePosition pos) { + appendQuotedString(pattern, pos, null); + } + + /** + * Learn whether the specified Collection contains non-null elements. + * @param coll to check + * @return true if some Object was found, false otherwise. + */ + private boolean containsElements(final Collection coll) { + if (coll == null || coll.isEmpty()) { + return false; + } + for (final Object name : coll) { + if (name != null) { + return true; + } + } + return false; + } +} diff --git a/Java/commons-lang-ExtendedMessageFormat_200/metadata.json b/Java/commons-lang-ExtendedMessageFormat_200/metadata.json new file mode 100644 index 000000000..e71c4c222 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_200/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExtendedMessageFormat_200", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java", + "line": 205, + "npe_method": "applyPattern", + "deref_field": "f", + "npe_class": "ExtendedMessageFormat", + "repo": "commons-lang", + "bug_id": "ExtendedMessageFormat_200" + } +} diff --git a/Java/commons-lang-ExtendedMessageFormat_200/npe.json b/Java/commons-lang-ExtendedMessageFormat_200/npe.json new file mode 100644 index 000000000..2c59dd268 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_200/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java", + "line": 205, + "npe_method": "applyPattern", + "deref_field": "f", + "npe_class": "ExtendedMessageFormat" +} \ No newline at end of file diff --git a/Java/commons-lang-ExtendedMessageFormat_423/Dockerfile b/Java/commons-lang-ExtendedMessageFormat_423/Dockerfile new file mode 100644 index 000000000..7b7fbe349 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_423/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:commons-lang + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/commons-lang-ExtendedMessageFormat_423/buggy.java b/Java/commons-lang-ExtendedMessageFormat_423/buggy.java new file mode 100644 index 000000000..7299f49b4 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_423/buggy.java @@ -0,0 +1,538 @@ +/* + * 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 org.apache.commons.lang3.text; + +import java.text.Format; +import java.text.MessageFormat; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.Validate; + +/** + * Extends java.text.MessageFormat to allow pluggable/additional formatting + * options for embedded format elements. Client code should specify a registry + * of FormatFactory instances associated with String + * format names. This registry will be consulted when the format elements are + * parsed from the message pattern. In this way custom patterns can be specified, + * and the formats supported by java.text.MessageFormat can be overridden + * at the format and/or format style level (see MessageFormat). A "format element" + * embedded in the message pattern is specified (()? signifies optionality):
+ * {argument-number(,format-name + * (,format-style)?)?} + * + *

+ * format-name and format-style values are trimmed of surrounding whitespace + * in the manner of java.text.MessageFormat. If format-name denotes + * FormatFactory formatFactoryInstance in registry, a Format + * matching format-name and format-style is requested from + * formatFactoryInstance. If this is successful, the Format + * found is used for this format element. + *

+ * + *

NOTICE: The various subformat mutator methods are considered unnecessary; they exist on the parent + * class to allow the type of customization which it is the job of this class to provide in + * a configurable fashion. These methods have thus been disabled and will throw + * UnsupportedOperationException if called. + *

+ * + *

Limitations inherited from java.text.MessageFormat:

+ *
    + *
  • When using "choice" subformats, support for nested formatting instructions is limited + * to that provided by the base class.
  • + *
  • Thread-safety of Formats, including MessageFormat and thus + * ExtendedMessageFormat, is not guaranteed.
  • + *
+ * + * @since 2.4 + */ +public class ExtendedMessageFormat extends MessageFormat { + private static final long serialVersionUID = -2362048321261811743L; + private static final int HASH_SEED = 31; + + private static final String DUMMY_PATTERN = ""; + private static final char START_FMT = ','; + private static final char END_FE = '}'; + private static final char START_FE = '{'; + private static final char QUOTE = '\''; + + private String toPattern; + private final Map registry; + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern) { + this(pattern, Locale.getDefault()); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Locale locale) { + this(pattern, locale, null); + } + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Map registry) { + this(pattern, Locale.getDefault(), registry); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(final String pattern, final Locale locale, final Map registry) { + super(DUMMY_PATTERN); + setLocale(locale); + this.registry = registry; + applyPattern(pattern); + } + + /** + * {@inheritDoc} + */ + @Override + public String toPattern() { + return toPattern; + } + + /** + * Apply the specified pattern. + * + * @param pattern String + */ + @Override + public final void applyPattern(final String pattern) { + if (registry == null) { + super.applyPattern(pattern); + toPattern = super.toPattern(); + return; + } + final ArrayList foundFormats = new ArrayList(); + final ArrayList foundDescriptions = new ArrayList(); + final StringBuilder stripCustom = new StringBuilder(pattern.length()); + + final ParsePosition pos = new ParsePosition(0); + final char[] c = pattern.toCharArray(); + int fmtCount = 0; + while (pos.getIndex() < pattern.length()) { + switch (c[pos.getIndex()]) { + case QUOTE: + appendQuotedString(pattern, pos, stripCustom); + break; + case START_FE: + fmtCount++; + seekNonWs(pattern, pos); + final int start = pos.getIndex(); + final int index = readArgumentIndex(pattern, next(pos)); + stripCustom.append(START_FE).append(index); + seekNonWs(pattern, pos); + Format format = null; + String formatDescription = null; + if (c[pos.getIndex()] == START_FMT) { + formatDescription = parseFormatDescription(pattern, + next(pos)); + format = getFormat(formatDescription); + if (format == null) { + stripCustom.append(START_FMT).append(formatDescription); + } + } + foundFormats.add(format); + foundDescriptions.add(format == null ? null : formatDescription); + Validate.isTrue(foundFormats.size() == fmtCount); + Validate.isTrue(foundDescriptions.size() == fmtCount); + if (c[pos.getIndex()] != END_FE) { + throw new IllegalArgumentException( + "Unreadable format element at position " + start); + } + //$FALL-THROUGH$ + default: + stripCustom.append(c[pos.getIndex()]); + next(pos); + } + } + super.applyPattern(stripCustom.toString()); + toPattern = insertFormats(super.toPattern(), foundDescriptions); + if (containsElements(foundFormats)) { + final Format[] origFormats = getFormats(); + // only loop over what we know we have, as MessageFormat on Java 1.3 + // seems to provide an extra format element: + int i = 0; + for (final Iterator it = foundFormats.iterator(); it.hasNext(); i++) { + final Format f = it.next(); + if (f != null) { + origFormats[i] = f; + } + } + super.setFormats(origFormats); + } + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param formatElementIndex format element index + * @param newFormat the new format + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormat(final int formatElementIndex, final Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param argumentIndex argument index + * @param newFormat the new format + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormatByArgumentIndex(final int argumentIndex, final Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormats(final Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException always thrown since this isn't supported by ExtendMessageFormat + */ + @Override + public void setFormatsByArgumentIndex(final Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Check if this extended message format is equal to another object. + * + * @param obj the object to compare to + * @return true if this object equals the other, otherwise false + */ + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (obj == null) { + return false; + } + if (!super.equals(obj)) { + return false; + } + if (ObjectUtils.notEqual(getClass(), obj.getClass())) { + return false; + } + final ExtendedMessageFormat rhs = (ExtendedMessageFormat)obj; + if (ObjectUtils.notEqual(toPattern, rhs.toPattern)) { + return false; + } + if (ObjectUtils.notEqual(registry, rhs.registry)) { + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings( "deprecation" ) // ObjectUtils.hashCode(Object) has been deprecated in 3.2 + @Override + public int hashCode() { + int result = super.hashCode(); + result = HASH_SEED * result + ObjectUtils.hashCode(registry); + result = HASH_SEED * result + ObjectUtils.hashCode(toPattern); + return result; + } + + /** + * Get a custom format from a format description. + * + * @param desc String + * @return Format + */ + private Format getFormat(final String desc) { + if (registry != null) { + String name = desc; + String args = null; + final int i = desc.indexOf(START_FMT); + if (i > 0) { + name = desc.substring(0, i).trim(); + args = desc.substring(i + 1).trim(); + } + final FormatFactory factory = registry.get(name); + if (factory != null) { + return factory.getFormat(name, args, getLocale()); + } + } + return null; + } + + /** + * Read the argument index from the current format element + * + * @param pattern pattern to parse + * @param pos current parse position + * @return argument index + */ + private int readArgumentIndex(final String pattern, final ParsePosition pos) { + final int start = pos.getIndex(); + seekNonWs(pattern, pos); + final StringBuilder result = new StringBuilder(); + boolean error = false; + for (; !error && pos.getIndex() < pattern.length(); next(pos)) { + char c = pattern.charAt(pos.getIndex()); + if (Character.isWhitespace(c)) { + seekNonWs(pattern, pos); + c = pattern.charAt(pos.getIndex()); + if (c != START_FMT && c != END_FE) { + error = true; + continue; + } + } + if ((c == START_FMT || c == END_FE) && result.length() > 0) { + try { + return Integer.parseInt(result.toString()); + } catch (final NumberFormatException e) { // NOPMD + // we've already ensured only digits, so unless something + // outlandishly large was specified we should be okay. + } + } + error = !Character.isDigit(c); + result.append(c); + } + if (error) { + throw new IllegalArgumentException( + "Invalid format argument index at position " + start + ": " + + pattern.substring(start, pos.getIndex())); + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Parse the format component of a format element. + * + * @param pattern string to parse + * @param pos current parse position + * @return Format description String + */ + private String parseFormatDescription(final String pattern, final ParsePosition pos) { + final int start = pos.getIndex(); + seekNonWs(pattern, pos); + final int text = pos.getIndex(); + int depth = 1; + for (; pos.getIndex() < pattern.length(); next(pos)) { + switch (pattern.charAt(pos.getIndex())) { + case START_FE: + depth++; + break; + case END_FE: + depth--; + if (depth == 0) { + return pattern.substring(text, pos.getIndex()); + } + break; + case QUOTE: + getQuotedString(pattern, pos); + break; + default: + break; + } + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Insert formats back into the pattern for toPattern() support. + * + * @param pattern source + * @param customPatterns The custom patterns to re-insert, if any + * @return full pattern + */ +/** + * Insert formats back into the pattern for toPattern() support. + * + * @param pattern + * source + * @param customPatterns + * The custom patterns to re-insert, if any + * @return full pattern + */ +private java.lang.String insertFormats(final java.lang.String pattern, final java.util.ArrayList customPatterns) { + if (!containsElements(customPatterns)) { + return pattern; + } + final java.lang.StringBuilder sb = new java.lang.StringBuilder(pattern.length() * 2); + final java.text.ParsePosition pos = new java.text.ParsePosition(0); + int fe = -1; + int depth = 0; + while (pos.getIndex() < pattern.length()) { + final char c = pattern.charAt(pos.getIndex()); + switch (c) { + case org.apache.commons.lang3.text.ExtendedMessageFormat.QUOTE : + appendQuotedString(pattern, pos, sb); + break; + case org.apache.commons.lang3.text.ExtendedMessageFormat.START_FE : + depth++; + sb.append(org.apache.commons.lang3.text.ExtendedMessageFormat.START_FE).append(readArgumentIndex(pattern, next(pos))); + // do not look for custom patterns when they are embedded, e.g. in a choice + if (depth == 1) { + fe++; + final java.lang.String customPattern = customPatterns.get(fe); + { + sb.append(org.apache.commons.lang3.text.ExtendedMessageFormat.START_FMT).append(/* NPEX_NULL_EXP */ + customPattern); + } + } + break; + case org.apache.commons.lang3.text.ExtendedMessageFormat.END_FE : + depth--; + // $FALL-THROUGH$ + default : + sb.append(c); + next(pos); + } + } + return sb.toString(); +} + + /** + * Consume whitespace from the current parse position. + * + * @param pattern String to read + * @param pos current position + */ + private void seekNonWs(final String pattern, final ParsePosition pos) { + int len = 0; + final char[] buffer = pattern.toCharArray(); + do { + len = StrMatcher.splitMatcher().isMatch(buffer, pos.getIndex()); + pos.setIndex(pos.getIndex() + len); + } while (len > 0 && pos.getIndex() < pattern.length()); + } + + /** + * Convenience method to advance parse position by 1 + * + * @param pos ParsePosition + * @return pos + */ + private ParsePosition next(final ParsePosition pos) { + pos.setIndex(pos.getIndex() + 1); + return pos; + } + + /** + * Consume a quoted string, adding it to appendTo if + * specified. + * + * @param pattern pattern to parse + * @param pos current parse position + * @param appendTo optional StringBuilder to append + * @return appendTo + */ + private StringBuilder appendQuotedString(final String pattern, final ParsePosition pos, + final StringBuilder appendTo) { + assert pattern.toCharArray()[pos.getIndex()] == QUOTE : + "Quoted string must start with quote character"; + + // handle quote character at the beginning of the string + if(appendTo != null) { + appendTo.append(QUOTE); + } + next(pos); + + final int start = pos.getIndex(); + final char[] c = pattern.toCharArray(); + int lastHold = start; + for (int i = pos.getIndex(); i < pattern.length(); i++) { + switch (c[pos.getIndex()]) { + case QUOTE: + next(pos); + return appendTo == null ? null : appendTo.append(c, lastHold, + pos.getIndex() - lastHold); + default: + next(pos); + } + } + throw new IllegalArgumentException( + "Unterminated quoted string at position " + start); + } + + /** + * Consume quoted string only + * + * @param pattern pattern to parse + * @param pos current parse position + */ + private void getQuotedString(final String pattern, final ParsePosition pos) { + appendQuotedString(pattern, pos, null); + } + + /** + * Learn whether the specified Collection contains non-null elements. + * @param coll to check + * @return true if some Object was found, false otherwise. + */ + private boolean containsElements(final Collection coll) { + if (coll == null || coll.isEmpty()) { + return false; + } + for (final Object name : coll) { + if (name != null) { + return true; + } + } + return false; + } +} diff --git a/Java/commons-lang-ExtendedMessageFormat_423/metadata.json b/Java/commons-lang-ExtendedMessageFormat_423/metadata.json new file mode 100644 index 000000000..0b7c76f04 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_423/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "commons-lang-ExtendedMessageFormat_423", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java", + "line": 434, + "npe_method": "insertFormats", + "deref_field": "customPattern", + "npe_class": "ExtendedMessageFormat", + "repo": "commons-lang", + "bug_id": "ExtendedMessageFormat_423" + } +} diff --git a/Java/commons-lang-ExtendedMessageFormat_423/npe.json b/Java/commons-lang-ExtendedMessageFormat_423/npe.json new file mode 100644 index 000000000..2975a4543 --- /dev/null +++ b/Java/commons-lang-ExtendedMessageFormat_423/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/commons/lang3/text/ExtendedMessageFormat.java", + "line": 434, + "npe_method": "insertFormats", + "deref_field": "customPattern", + "npe_class": "ExtendedMessageFormat" +} \ No newline at end of file