/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.utility.string; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.persistence.tools.workbench.utility.ClassTools; import org.eclipse.persistence.tools.workbench.utility.CollectionTools; public final class StringTools { /** carriage return */ public static final String CR = System.getProperty("line.separator"); /** empty string */ public static final String EMPTY_STRING = ""; /** empty array */ public static final String[] EMPTY_STRING_ARRAY = new String[0]; /** * The minimum count to be consider a match between two strings; which is 2. */ public static final int MINIMUM_MATCHING_LETTER = 2; /** * The lowest weight for matching two strings. This means there is no match. */ public static final int NO_MATCH = 0; /** * one of the weight for matching two strings. This means the first * string contains the second string and is longer than the second one. */ public static final int STRING_CONTAINS = 6; /** * The second lowest weight for matching two strings. This means the first * string contains a section of the second string. */ public static final int STRING_CONTAINS_PART = 2; /** * One of the weight for matching two strings. This means ... */ public static final int STRING_CONTAINS_PART_CONTINUOUS = 4; /** * The weight of a potiential match between two strings. The value is 10 * since both strings are equals and we have a match. */ public static final int STRINGS_EQUAL = 10; /** * The weight of a potiential match between two strings. The value is 8. * Both strings are equals but one of them contains a delimitor between * charater. */ public static final int STRINGS_EQUAL_WITH_IGNORE_CHARACTERS = 8; // ********** padding/truncating ********** /** * Pad the specified string to the specified length. * If the string is already the specified length, it is returned unchanged. * If it is longer than the specified length, an IllegalArgumentException is thrown. * If it is shorter than the specified length, it is padded with spaces at the end. * String#pad(int) */ public static String pad(String string, int length) { int stringLength = string.length(); if (stringLength > length) { throw new IllegalArgumentException("String is too long: " + stringLength + " > " + length); } if (stringLength == length) { return string; } return padInternal(string, length); } /** * Pad or truncate the specified string to the specified length. * If the string is already the specified length, it is returned unchanged. * If it is longer than the specified length, it is truncated. * If it is shorter than the specified length, it is padded with spaces at the end. * String#padOrTruncate(int) */ public static String padOrTruncate(String string, int length) { int stringLength = string.length(); if (stringLength == length) { return string; } if (stringLength > length) { return string.substring(0, length); } return padInternal(string, length); } /** * Pad the specified string without validating the parms. */ private static String padInternal(String string, int length) { char[] a = new char[length]; int stringLength = string.length(); string.getChars(0, stringLength, a, 0); Arrays.fill(a, stringLength, length, ' '); return new String(a); } /** * Pad the specified string to the specified length. * If the string is already the specified length, it is returned unchanged. * If it is longer than the specified length, an IllegalArgumentException is thrown. * If it is shorter than the specified length, it is padded with zeros at the front. * String#zeroPad(int) */ public static String zeroPad(String string, int length) { int stringLength = string.length(); if (stringLength > length) { throw new IllegalArgumentException("String is too long: " + stringLength + " > " + length); } if (stringLength == length) { return string; } return zeroPadInternal(string, length); } /** * Pad or truncate the specified string to the specified length. * If the string is already the specified length, it is returned unchanged. * If it is longer than the specified length, only the last part of the string is returned. * If it is shorter than the specified length, it is padded with zeros at the front. * String#zeroPadOrTruncate(int) */ public static String zeroPadOrTruncate(String string, int length) { int stringLength = string.length(); if (stringLength == length) { return string; } if (stringLength > length) { return string.substring(stringLength - length); } return zeroPadInternal(string, length); } /** * Zero-pad the specified string without validating the parms. */ private static String zeroPadInternal(String string, int length) { char[] a = new char[length]; int stringLength = string.length(); int padLength = length - stringLength; string.getChars(0, stringLength, a, padLength); Arrays.fill(a, 0, padLength, '0'); return new String(a); } // ********** removing characters ********** /** * Remove the first occurrence of the specified character * from the specified string and return the result. * String#removeFirstOccurrence(char) */ public static String removeFirstOccurrence(String string, char character) { int index = string.indexOf(character); if (index == -1) { // character not found return string; } if (index == 0) { // character found at the front of string return string.substring(1); } int last = string.length() - 1; if (index == last) { // character found at the end of string return string.substring(0, last); } // character found somewhere in the middle of the string return string.substring(0, index).concat(string.substring(index + 1)); } /** * Remove all occurrences of the specified character * from the specified string and return the result. * String#removeAllOccurrences(char) */ public static String removeAllOccurrences(String string, char character) { StringBuffer sb = new StringBuffer(string.length()); removeAllOccurrencesOn(string, character, sb); return sb.toString(); } /** * Remove all occurrences of the specified character * from the specified string and append the result to the * specified string buffer. * String#removeAllOccurrencesOn(char, StringBuffer) */ public static void removeAllOccurrencesOn(String string, char character, StringBuffer sb) { int len = string.length(); for (int i = 0; i < len; i++) { char c = string.charAt(i); if (c != character) { sb.append(c); } } } /** * Remove all the spaces from the specified string and return the result. * String#removeAllSpaces() */ public static String removeAllSpaces(String string) { return removeAllOccurrences(string, ' '); } // ********** common prefix ********** /** * Return the length of the common prefix shared by the specified strings. * String#commonPrefixLength(String) */ public static int commonPrefixLength(String s1, String s2) { return commonPrefixLength(s1.toCharArray(), s2.toCharArray()); } /** * Return the length of the common prefix shared by the specified strings. */ public static int commonPrefixLength(char[] s1, char[] s2) { return commonPrefixLengthInternal(s1, s2, Math.min(s1.length, s2.length)); } /** * Return the length of the common prefix shared by the specified strings; * but limit the length to the specified maximum. * String#commonPrefixLength(String, int) */ public static int commonPrefixLength(String s1, String s2, int max) { return commonPrefixLength(s1.toCharArray(), s2.toCharArray()); } /** * Return the length of the common prefix shared by the specified strings; * but limit the length to the specified maximum. */ public static int commonPrefixLength(char[] s1, char[] s2, int max) { return commonPrefixLengthInternal(s1, s2, Math.min(max, Math.min(s1.length, s2.length))); } /** * Return the length of the common prefix shared by the specified strings; * but limit the length to the specified maximum. Assume the specified * maximum is less than the lengths of the specified strings. */ private static int commonPrefixLengthInternal(char[] s1, char[] s2, int max) { for (int i = 0; i < max; i++) { if (s1[i] != s2[i]) { return i; } } return max; // all the characters up to 'max' are the same } // ********** capitalization ********** /** * no zero-length check or lower case check */ private static char[] capitalizeInternal(char[] string) { string[0] = Character.toUpperCase(string[0]); return string; } /** * Modify and return the specified string with * its first letter capitalized. */ public static char[] capitalize(char[] string) { if ((string.length == 0) || Character.isUpperCase(string[0])) { return string; } return capitalizeInternal(string); } /** * Return the specified string with its first letter capitalized. * String#capitalize() */ public static String capitalize(String string) { if ((string.length() == 0) || Character.isUpperCase(string.charAt(0))) { return string; } return new String(capitalizeInternal(string.toCharArray())); } /** * no zero-length check or upper case check */ private static void capitalizeOnInternal(char[] string, StringBuffer sb) { sb.append(Character.toUpperCase(string[0])); sb.append(string, 1, string.length - 1); } /** * Append the specified string to the specified string buffer * with its first letter capitalized. */ public static void capitalizeOn(char[] string, StringBuffer sb) { if (string.length == 0) { return; } if (Character.isUpperCase(string[0])) { sb.append(string); } else { capitalizeOnInternal(string, sb); } } /** * Append the specified string to the specified string buffer * with its first letter capitalized. * String#capitalizeOn(StringBuffer) */ public static void capitalizeOn(String string, StringBuffer sb) { if (string.length() == 0) { return; } if (Character.isUpperCase(string.charAt(0))) { sb.append(string); } else { capitalizeOnInternal(string.toCharArray(), sb); } } /** * no zero-length check or upper case check */ private static void capitalizeOnInternal(char[] string, Writer writer) { try { writer.write(Character.toUpperCase(string[0])); writer.write(string, 1, string.length - 1); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * checked exceptions suck */ private static void writeStringOn(char[] string, Writer writer) { try { writer.write(string); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * Append the specified string to the specified string buffer * with its first letter capitalized. */ public static void capitalizeOn(char[] string, Writer writer) { if (string.length == 0) { return; } if (Character.isUpperCase(string[0])) { writeStringOn(string, writer); } else { capitalizeOnInternal(string, writer); } } /** * checked exceptions suck */ private static void writeStringOn(String string, Writer writer) { try { writer.write(string); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * Append the specified string to the specified string buffer * with its first letter capitalized. * String#capitalizeOn(Writer) */ public static void capitalizeOn(String string, Writer writer) { if (string.length() == 0) { return; } if (Character.isUpperCase(string.charAt(0))) { writeStringOn(string, writer); } else { capitalizeOnInternal(string.toCharArray(), writer); } } /** * no zero-length check or lower case check */ private static char[] uncapitalizeInternal(char[] string) { string[0] = Character.toLowerCase(string[0]); return string; } private static boolean stringNeedNotBeUncapitalized(char[] string) { if (string.length == 0) { return true; } if (Character.isLowerCase(string[0])) { return true; } return false; } private static boolean stringNeedNotBeUncapitalizedJavaBean(char[] string) { if (stringNeedNotBeUncapitalized(string)) { return true; } // if both the first and second characters are capitalized, // return the string unchanged if ((string.length > 1) && Character.isUpperCase(string[1]) && Character.isUpperCase(string[0])){ return true; } return false; } /** * Modify and return the specified string with its * first letter converted to lower case. * (Unless both the first and second letters are upper case, * in which case the string is returned unchanged.) */ public static char[] uncapitalizeJavaBean(char[] string) { if (stringNeedNotBeUncapitalizedJavaBean(string)) { return string; } return uncapitalizeInternal(string); } /** * Modify and return the specified string with its * first letter converted to lower case. */ public static char[] uncapitalize(char[] string) { if (stringNeedNotBeUncapitalized(string)) { return string; } return uncapitalizeInternal(string); } private static boolean stringNeedNotBeUncapitalized(String string) { if (string.length() == 0) { return true; } if (Character.isLowerCase(string.charAt(0))) { return true; } return false; } private static boolean stringNeedNotBeUncapitalizedJavaBean(String string) { if (stringNeedNotBeUncapitalized(string)) { return true; } // if both the first and second characters are capitalized, // return the string unchanged if ((string.length() > 1) && Character.isUpperCase(string.charAt(1)) && Character.isUpperCase(string.charAt(0))){ return true; } return false; } /** * Return the specified string with its first letter converted to lower case. * (Unless both the first and second letters are upper case, * in which case the string is returned unchanged.) * String#uncapitalize() */ public static String uncapitalizeJavaBean(String string) { if (stringNeedNotBeUncapitalizedJavaBean(string)) { return string; } return new String(uncapitalizeInternal(string.toCharArray())); } /** * Return the specified string with its first letter converted to lower case. */ public static String uncapitalize(String string) { if (stringNeedNotBeUncapitalized(string)) { return string; } return new String(uncapitalizeInternal(string.toCharArray())); } /** * no zero-length check or lower case check */ private static void uncapitalizeOnInternal(char[] string, StringBuffer sb) { sb.append(Character.toLowerCase(string[0])); sb.append(string, 1, string.length - 1); } /** * Append the specified string to the specified string buffer * with its first letter converted to lower case. * (Unless both the first and second letters are upper case, * in which case the string is returned unchanged.) */ public static void uncapitalizeOn(char[] string, StringBuffer sb) { if (stringNeedNotBeUncapitalized(string)) { sb.append(string); } else { uncapitalizeOnInternal(string, sb); } } /** * Append the specified string to the specified string buffer * with its first letter converted to lower case. * (Unless both the first and second letters are upper case, * in which case the string is returned unchanged.) * String#uncapitalizeOn(StringBuffer) */ public static void uncapitalizeOn(String string, StringBuffer sb) { if (stringNeedNotBeUncapitalized(string)) { sb.append(string); } else { uncapitalizeOnInternal(string.toCharArray(), sb); } } /** * no zero-length check or upper case check */ private static void uncapitalizeOnInternal(char[] string, Writer writer) { try { writer.write(Character.toLowerCase(string[0])); writer.write(string, 1, string.length - 1); } catch (IOException ex) { throw new RuntimeException(ex); } } /** * Append the specified string to the specified string buffer * with its first letter converted to lower case. * (Unless both the first and second letters are upper case, * in which case the string is returned unchanged.) */ public static void uncapitalizeOn(char[] string, Writer writer) { if (stringNeedNotBeUncapitalized(string)) { writeStringOn(string, writer); } else { uncapitalizeOnInternal(string, writer); } } /** * Append the specified string to the specified string buffer * with its first letter converted to lower case. * (Unless both the first and second letters are upper case, * in which case the string is returned unchanged.) * String#uncapitalizeOn(Writer) */ public static void uncapitalizeOn(String string, Writer writer) { if (stringNeedNotBeUncapitalized(string)) { writeStringOn(string, writer); } else { uncapitalizeOnInternal(string.toCharArray(), writer); } } // ********** #toString() helper methods ********** /** * Build a "standard" #toString() result for the specified object * and additional information: * ClassName[00F3EE42] (add'l info) */ public static String buildToStringFor(Object o, Object additionalInfo) { StringBuffer sb = new StringBuffer(); buildSimpleToStringOn(o, sb); sb.append(" ("); sb.append(additionalInfo); sb.append(')'); return sb.toString(); } /** * Build a "standard" simple #toString() result for the specified object: * ClassName[00F3EE42] */ public static String buildToStringFor(Object o) { StringBuffer sb = new StringBuffer(); buildSimpleToStringOn(o, sb); return sb.toString(); } /** * Append a "standard" simple #toString() for the specified object to * the specified string buffer: * ClassName[00F3EE42] */ public static void buildSimpleToStringOn(Object o, StringBuffer sb) { sb.append(ClassTools.toStringClassNameForObject(o)); sb.append('['); // use System#identityHashCode(Object), since Object#hashCode() may be overridden sb.append(zeroPad(Integer.toHexString(System.identityHashCode(o)).toUpperCase(), 8)); sb.append(']'); } // ********** queries ********** /** * Return whether the specified string is null, empty, or contains * only whitespace characters. */ public static boolean stringIsEmpty(String text) { if ((text == null) || (text.length() == 0)) { return true; } for (int i = text.length(); i-- > 0; ) { if ( ! Character.isWhitespace(text.charAt(i))) { return false; } } return true; } /** * Return whether the specified strings are equal, ignoring case. * Check for nulls. */ public static boolean stringsAreEqualIgnoreCase(String s1, String s2) { if ((s1 == null) && (s2 == null)) { return true; // both are null } if ((s1 == null) || (s2 == null)) { return false; // one is null but the other is not } return s1.equalsIgnoreCase(s2); } /** * Return whether the specified strings are equal. * Check for nulls. */ public static boolean stringsAreEqual(String s1, String s2) { if ((s1 == null) && (s2 == null)) { return true; // both are null } if ((s1 == null) || (s2 == null)) { return false; // one is null but the other is not } return s1.equals(s2); } private static final char[] VOWELS = new char[] {'a', 'e', 'i', 'o', 'u'}; public static boolean charIsVowel(char c) { return CollectionTools.contains(VOWELS, Character.toLowerCase(c)); } // ********** conversions ********** public static String replaceHTMLBreaks(String string) { return string.replaceAll("<br>", CR).replaceAll("<BR>", CR); } /** * Convert the specified "camel back" string to an "all caps" string: * "largeProject" -> "LARGE_PROJECT" */ public static String convertCamelBackToAllCaps(String camelBackString) { int len = camelBackString.length(); if (len == 0) { return ""; } char prev = 0; // assume 0 is not a valid char char c = 0; char next = camelBackString.charAt(0); StringBuffer sb = new StringBuffer(len * 2); for (int i = 1; i <= len; i++) { // NB: start at 1 and end at len! c = next; next = ((i == len) ? 0 : camelBackString.charAt(i)); if (camelBackWordBreak(prev, c, next)) { sb.append('_'); } sb.append(Character.toUpperCase(c)); prev = c; } return sb.toString(); } /** * Convert the specified "camel back" string to an "all caps" string: * "largeProject" -> "LARGE_PROJECT" * Limit the resulting string to the specified maximum length. */ public static String convertCamelBackToAllCaps(String camelBackString, int maxLength) { // String result = convertToAllCaps(camelBackString); // return (result.length() > maxLength) ? result.substring(0, maxLength) : result; int len = camelBackString.length(); if ((len == 0) || (maxLength == 0)) { return ""; } char prev = 0; // assume 0 is not a valid char char c = 0; char next = camelBackString.charAt(0); StringBuffer sb = new StringBuffer(maxLength); for (int i = 1; i <= len; i++) { // NB: start at 1 and end at len! c = next; next = ((i == len) ? 0 : camelBackString.charAt(i)); if (camelBackWordBreak(prev, c, next)) { sb.append('_'); if (sb.length() == maxLength) { return sb.toString(); } } sb.append(Character.toUpperCase(c)); if (sb.length() == maxLength) { return sb.toString(); } prev = c; } return sb.toString(); } /** * Return whether the specified series of characters occur at * a "camel back" work break: * "*aa" -> false * "*AA" -> false * "*Aa" -> false * "AaA" -> false * "AAA" -> false * "aa*" -> false * "AaA" -> false * "aAa" -> true * "AA*" -> false * "AAa" -> true * where '*' == any char */ private static boolean camelBackWordBreak(char prev, char c, char next) { if (prev == 0) { // start of string return false; } if (Character.isLowerCase(c)) { return false; } if (Character.isLowerCase(prev)) { return true; } if (next == 0) { // end of string return false; } return Character.isLowerCase(next); } /** * Convert the specified "all caps" string to a "camel back" string: * "LARGE_PROJECT" -> "LargeProject" * Capitalize the first letter. */ public static String convertAllCapsToCamelBack(String allCapsString) { return convertAllCapsToCamelBack(allCapsString, true); } /** * Convert the specified "all caps" string to a "camel back" string: * "LARGE_PROJECT" -> "largeProject" * Optionally capitalize the first letter. */ public static String convertAllCapsToCamelBack(String allCapsString, boolean capitalizeFirstLetter) { int len = allCapsString.length(); if (len == 0) { return ""; } char prev = 0; char c = 0; StringBuffer sb = new StringBuffer(len); for (int i = 0; i < len; i++) { // NB: start at 1 prev = c; c = allCapsString.charAt(i); if (c == '_') { continue; } if (sb.length() == 0) { if (capitalizeFirstLetter) { sb.append(Character.toUpperCase(c)); } else { sb.append(Character.toLowerCase(c)); } } else { if (prev == '_') { sb.append(Character.toUpperCase(c)); } else { sb.append(Character.toLowerCase(c)); } } } return sb.toString(); } // ********** constructor ********** /** * Suppress default constructor, ensuring non-instantiability. */ private StringTools() { super(); throw new UnsupportedOperationException(); } //************* comparison ************** /** * Calculates * * @param string1 * @param string2 * @return */ public static double calculateHighestMatchWeight(String string1,String string2) { return calculateHighestMatchWeight(string1, string2, new char[0], MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @return */ public static double calculateHighestMatchWeight(String string1, String string2, char ignoreCharacter) { return calculateHighestMatchWeight(string1, string2, new char[] { ignoreCharacter }, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @param minimumMatchingLetterCount * @return */ public static double calculateHighestMatchWeight(String string1, String string2, char ignoreCharacter, int minimumMatchingLetterCount) { return calculateHighestMatchWeight(string1, string2, new char[] { ignoreCharacter }, minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @return */ public static double calculateHighestMatchWeight(String string1, String string2, char[] ignoreCharacters) { return calculateHighestMatchWeight(string1, string2, ignoreCharacters, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @param minimumMatchingLetterCount * @return */ public static double calculateHighestMatchWeight(String string1, String string2, char[] ignoreCharacters, int minimumMatchingLetterCount) { if ((string1 == null) || (string2 == null)) return 0.0; // Test 1: Look if they are equals if (string1.equals(string2)) { return 1.0; } // Test 2: Look if string1 and string2 are the same except for the list of // characters to ignore if (equals(string1, string2, ignoreCharacters)) { double top = string2.length(); double bottom = string1.length(); return top/bottom; } // Test 3: Look if string2 is a substring of string1 if (string1.indexOf(string2) > -1) { double top = string2.length(); double bottom = string1.length(); return top/bottom; } // Test 4: Look if a part - continuous - of string2 is a substring of string1 int weight = weightContainsPartContinuous(string1, string2, ignoreCharacters, minimumMatchingLetterCount); if (weight >= minimumMatchingLetterCount) { double weight1 = (double) weight / (double) string1.length(); double weight2 = (double) weight / (double) string2.length(); return (weight1 + weight2) / 2; } // Test 5: Find all the substrings and then the common letters between // string1 and string2 and return the count weight = weightContainsPart(string1, string2, ignoreCharacters, minimumMatchingLetterCount); if (weight >= minimumMatchingLetterCount) { double weight1 = (double) weight / (double) string1.length(); double weight2 = (double) weight / (double) string2.length(); return (weight1 + weight2) / 2; } return 0.0; } /** * Calculates * * @param string1 * @param string2 * @param minimumMatchingLetterCount * @return */ public static double calculateHighestMatchWeight(String string1, String string2, int minimumMatchingLetterCount) { return calculateHighestMatchWeight(string1, string2, new char[0], minimumMatchingLetterCount); } /** * Calculates the maximum weight between the given two strings. The case is * not used. * <p> * The weight can be one of the following: * <ul> * <li>{@link #NO_MATCH} No match was found * <li>{@link #STRING_CONTAINS_PART_OF_THE_OTHER} * <li>{@link #STRING_CONTAINS_THE_OTHER} * <li>{@link #STRINGS_SUBSET_NON_CONTINUOUS} * <li>{@link #STRINGS_SUBSET_CONTINUOUS} * <li>{@link #STRINGS_EQUAL_WITH_DELIMITOR} Both strings are equals by * ignoring one character * <li>{@link #STRINGS_EQUAL} Both strings are equals * </ul> * * @param string1 The string that is used to verify if the sub-string * could be the equals, contained or a sub-set * @param string2 The string to use to check if it is equal, contained or * partially contained in the main string * @return The weight of the match */ public static int calculateMatchWeight(String string1, String string2) { return calculateMatchWeight(string1, string2, new char[0], MINIMUM_MATCHING_LETTER); } /** * Calculates the maximum weight between the given two strings. The case is * not used. * <p> * The weight can be one of the following: * <ul> * <li>{@link #NO_MATCH} No match was found * <li>{@link #STRING_CONTAINS_PART_OF_THE_OTHER} * <li>{@link #STRING_CONTAINS_THE_OTHER} * <li>{@link #STRINGS_SUBSET_CONTINUOUS} * <li>{@link #STRINGS_EQUAL_WITH_DELIMITOR} Both strings are equals by * ignoring one character * <li>{@link #STRINGS_EQUAL} Both strings are equals * </ul> * * @param string1 The string that is used to verify if the sub-string * could be the equals, contained or a sub-set * @param string2 The string to use to check if it is equal, contained or * partially contained in the main string * @param ignoreCharacter The character that can be ignored during the * calculation or '\0' to not ignore any character * @return The weight of the match */ public static int calculateMatchWeight(String string1, String string2, char ignoreCharacter) { return calculateMatchWeight(string1, string2, new char[] { ignoreCharacter }, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @param minimumMatchingLetterCount * @return */ public static int calculateMatchWeight(String string1, String string2, char ignoreCharacter, int minimumMatchingLetterCount) { return calculateMatchWeight(string1, string2, new char[] { ignoreCharacter }, minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @return */ public static int calculateMatchWeight(String string1, String string2, char[] ignoreCharacters) { return calculateMatchWeight(string1, string2, ignoreCharacters, MINIMUM_MATCHING_LETTER); } /** * Calculates the maximum weight between the given two strings. The case is * not used. * <p> * The weight can be one of the following: * <ul> * <li>{@link #NO_MATCH} No match was found * <li>{@link #STRING_CONTAINS_PART_OF_THE_OTHER} * <li>{@link #STRING_CONTAINS_THE_OTHER} * <li>{@link #STRINGS_SUBSET_CONTINUOUS} * <li>{@link #STRINGS_EQUAL_WITH_DELIMITOR} Both strings are equals by * ignoring one character * <li>{@link #STRINGS_EQUAL} Both strings are equals * </ul> * * @param string1 The string that is used to verify if the sub-string * could be the equals, contained or a sub-set * @param string2 The string to use to check if it is equal, contained or * partially contained in the main string * @param ignoreCharacter The character that can be ignored during the * calculation or '\0' to not ignore any character * @return The weight of the match */ public static int calculateMatchWeight(String string1, String string2, char[] ignoreCharacters, int minimumMatchingLetterCount) { if ((string1 == null) || (string2 == null)) return NO_MATCH; // Test 1: Look if they are equals if (string1.equals(string2)) { return STRINGS_EQUAL; } // Test 2: Look if they are the same except for the list of characters to // ignore if (equals(string1, string2, ignoreCharacters)) { return STRINGS_EQUAL_WITH_IGNORE_CHARACTERS; } // Test 3: Look if string2 is a substring of string1 if (string1.indexOf(string2) > -1) { return STRING_CONTAINS; } // Test 4: Look if a part - continuous - of string2 is a substring of string1 if (containsPartContinuous(string1, string2, ignoreCharacters, minimumMatchingLetterCount)) { return STRING_CONTAINS_PART_CONTINUOUS; } // Test 5: Look if a part of string2 is a substring of string1 if (containsPart(string1, string2, ignoreCharacters, minimumMatchingLetterCount)) { return STRING_CONTAINS_PART; } return NO_MATCH; } /** * Calculates * * @param string1 * @param string2 * @param minimumMatchingLetterCount * @return */ public static int calculateMatchWeight(String string1, String string2, int minimumMatchingLetterCount) { return calculateMatchWeight(string1, string2, new char[0], minimumMatchingLetterCount); } /** * Calculates the maximum weight between the given two strings. The case is * not used. * <p> * The weight can be one of the following: * <ul> * <li>{@link #NO_MATCH} No match was found * <li>{@link #STRING_CONTAINS_PART_OF_THE_OTHER} * <li>{@link #STRING_CONTAINS_THE_OTHER} * <li>{@link #STRINGS_SUBSET_NON_CONTINUOUS} * <li>{@link #STRINGS_SUBSET_CONTINUOUS} * <li>{@link #STRINGS_EQUAL_WITH_DELIMITOR} Both strings are equals by * ignoring one character * <li>{@link #STRINGS_EQUAL} Both strings are equals * </ul> * * @param string1 The string that is used to verify if the sub-string * could be the equals, contained or a sub-set * @param string2 The string to use to check if it is equal, contained * or partially contained in the main string * @return The weight of the match */ public static int calculateMatchWeightIgnoreCase(String string1, String string2) { return calculateMatchWeightIgnoreCase(string1, string2, new char[0], MINIMUM_MATCHING_LETTER); } /** * Calculates the maximum weight between the given two strings. The case is * not used. * <p> * The weight can be one of the following: * <ul> * <li>{@link #NO_MATCH} No match was found * <li>{@link #STRING_CONTAINS_PART_OF_THE_OTHER} * <li>{@link #STRING_CONTAINS_THE_OTHER} * <li>{@link #STRINGS_SUBSET_CONTINUOUS} * <li>{@link #STRINGS_EQUAL_WITH_DELIMITOR} Both strings are equals by * ignoring one character * <li>{@link #STRINGS_EQUAL} Both strings are equals * </ul> * * @param string1 The string that is used to verify if the sub-string * could be the equals, contained or a sub-set * @param string2 The string to use to check if it is equal, contained * or partially contained in the main string * @param ignoreCharacter The character that can be ignored during the * calculation or '\0' to not ignore any character * @return The weight of the match */ public static int calculateMatchWeightIgnoreCase(String string1, String string2, char ignoreCharacter) { return calculateMatchWeightIgnoreCase(string1, string2, new char[] { ignoreCharacter }, MINIMUM_MATCHING_LETTER); } /** * Calculates the maximum weight between the given two strings. The case is * not used. * <p> * The weight can be one of the following: * <ul> * <li>{@link #NO_MATCH} No match was found * <li>{@link #STRING_CONTAINS_PART_OF_THE_OTHER} * <li>{@link #STRING_CONTAINS_THE_OTHER} * <li>{@link #STRINGS_SUBSET_CONTINUOUS} * <li>{@link #STRINGS_EQUAL_WITH_DELIMITOR} Both strings are equals by * ignoring one character * <li>{@link #STRINGS_EQUAL} Both strings are equals * </ul> * * @param string1 The string that is used to verify if the sub-string * could be the equals, contained or a sub-set * @param string2 The string to use to check if it is equal, contained * or partially contained in the main string * @param ignoreCharacter The character that can be ignored during the * calculation or '\0' to not ignore any character * @return The weight of the match */ public static int calculateMatchWeightIgnoreCase(String string1, String string2, char[] ignoreCharacters) { return calculateMatchWeightIgnoreCase(string1, string2, ignoreCharacters, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @param minimumMatchingLetterCount * @return */ public static int calculateMatchWeightIgnoreCase(String string1, String string2, char[] ignoreCharacters, int minimumMatchingLetterCount) { if ((string1 == null) || (string2 == null)) return NO_MATCH; return calculateMatchWeight(string1.toLowerCase(), string2.toLowerCase(), ignoreCharacters, minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param minimumMatchingLetterCount * @return */ public static int calculateMatchWeightIgnoreCase(String string1, String string2, int minimumMatchingLetterCount) { return calculateMatchWeightIgnoreCase(string1, string2, new char[0], minimumMatchingLetterCount); } /** * Determines whether the given two strings are equals. A <code>null</code> * string is also checked. * * @param string1 The first string to compare * @param string2 The second string to compare * @return <code>true</code> if both string are equals; <code>false</code> if * one of them is <code>null</code> or both are <code>null</code> */ public static boolean equals(String string1, String string2) { return (string1 != null) && string1.equals(string2) || (string2 != null) && string2.equals(string1); } /** * Determines if the first string and the second string are the same minus * ignoring the given character. * <p> * Example:<br> * equals("INT_DOUBLE", "_INT_DOUBLE_", '_') returns <code>true</code><br> * * @param string1 The first string to compare * @param string2 The second string to compare * @param ignoreCharacter The character to ignore when comparing the strings * @return <code>true</code> if both strings are equivalent; <code>false</code> * otherwise */ public static boolean equals(String string1, String string2, char ignoreCharacter) { return equals(string1, string2, new char[] { ignoreCharacter }); } /** * Determines if the first string and the second string are the same minus * the given list of characters to ignore. * <p> * Example:<br> * equals("INT_DOUBLE", "_INT_DOUBLE_", '_') returns <code>true</code><br> * * @param string1 The first string to compare * @param string2 The second string to compare * @param ignoreCharacters The list of characters to ignore when comparing * the strings * @return <code>true</code> if both strings are equivalent; <code>false</code> * otherwise */ public static boolean equals(String string1, String string2, char[] ignoreCharacters) { if ((string1 == null) || (string2 == null)) return false; for (int index = 0; index < ignoreCharacters.length; index++) { String character = String.valueOf(ignoreCharacters[index]); string1 = string1.replaceAll(character, ""); string2 = string2.replaceAll(character, ""); } return string1.equals(string2); } /** * Determines whether the given two strings are equals by ignoring the case * sensitivity. A <code>null</code> string is also checked. * * @param string1 The first string to compare * @param string2 The second string to compare * @return <code>true</code> if both string are equals; <code>false</code> if * one of them is <code>null</code> or both are <code>null</code> */ public static boolean equalsIgnoreCase(String string1, String string2) { return (string1 != null) && string1.equalsIgnoreCase(string2) || (string2 != null) && string2.equalsIgnoreCase(string1); } /** * Determines if the first string and the second string are the same minus * the given list of characters to ignore. * <p> * Example:<br> * equals("INT_DOUBLE", "_INT_double_", '_') returns <code>true</code><br> * * @param string1 The first string to compare * @param string2 The second string to compare * @param ignoreCharacter The character to ignore when comparing the strings * @return <code>true</code> if both strings are equivalent; <code>false</code> * otherwise * @return <code>true</code> if both strings are equivalent; <code>false</code> * otherwise */ public static boolean equalsIgnoreCase(String string1, String string2, char ignoreCharacter) { if ((string1 == null) || (string2 == null)) return false; return equals(string1.toLowerCase(), string2.toLowerCase(), new char[] { ignoreCharacter }); } /** * Determines if the first string and the second string are the same minus * the given list of characters to ignore. * <p> * Example:<br> * equals("INT_DOUBLE", "_INT_DOUBLE_", '_') returns <code>true</code><br> * * @param string1 The first string to compare * @param string2 The second string to compare * @param ignoreCharacters The list of characters to ignore when comparing * the strings * @return <code>true</code> if both strings are equivalent; <code>false</code> * otherwise */ public static boolean equalsIgnoreCase(String string1, String string2, char[] ignoreCharacters) { if ((string1 == null) || (string2 == null)) return false; return equals(string1.toLowerCase(), string2.toLowerCase(), ignoreCharacters); } /** * Calculates * * @param string1 * @param string2 * @return */ public static int weightContainsPart(String string1, String string2) { return weightContainsPart(string1, string2, new char[0], MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @return */ public static int weightContainsPart(String string1, String string2, char ignoreCharacter) { return weightContainsPart(string1, string2, new char[] { ignoreCharacter }, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @param minimumMatchingLetterCount * @return */ public static int weightContainsPart(String string1, String string2, char ignoreCharacter, int minimumMatchingLetterCount) { return weightContainsPart(string1, string2, new char[] { ignoreCharacter }, minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @return */ public static int weightContainsPart(String string1, String string2, char[] ignoreCharacters) { return weightContainsPart(string1, string2, ignoreCharacters, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @param minimumMatchingLetterCount * @return */ public static int weightContainsPart(String string1, String string2, char[] ignoreCharacters, int minimumMatchingLetterCount) { if ((string1 == null) || (string2 == null) ||(minimumMatchingLetterCount < 2)) return 0; if (string1.length() < minimumMatchingLetterCount) minimumMatchingLetterCount = string1.length(); string1 = removeIgnoreCharacters(string1, ignoreCharacters); string2 = removeIgnoreCharacters(string2, ignoreCharacters); Position[] positions = (string1.length() < string2.length()) ? weightContainsPartImp(string2, string1) : weightContainsPartImp(string1, string2); int count = 0; // TODO: Need to check the order and remove any substrings that are not // allowed. Here an example: string1=JohnPascalandAnuj and // string2=JohnAnujPascal, the result is 14 when it should be 10 for (int index = positions.length; --index >= 0;) { if (Position.canAddWeight(positions, index)) { count += positions[index].length; } else if (index > 0) { Position[] oldPositions = positions; positions = new Position[positions.length - 1]; System.arraycopy(oldPositions, 0, positions, 0, index); System.arraycopy(oldPositions, index, positions, index, oldPositions.length - index - 1); } } if (count < minimumMatchingLetterCount) return 0; return count; } /** * Calculates * * @param string1 * @param string2 * @param minimumMatchingLetterCount * @return */ public static int weightContainsPart(String string1, String string2, int minimumMatchingLetterCount) { return weightContainsPart(string1, string2, new char[0], minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @return */ public static int weightContainsPartContinuous(String string1, String string2) { return weightContainsPartContinuous(string1, string2, new char[0], MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @return */ public static int weightContainsPartContinuous(String string1, String string2, char ignoreCharacter) { return weightContainsPartContinuous(string1, string2, new char[] { ignoreCharacter }, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @param minimumMatchingLetterCount * @return */ public static int weightContainsPartContinuous(String string1, String string2, char ignoreCharacter, int minimumMatchingLetterCount) { return weightContainsPartContinuous(string1, string2, new char[] { ignoreCharacter }, minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @return */ public static int weightContainsPartContinuous(String string1, String string2, char[] ignoreCharacters) { return weightContainsPartContinuous(string1, string2, ignoreCharacters, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @param minimumMatchingLetterCount * @return */ public static int weightContainsPartContinuous(String string1, String string2, char[] ignoreCharacters, int minimumMatchingLetterCount) { if ((string1 == null) || (string2 == null) || (minimumMatchingLetterCount < 2)) return 0; string1 = removeIgnoreCharacters(string1, ignoreCharacters); string2 = removeIgnoreCharacters(string2, ignoreCharacters); // if (string2.length() < minimumMatchingLetterCount) // minimumMatchingLetterCount = string2.length(); // // if (string1.length() < minimumMatchingLetterCount) // minimumMatchingLetterCount = Math.min(string1.length(), minimumMatchingLetterCount); return weightContainsPartContinuousImp(string1, string2, minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param minimumMatchingLetterCount * @return */ public static int weightContainsPartContinuous(String string1, String string2, int minimumMatchingLetterCount) { return weightContainsPartContinuous(string1, string2, new char[0], minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param minimumMatchingLetterCount * @return */ private static int weightContainsPartContinuousImp(String string1, String string2, int minimumMatchingLetterCount) { int currentLength = string2.length(); // Can't go smaller than the minimum count if (currentLength < minimumMatchingLetterCount) return 0; // First test to see if string2 is a substring of string1 if (string1.indexOf(string2) > -1) return currentLength; // Truncate on the left and start to test it recursively int weightLeft = weightContainsPartContinuousImp(string1, string2.substring(1, currentLength), minimumMatchingLetterCount); // Truncate on the right and start to test it recursively int weightRight = weightContainsPartContinuousImp(string1, string2.substring(0, currentLength - 1), minimumMatchingLetterCount); return Math.max(weightLeft, weightRight); } /** * Calculates * * @param string1 * @param string2 * @return */ private static Position[] weightContainsPartImp(String string1, String string2) { int currentLength = string2.length(); // Can't go smaller than the minimum count if (currentLength == 0) return new Position[0]; // First test to see if string2 is a substring of string1 int index = string1.indexOf(string2); if (index > -1) { Position position = new Position(index, currentLength); int string1Length = string1.length(); // Gather all the locations of string2 in string1 while ((index > -1) && (string1Length - index - 2*currentLength >= 0)) { index = string1.indexOf(string2, index + currentLength); if (index > -1) position.add(index); } return new Position[] { position }; } // One letter string was testing with indexOf(String) if (currentLength > 1) { // Truncate to the left and start to test it recursively String leftSubString = string2.substring(1, currentLength); Position[] leftPos = weightContainsPartImp(string1, leftSubString); // Truncate to the right and start to test it recursively String rightSubString = string2.substring(0, currentLength - 1); Position[] rightPos = weightContainsPartImp(string1, rightSubString); return Position.merge(leftPos, rightPos); } return new Position[0]; } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @return */ public static boolean contains(String string1, String string2, char ignoreCharacter) { return contains(string1, string2, new char[] { ignoreCharacter }); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @return */ public static boolean contains(String string1, String string2, char[] ignoreCharacters) { if ((string1 == null) || (string2 == null)) return false; string1 = removeIgnoreCharacters(string1, ignoreCharacters); string2 = removeIgnoreCharacters(string2, ignoreCharacters); return string1.indexOf(string2) > -1; } /** * Checks to see if the given collection contains the specified string. The * check is not case-sensitive and <code>toString()</code> is used to retrieve * a string representation for each object. The collection can contain * <code>null</code> values. * * @param iter The collection of objects to be tested with the given string * @param string The string to be compared (case ignored) with all the objects * contained in the given collection * @return <code>true</code> if the collection contains the given string, * case ignored; <code>false</code> otherwise */ public static boolean containsIgnoreCase(Collection collection, String string) { return containsIgnoreCase(collection.iterator(), string); } /** * Checks to see if the given collection contains the specified string. The * check is not case-sensitive and <code>toString()</code> is used to retrieve * a string representation for each object. The collection can contain * <code>null</code> values. * * @param iter The <code>Iterator</code> over the collection of objects to be * tested with the given string * @param string The string to be compared (case ignored) with all the objects * contained in the collection iterated with the given <code>Iterator</code> * @return <code>true</code> if the collection iterated with the given * <code>Iterator</code> contains the given string, case ignored; * <code>false</code> otherwise */ public static boolean containsIgnoreCase(Iterator iter, String string) { if (string == null) return false; while (iter.hasNext()) { Object item = iter.next(); if ((item != null) && string.equalsIgnoreCase(item.toString())) return true; } return false; } /** * Calculates * * @param string1 * @param string2 * @return */ public static boolean containsPart(String string1, String string2) { return containsPart(string1, string2, new char[0], MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @return */ public static boolean containsPart(String string1, String string2, char ignoreCharacter) { return containsPart(string1, string2, new char[] { ignoreCharacter }, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @param minimumMatchingLetterCount * @return */ public static boolean containsPart(String string1, String string2, char ignoreCharacter, int minimumMatchingLetterCount) { return containsPart(string1, string2, new char[] { ignoreCharacter }, minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @return */ public static boolean containsPart(String string1, String string2, char[] ignoreCharacters) { return containsPart(string1, string2, ignoreCharacters, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @param minimumMatchingLetterCount * @return */ public static boolean containsPart(String string1, String string2, char[] ignoreCharacters, int minimumMatchingLetterCount) { return weightContainsPart(string1, string2, ignoreCharacters, minimumMatchingLetterCount) >= minimumMatchingLetterCount; } /** * Calculates * * @param string1 * @param string2 * @param minimumMatchingLetterCount * @return */ public static boolean containsPart(String string1, String string2, int minimumMatchingLetterCount) { return containsPart(string1, string2, new char[0], minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @return */ public static boolean containsPartContinuous(String string1, String string2) { return containsPartContinuous(string1, string2, new char[0], MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @return */ public static boolean containsPartContinuous(String string1, String string2, char ignoreCharacter) { return containsPartContinuous(string1, string2, new char[] { ignoreCharacter }, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacter * @param minimumMatchingLetterCount * @return */ public static boolean containsPartContinuous(String string1, String string2, char ignoreCharacter, int minimumMatchingLetterCount) { return containsPartContinuous(string1, string2, new char[0], minimumMatchingLetterCount); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @return */ public static boolean containsPartContinuous(String string1, String string2, char[] ignoreCharacters) { return containsPartContinuous(string1, string2, ignoreCharacters, MINIMUM_MATCHING_LETTER); } /** * Calculates * * @param string1 * @param string2 * @param ignoreCharacters * @param minimumMatchingLetterCount * @return */ public static boolean containsPartContinuous(String string1, String string2, char[] ignoreCharacters, int minimumMatchingLetterCount) { return weightContainsPartContinuous(string1, string2, ignoreCharacters, minimumMatchingLetterCount) >= minimumMatchingLetterCount; } /** * Calculates * * @param string1 * @param string2 * @param minimumMatchingLetterCount * @return */ public static boolean containsPartContinuous(String string1, String string2, int minimumMatchingLetterCount) { return containsPartContinuous(string1, string2, new char[0], minimumMatchingLetterCount); } /** * Removes * * @param string * @param ignoreCharacters * @return */ public static String removeIgnoreCharacters(String string, char[] ignoreCharacters) { if ((string == null) || (string.length() == 0) || (ignoreCharacters.length == 0)) { return string; } for (int index = 0; index < ignoreCharacters.length; index++) { String character = String.valueOf(ignoreCharacters[index]); string = string.replaceAll(character, ""); } return string; } /** * This <code>Position</code> class is used by * {@link StringUtility#weightContainsPartImp(String, String)}. */ private static class Position { int indices[]; int length; Position() { super(); indices = new int[1]; indices[0] = -1; } Position(int index, int length) { this(); indices[0] = index; this.length = length; } private static List asList(Position[] array) { ArrayList list = new ArrayList(array.length); for (int index = 0; index < array.length; index++) list.add(array[index]); return list; } private static boolean canAddWeight(Position[] positions, int index) { // The last one can always be added if (index + 1 == positions.length) return true; Position position1 = positions[index]; Position position2 = positions[index + 1]; for (int index1 = 0; index1 < position1.indices.length; index1++) { for (int index2 = 0; index2 < position2.indices.length; index2++) { int posIndex1 = position1.indices[index1]; int posIndex2 = position2.indices[index2]; if (posIndex1 > posIndex2) return true; } } return false; } static Position[] merge(Position[] positions1, Position[] positions2) { List positions1List = asList(positions1); List positions2List = asList(positions2); Iterator iter1 = positions1List.iterator(); while (iter1.hasNext()) { Position firstPosition = (Position) iter1.next(); Iterator iter2 = positions2List.iterator(); while (iter2.hasNext()) { Position secondPosition = (Position) iter2.next(); if (firstPosition.inside(secondPosition)) iter2.remove(); else if (secondPosition.inside(firstPosition)) iter1.remove(); } } // Return the result of the merge, which is the remaining of both arrays Position[] positions = new Position[positions1List.size() + positions2List.size()]; System.arraycopy(positions1List.toArray(), 0, positions, 0, positions1List.size()); System.arraycopy(positions2List.toArray(), 0, positions, positions1List.size(), positions2List.size()); return positions; } void add(int index) { ensureCapacity(); indices[indices.length - 1] = index; } private void ensureCapacity() { int[] oldIndices = indices; indices = new int[oldIndices.length + 1]; System.arraycopy(oldIndices, 0, indices, 0, oldIndices.length); } boolean inside(Position position) { for (int index1 = 0; index1 < indices.length; index1++) { for (int index2 = 0; index2 < position.indices.length; index2++) { if (indices[index1] + length >= position.indices[index2] + position.length && indices[index1] + length >= position.length && indices[index1] <= position.indices[index2]) { return true; } } } return false; } } }