From acc7b356ee8310c3f4fc694426bec365d266c370 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 21 Nov 2017 22:10:48 +0000 Subject: [PATCH] add forbidden api checks for non-Locale toLowerCase and toUpperCase git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1815994 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/sl/usermodel/ShapeType.java | 4 +- .../poi/ss/format/CellDateFormatter.java | 9 +- .../apache/poi/ss/format/CellFormatPart.java | 3 +- src/java/org/apache/poi/util/StringUtil.java | 916 +++++++++--------- .../usermodel/helpers/XSSFPasswordHelper.java | 4 +- .../devtools/forbidden-signatures.txt | 6 +- .../org/apache/poi/hwpf/dev/RecordUtil.java | 13 +- 7 files changed, 496 insertions(+), 459 deletions(-) diff --git a/src/java/org/apache/poi/sl/usermodel/ShapeType.java b/src/java/org/apache/poi/sl/usermodel/ShapeType.java index b2cb73b0f5..31ab5011ae 100644 --- a/src/java/org/apache/poi/sl/usermodel/ShapeType.java +++ b/src/java/org/apache/poi/sl/usermodel/ShapeType.java @@ -17,6 +17,8 @@ package org.apache.poi.sl.usermodel; +import org.apache.poi.util.StringUtil; + /** * known preset shape geometries in PresentationML */ @@ -299,7 +301,7 @@ public enum ShapeType { toLower = false; continue; } - sb.append(toLower ? Character.toLowerCase(ch) : Character.toUpperCase(ch)); + sb.append(toLower ? StringUtil.toLowerCase(ch) : StringUtil.toUpperCase(ch)); toLower = true; } diff --git a/src/java/org/apache/poi/ss/format/CellDateFormatter.java b/src/java/org/apache/poi/ss/format/CellDateFormatter.java index 375e1fbae3..42b71bddb1 100644 --- a/src/java/org/apache/poi/ss/format/CellDateFormatter.java +++ b/src/java/org/apache/poi/ss/format/CellDateFormatter.java @@ -27,6 +27,7 @@ import java.util.Locale; import java.util.regex.Matcher; import org.apache.poi.util.LocaleUtil; +import org.apache.poi.util.StringUtil; /** * Formats a date value. @@ -110,9 +111,9 @@ public class CellDateFormatter extends CellFormatter { // am/pm marker mStart = -1; showAmPm = true; - showM = Character.toLowerCase(part.charAt(1)) == 'm'; + showM = StringUtil.toLowerCase(part.charAt(1)).equals("m"); // For some reason "am/pm" becomes AM or PM, but "a/p" becomes a or p - amPmUpper = showM || Character.isUpperCase(part.charAt(0)); + amPmUpper = showM || StringUtil.isUpperCase(part.charAt(0)); return "a"; } @@ -199,11 +200,11 @@ public class CellDateFormatter extends CellFormatter { if (!doneAm) { if (showAmPm) { if (amPmUpper) { - toAppendTo.append(Character.toString(ch).toUpperCase(LocaleUtil.getUserLocale())); + toAppendTo.append(StringUtil.toUpperCase(ch)); if (showM) toAppendTo.append('M'); } else { - toAppendTo.append(Character.toString(ch).toLowerCase(LocaleUtil.getUserLocale())); + toAppendTo.append(StringUtil.toLowerCase(ch)); if (showM) toAppendTo.append('m'); } diff --git a/src/java/org/apache/poi/ss/format/CellFormatPart.java b/src/java/org/apache/poi/ss/format/CellFormatPart.java index 951b5b8861..6ddb07efc8 100644 --- a/src/java/org/apache/poi/ss/format/CellFormatPart.java +++ b/src/java/org/apache/poi/ss/format/CellFormatPart.java @@ -18,6 +18,7 @@ package org.apache.poi.ss.format; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.util.LocaleUtil; +import org.apache.poi.util.StringUtil; import javax.swing.*; @@ -341,7 +342,7 @@ public class CellFormatPart { char c1 = repl.charAt(0); char c2 = 0; if (repl.length() > 1) - c2 = Character.toLowerCase(repl.charAt(1)); + c2 = StringUtil.toLowerCase(repl.charAt(1)).charAt(0); switch (c1) { case '@': diff --git a/src/java/org/apache/poi/util/StringUtil.java b/src/java/org/apache/poi/util/StringUtil.java index 32b5acf9ab..8ba6be56ff 100644 --- a/src/java/org/apache/poi/util/StringUtil.java +++ b/src/java/org/apache/poi/util/StringUtil.java @@ -23,7 +23,7 @@ import java.util.Iterator; import java.util.Map; /** - * Collection of string handling utilities + * Collection of string handling utilities */ @Internal public class StringUtil { @@ -36,31 +36,31 @@ public class StringUtil { public static final Charset WIN_1252 = Charset.forName("cp1252"); public static final Charset BIG5 = Charset.forName("Big5"); - private static Map msCodepointToUnicode; + private static Map msCodepointToUnicode; private StringUtil() { // no instances of this class } /** - * Given a byte array of 16-bit unicode characters in Little Endian - * format (most important byte last), return a Java String representation - * of it. - * + * Given a byte array of 16-bit unicode characters in Little Endian + * format (most important byte last), return a Java String representation + * of it. + *

* { 0x16, 0x00 } -0x16 * - * @param string the byte array to be converted - * @param offset the initial offset into the - * byte array. it is assumed that string[ offset ] and string[ offset + - * 1 ] contain the first 16-bit unicode character - * @param len the length of the final string + * @param string the byte array to be converted + * @param offset the initial offset into the + * byte array. it is assumed that string[ offset ] and string[ offset + + * 1 ] contain the first 16-bit unicode character + * @param len the length of the final string * @return the converted string, never null. - * @exception ArrayIndexOutOfBoundsException if offset is out of bounds for - * the byte array (i.e., is negative or is greater than or equal to - * string.length) - * @exception IllegalArgumentException if len is too large (i.e., - * there is not enough data in string to create a String of that - * length) + * @throws ArrayIndexOutOfBoundsException if offset is out of bounds for + * the byte array (i.e., is negative or is greater than or equal to + * string.length) + * @throws IllegalArgumentException if len is too large (i.e., + * there is not enough data in string to create a String of that + * length) */ public static String getFromUnicodeLE( final byte[] string, @@ -78,17 +78,19 @@ public class StringUtil { } /** - * Given a byte array of 16-bit unicode characters in little endian - * format (most important byte last), return a Java String representation - * of it. - * + * Given a byte array of 16-bit unicode characters in little endian + * format (most important byte last), return a Java String representation + * of it. + *

* { 0x16, 0x00 } -0x16 * - * @param string the byte array to be converted + * @param string the byte array to be converted * @return the converted string, never null */ public static String getFromUnicodeLE(byte[] string) { - if(string.length == 0) { return ""; } + if (string.length == 0) { + return ""; + } return getFromUnicodeLE(string, 0, string.length / 2); } @@ -134,7 +136,7 @@ public class StringUtil { *

  • byte[]/char[] characterData
  • * * For this encoding, the is16BitFlag is always present even if nChars==0. - * + *

    * This structure is also known as a XLUnicodeString. */ public static String readUnicodeString(LittleEndianInput in) { @@ -146,6 +148,7 @@ public class StringUtil { } return readUnicodeLE(in, nChars); } + /** * InputStream in is expected to contain: *

      @@ -165,6 +168,7 @@ public class StringUtil { } return readUnicodeLE(in, nChars); } + /** * OutputStream out will get: *
        @@ -185,114 +189,116 @@ public class StringUtil { putCompressedUnicode(value, out); } } - /** - * OutputStream out will get: - *
          - *
        1. byte is16BitFlag
        2. - *
        3. byte[]/char[] characterData
        4. - *
        - * For this encoding, the is16BitFlag is always present even if nChars==0. - *
        - * This method should be used when the nChars field is not stored - * as a ushort immediately before the is16BitFlag. Otherwise, {@link - * #writeUnicodeString(LittleEndianOutput, String)} can be used. - */ - public static void writeUnicodeStringFlagAndData(LittleEndianOutput out, String value) { - boolean is16Bit = hasMultibyte(value); - out.writeByte(is16Bit ? 0x01 : 0x00); - if (is16Bit) { - putUnicodeLE(value, out); - } else { - putCompressedUnicode(value, out); - } - } - - /** - * @return the number of bytes that would be written by {@link #writeUnicodeString(LittleEndianOutput, String)} - */ - public static int getEncodedSize(String value) { - int result = 2 + 1; - result += value.length() * (StringUtil.hasMultibyte(value) ? 2 : 1); - return result; - } - - /** - * Takes a unicode (java) string, and returns it as 8 bit data (in ISO-8859-1 - * codepage). - * (In Excel terms, write compressed 8 bit unicode) - * - * @param input the String containing the data to be written - * @param output the byte array to which the data is to be written - * @param offset an offset into the byte arrat at which the data is start - * when written - */ - public static void putCompressedUnicode(String input, byte[] output, int offset) { - byte[] bytes = input.getBytes(ISO_8859_1); - System.arraycopy(bytes, 0, output, offset, bytes.length); - } - - public static void putCompressedUnicode(String input, LittleEndianOutput out) { - byte[] bytes = input.getBytes(ISO_8859_1); - out.write(bytes); - } - - /** - * Takes a unicode string, and returns it as little endian (most - * important byte last) bytes in the supplied byte array. - * (In Excel terms, write uncompressed unicode) - * - * @param input the String containing the unicode data to be written - * @param output the byte array to hold the uncompressed unicode, should be twice the length of the String - * @param offset the offset to start writing into the byte array - */ - public static void putUnicodeLE(String input, byte[] output, int offset) { - byte[] bytes = input.getBytes(UTF16LE); - System.arraycopy(bytes, 0, output, offset, bytes.length); - } - public static void putUnicodeLE(String input, LittleEndianOutput out) { - byte[] bytes = input.getBytes(UTF16LE); - out.write(bytes); - } - - public static String readUnicodeLE(LittleEndianInput in, int nChars) { - byte[] bytes = IOUtils.safelyAllocate(nChars*2, MAX_RECORD_LENGTH); + + /** + * OutputStream out will get: + *
          + *
        1. byte is16BitFlag
        2. + *
        3. byte[]/char[] characterData
        4. + *
        + * For this encoding, the is16BitFlag is always present even if nChars==0. + *
        + * This method should be used when the nChars field is not stored + * as a ushort immediately before the is16BitFlag. Otherwise, {@link + * #writeUnicodeString(LittleEndianOutput, String)} can be used. + */ + public static void writeUnicodeStringFlagAndData(LittleEndianOutput out, String value) { + boolean is16Bit = hasMultibyte(value); + out.writeByte(is16Bit ? 0x01 : 0x00); + if (is16Bit) { + putUnicodeLE(value, out); + } else { + putCompressedUnicode(value, out); + } + } + + /** + * @return the number of bytes that would be written by {@link #writeUnicodeString(LittleEndianOutput, String)} + */ + public static int getEncodedSize(String value) { + int result = 2 + 1; + result += value.length() * (StringUtil.hasMultibyte(value) ? 2 : 1); + return result; + } + + /** + * Takes a unicode (java) string, and returns it as 8 bit data (in ISO-8859-1 + * codepage). + * (In Excel terms, write compressed 8 bit unicode) + * + * @param input the String containing the data to be written + * @param output the byte array to which the data is to be written + * @param offset an offset into the byte arrat at which the data is start + * when written + */ + public static void putCompressedUnicode(String input, byte[] output, int offset) { + byte[] bytes = input.getBytes(ISO_8859_1); + System.arraycopy(bytes, 0, output, offset, bytes.length); + } + + public static void putCompressedUnicode(String input, LittleEndianOutput out) { + byte[] bytes = input.getBytes(ISO_8859_1); + out.write(bytes); + } + + /** + * Takes a unicode string, and returns it as little endian (most + * important byte last) bytes in the supplied byte array. + * (In Excel terms, write uncompressed unicode) + * + * @param input the String containing the unicode data to be written + * @param output the byte array to hold the uncompressed unicode, should be twice the length of the String + * @param offset the offset to start writing into the byte array + */ + public static void putUnicodeLE(String input, byte[] output, int offset) { + byte[] bytes = input.getBytes(UTF16LE); + System.arraycopy(bytes, 0, output, offset, bytes.length); + } + + public static void putUnicodeLE(String input, LittleEndianOutput out) { + byte[] bytes = input.getBytes(UTF16LE); + out.write(bytes); + } + + public static String readUnicodeLE(LittleEndianInput in, int nChars) { + byte[] bytes = IOUtils.safelyAllocate(nChars * 2, MAX_RECORD_LENGTH); in.readFully(bytes); return new String(bytes, UTF16LE); - } - - /** - * @return the encoding we want to use, currently hardcoded to ISO-8859-1 - */ - public static String getPreferredEncoding() { - return ISO_8859_1.name(); - } - - /** - * check the parameter has multibyte character - * - * @param value string to check - * @return boolean result true:string has at least one multibyte character - */ - public static boolean hasMultibyte(String value) { - if (value == null) - return false; - for (char c : value.toCharArray()) { - if (c > 0xFF) { - return true; - } - } - return false; - } - - /** - * Checks to see if a given String needs to be represented as Unicode - * - * @param value The string to look at. - * @return true if string needs Unicode to be represented. - */ - public static boolean isUnicodeString(final String value) { + } + + /** + * @return the encoding we want to use, currently hardcoded to ISO-8859-1 + */ + public static String getPreferredEncoding() { + return ISO_8859_1.name(); + } + + /** + * check the parameter has multibyte character + * + * @param value string to check + * @return boolean result true:string has at least one multibyte character + */ + public static boolean hasMultibyte(String value) { + if (value == null) + return false; + for (char c : value.toCharArray()) { + if (c > 0xFF) { + return true; + } + } + return false; + } + + /** + * Checks to see if a given String needs to be represented as Unicode + * + * @param value The string to look at. + * @return true if string needs Unicode to be represented. + */ + public static boolean isUnicodeString(final String value) { return !value.equals(new String(value.getBytes(ISO_8859_1), ISO_8859_1)); - } + } /** * Tests if the string starts with the specified prefix, ignoring case consideration. @@ -300,7 +306,7 @@ public class StringUtil { public static boolean startsWithIgnoreCase(String haystack, String prefix) { return haystack.regionMatches(true, 0, prefix, 0, prefix.length()); } - + /** * Tests if the string ends with the specified suffix, ignoring case consideration. */ @@ -310,320 +316,338 @@ public class StringUtil { return haystack.regionMatches(true, start, suffix, 0, length); } - /** - * An Iterator over an array of Strings. - */ - public static class StringsIterator implements Iterator { - private String[] strings = {}; - private int position; - public StringsIterator(String[] strings) { - if (strings != null) { - this.strings = strings.clone(); - } - } - - public boolean hasNext() { - return position < strings.length; - } - public String next() { - int ourPos = position++; - if(ourPos >= strings.length) { - throw new ArrayIndexOutOfBoundsException(ourPos); - } - return strings[ourPos]; - } - public void remove() {} - } - - - /** - * Some strings may contain encoded characters of the unicode private use area. - * Currently the characters of the symbol fonts are mapped to the corresponding - * characters in the normal unicode range. - * - * @param string the original string - * @return the string with mapped characters - * - * @see Private Use Area (symbol) - * @see Symbol font - Unicode alternatives for Greek and special characters in HTML - */ - public static String mapMsCodepointString(String string) { - if (string == null || string.isEmpty()) return string; - initMsCodepointMap(); - - StringBuilder sb = new StringBuilder(); - final int length = string.length(); - for (int offset = 0; offset < length; ) { - Integer msCodepoint = string.codePointAt(offset); - Integer uniCodepoint = msCodepointToUnicode.get(msCodepoint); - sb.appendCodePoint(uniCodepoint == null ? msCodepoint : uniCodepoint); - offset += Character.charCount(msCodepoint); - } - - return sb.toString(); - } - - public static synchronized void mapMsCodepoint(int msCodepoint, int unicodeCodepoint) { - initMsCodepointMap(); - msCodepointToUnicode.put(msCodepoint, unicodeCodepoint); - } - - private static synchronized void initMsCodepointMap() { - if (msCodepointToUnicode != null) return; - msCodepointToUnicode = new HashMap<>(); - int i=0xF020; - for (int ch : symbolMap_f020) { - msCodepointToUnicode.put(i++, ch); - } - i = 0xf0a0; - for (int ch : symbolMap_f0a0) { - msCodepointToUnicode.put(i++, ch); - } - } - - private static final int symbolMap_f020[] = { - ' ', // 0xf020 space - '!', // 0xf021 exclam - 8704, // 0xf022 universal - '#', // 0xf023 numbersign - 8707, // 0xf024 existential - '%', // 0xf025 percent - '&', // 0xf026 ampersand - 8717, // 0xf027 suchthat - '(', // 0xf028 parenleft - ')', // 0xf029 parentright - 8727, // 0xf02a asteriskmath - '+', // 0xf02b plus - ',', // 0xf02c comma - 8722, // 0xf02d minus sign (long -) - '.', // 0xf02e period - '/', // 0xf02f slash - '0', // 0xf030 0 - '1', // 0xf031 1 - '2', // 0xf032 2 - '3', // 0xf033 3 - '4', // 0xf034 4 - '5', // 0xf035 5 - '6', // 0xf036 6 - '7', // 0xf037 7 - '8', // 0xf038 8 - '9', // 0xf039 9 - ':', // 0xf03a colon - ';', // 0xf03b semicolon - '<', // 0xf03c less - '=', // 0xf03d equal - '>', // 0xf03e greater - '?', // 0xf03f question - 8773, // 0xf040 congruent - 913, // 0xf041 alpha (upper) - 914, // 0xf042 beta (upper) - 935, // 0xf043 chi (upper) - 916, // 0xf044 delta (upper) - 917, // 0xf045 epsilon (upper) - 934, // 0xf046 phi (upper) - 915, // 0xf047 gamma (upper) - 919, // 0xf048 eta (upper) - 921, // 0xf049 iota (upper) - 977, // 0xf04a theta1 (lower) - 922, // 0xf04b kappa (upper) - 923, // 0xf04c lambda (upper) - 924, // 0xf04d mu (upper) - 925, // 0xf04e nu (upper) - 927, // 0xf04f omicron (upper) - 928, // 0xf050 pi (upper) - 920, // 0xf051 theta (upper) - 929, // 0xf052 rho (upper) - 931, // 0xf053 sigma (upper) - 932, // 0xf054 tau (upper) - 933, // 0xf055 upsilon (upper) - 962, // 0xf056 simga1 (lower) - 937, // 0xf057 omega (upper) - 926, // 0xf058 xi (upper) - 936, // 0xf059 psi (upper) - 918, // 0xf05a zeta (upper) - '[', // 0xf05b bracketleft - 8765, // 0xf05c therefore - ']', // 0xf05d bracketright - 8869, // 0xf05e perpendicular - '_', // 0xf05f underscore - ' ', // 0xf060 radicalex (doesn't exist in unicode) - 945, // 0xf061 alpha (lower) - 946, // 0xf062 beta (lower) - 967, // 0xf063 chi (lower) - 948, // 0xf064 delta (lower) - 949, // 0xf065 epsilon (lower) - 966, // 0xf066 phi (lower) - 947, // 0xf067 gamma (lower) - 951, // 0xf068 eta (lower) - 953, // 0xf069 iota (lower) - 981, // 0xf06a phi1 (lower) - 954, // 0xf06b kappa (lower) - 955, // 0xf06c lambda (lower) - 956, // 0xf06d mu (lower) - 957, // 0xf06e nu (lower) - 959, // 0xf06f omnicron (lower) - 960, // 0xf070 pi (lower) - 952, // 0xf071 theta (lower) - 961, // 0xf072 rho (lower) - 963, // 0xf073 sigma (lower) - 964, // 0xf074 tau (lower) - 965, // 0xf075 upsilon (lower) - 982, // 0xf076 piv (lower) - 969, // 0xf077 omega (lower) - 958, // 0xf078 xi (lower) - 968, // 0xf079 psi (lower) - 950, // 0xf07a zeta (lower) - '{', // 0xf07b braceleft - '|', // 0xf07c bar - '}', // 0xf07d braceright - 8764, // 0xf07e similar '~' - ' ', // 0xf07f not defined - }; - - private static final int symbolMap_f0a0[] = { - 8364, // 0xf0a0 not defined / euro symbol - 978, // 0xf0a1 upsilon1 (upper) - 8242, // 0xf0a2 minute - 8804, // 0xf0a3 lessequal - 8260, // 0xf0a4 fraction - 8734, // 0xf0a5 infinity - 402, // 0xf0a6 florin - 9827, // 0xf0a7 club - 9830, // 0xf0a8 diamond - 9829, // 0xf0a9 heart - 9824, // 0xf0aa spade - 8596, // 0xf0ab arrowboth - 8591, // 0xf0ac arrowleft - 8593, // 0xf0ad arrowup - 8594, // 0xf0ae arrowright - 8595, // 0xf0af arrowdown - 176, // 0xf0b0 degree - 177, // 0xf0b1 plusminus - 8243, // 0xf0b2 second - 8805, // 0xf0b3 greaterequal - 215, // 0xf0b4 multiply - 181, // 0xf0b5 proportional - 8706, // 0xf0b6 partialdiff - 8729, // 0xf0b7 bullet - 247, // 0xf0b8 divide - 8800, // 0xf0b9 notequal - 8801, // 0xf0ba equivalence - 8776, // 0xf0bb approxequal - 8230, // 0xf0bc ellipsis - 9168, // 0xf0bd arrowvertex - 9135, // 0xf0be arrowhorizex - 8629, // 0xf0bf carriagereturn - 8501, // 0xf0c0 aleph - 8475, // 0xf0c1 Ifraktur - 8476, // 0xf0c2 Rfraktur - 8472, // 0xf0c3 weierstrass - 8855, // 0xf0c4 circlemultiply - 8853, // 0xf0c5 circleplus - 8709, // 0xf0c6 emptyset - 8745, // 0xf0c7 intersection - 8746, // 0xf0c8 union - 8835, // 0xf0c9 propersuperset - 8839, // 0xf0ca reflexsuperset - 8836, // 0xf0cb notsubset - 8834, // 0xf0cc propersubset - 8838, // 0xf0cd reflexsubset - 8712, // 0xf0ce element - 8713, // 0xf0cf notelement - 8736, // 0xf0d0 angle - 8711, // 0xf0d1 gradient - 174, // 0xf0d2 registerserif - 169, // 0xf0d3 copyrightserif - 8482, // 0xf0d4 trademarkserif - 8719, // 0xf0d5 product - 8730, // 0xf0d6 radical - 8901, // 0xf0d7 dotmath - 172, // 0xf0d8 logicalnot - 8743, // 0xf0d9 logicaland - 8744, // 0xf0da logicalor - 8660, // 0xf0db arrowdblboth - 8656, // 0xf0dc arrowdblleft - 8657, // 0xf0dd arrowdblup - 8658, // 0xf0de arrowdblright - 8659, // 0xf0df arrowdbldown - 9674, // 0xf0e0 lozenge - 9001, // 0xf0e1 angleleft - 174, // 0xf0e2 registersans - 169, // 0xf0e3 copyrightsans - 8482, // 0xf0e4 trademarksans - 8721, // 0xf0e5 summation - 9115, // 0xf0e6 parenlefttp - 9116, // 0xf0e7 parenleftex - 9117, // 0xf0e8 parenleftbt - 9121, // 0xf0e9 bracketlefttp - 9122, // 0xf0ea bracketleftex - 9123, // 0xf0eb bracketleftbt - 9127, // 0xf0ec bracelefttp - 9128, // 0xf0ed braceleftmid - 9129, // 0xf0ee braceleftbt - 9130, // 0xf0ef braceex - ' ', // 0xf0f0 not defined - 9002, // 0xf0f1 angleright - 8747, // 0xf0f2 integral - 8992, // 0xf0f3 integraltp - 9134, // 0xf0f4 integralex - 8993, // 0xf0f5 integralbt - 9118, // 0xf0f6 parenrighttp - 9119, // 0xf0f7 parenrightex - 9120, // 0xf0f8 parenrightbt - 9124, // 0xf0f9 bracketrighttp - 9125, // 0xf0fa bracketrightex - 9126, // 0xf0fb bracketrightbt - 9131, // 0xf0fc bracerighttp - 9132, // 0xf0fd bracerightmid - 9133, // 0xf0fe bracerightbt - ' ', // 0xf0ff not defined - }; - - - // Could be replaced with org.apache.commons.lang3.StringUtils#join - @Internal - public static String join(Object[] array, String separator) { - if (array == null || array.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - sb.append(array[0]); - for (int i=1; i { + private String[] strings = {}; + private int position; + + public StringsIterator(String[] strings) { + if (strings != null) { + this.strings = strings.clone(); + } + } + + public boolean hasNext() { + return position < strings.length; + } + + public String next() { + int ourPos = position++; + if (ourPos >= strings.length) { + throw new ArrayIndexOutOfBoundsException(ourPos); + } + return strings[ourPos]; + } + + public void remove() { + } + } + + @Internal + public static String toLowerCase(char c) { + return Character.toString(c).toLowerCase(LocaleUtil.getUserLocale()); + } + + @Internal + public static String toUpperCase(char c) { + return Character.toString(c).toUpperCase(LocaleUtil.getUserLocale()); + } + + @Internal + public static boolean isUpperCase(char c) { + String s = Character.toString(c); + return s.toUpperCase(LocaleUtil.getUserLocale()).equals(s); + } + + /** + * Some strings may contain encoded characters of the unicode private use area. + * Currently the characters of the symbol fonts are mapped to the corresponding + * characters in the normal unicode range. + * + * @param string the original string + * @return the string with mapped characters + * @see Private Use Area (symbol) + * @see Symbol font - Unicode alternatives for Greek and special characters in HTML + */ + public static String mapMsCodepointString(String string) { + if (string == null || string.isEmpty()) return string; + initMsCodepointMap(); + + StringBuilder sb = new StringBuilder(); + final int length = string.length(); + for (int offset = 0; offset < length; ) { + Integer msCodepoint = string.codePointAt(offset); + Integer uniCodepoint = msCodepointToUnicode.get(msCodepoint); + sb.appendCodePoint(uniCodepoint == null ? msCodepoint : uniCodepoint); + offset += Character.charCount(msCodepoint); + } + + return sb.toString(); + } + + public static synchronized void mapMsCodepoint(int msCodepoint, int unicodeCodepoint) { + initMsCodepointMap(); + msCodepointToUnicode.put(msCodepoint, unicodeCodepoint); + } + + private static synchronized void initMsCodepointMap() { + if (msCodepointToUnicode != null) return; + msCodepointToUnicode = new HashMap<>(); + int i = 0xF020; + for (int ch : symbolMap_f020) { + msCodepointToUnicode.put(i++, ch); + } + i = 0xf0a0; + for (int ch : symbolMap_f0a0) { + msCodepointToUnicode.put(i++, ch); + } + } + + private static final int symbolMap_f020[] = { + ' ', // 0xf020 space + '!', // 0xf021 exclam + 8704, // 0xf022 universal + '#', // 0xf023 numbersign + 8707, // 0xf024 existential + '%', // 0xf025 percent + '&', // 0xf026 ampersand + 8717, // 0xf027 suchthat + '(', // 0xf028 parenleft + ')', // 0xf029 parentright + 8727, // 0xf02a asteriskmath + '+', // 0xf02b plus + ',', // 0xf02c comma + 8722, // 0xf02d minus sign (long -) + '.', // 0xf02e period + '/', // 0xf02f slash + '0', // 0xf030 0 + '1', // 0xf031 1 + '2', // 0xf032 2 + '3', // 0xf033 3 + '4', // 0xf034 4 + '5', // 0xf035 5 + '6', // 0xf036 6 + '7', // 0xf037 7 + '8', // 0xf038 8 + '9', // 0xf039 9 + ':', // 0xf03a colon + ';', // 0xf03b semicolon + '<', // 0xf03c less + '=', // 0xf03d equal + '>', // 0xf03e greater + '?', // 0xf03f question + 8773, // 0xf040 congruent + 913, // 0xf041 alpha (upper) + 914, // 0xf042 beta (upper) + 935, // 0xf043 chi (upper) + 916, // 0xf044 delta (upper) + 917, // 0xf045 epsilon (upper) + 934, // 0xf046 phi (upper) + 915, // 0xf047 gamma (upper) + 919, // 0xf048 eta (upper) + 921, // 0xf049 iota (upper) + 977, // 0xf04a theta1 (lower) + 922, // 0xf04b kappa (upper) + 923, // 0xf04c lambda (upper) + 924, // 0xf04d mu (upper) + 925, // 0xf04e nu (upper) + 927, // 0xf04f omicron (upper) + 928, // 0xf050 pi (upper) + 920, // 0xf051 theta (upper) + 929, // 0xf052 rho (upper) + 931, // 0xf053 sigma (upper) + 932, // 0xf054 tau (upper) + 933, // 0xf055 upsilon (upper) + 962, // 0xf056 simga1 (lower) + 937, // 0xf057 omega (upper) + 926, // 0xf058 xi (upper) + 936, // 0xf059 psi (upper) + 918, // 0xf05a zeta (upper) + '[', // 0xf05b bracketleft + 8765, // 0xf05c therefore + ']', // 0xf05d bracketright + 8869, // 0xf05e perpendicular + '_', // 0xf05f underscore + ' ', // 0xf060 radicalex (doesn't exist in unicode) + 945, // 0xf061 alpha (lower) + 946, // 0xf062 beta (lower) + 967, // 0xf063 chi (lower) + 948, // 0xf064 delta (lower) + 949, // 0xf065 epsilon (lower) + 966, // 0xf066 phi (lower) + 947, // 0xf067 gamma (lower) + 951, // 0xf068 eta (lower) + 953, // 0xf069 iota (lower) + 981, // 0xf06a phi1 (lower) + 954, // 0xf06b kappa (lower) + 955, // 0xf06c lambda (lower) + 956, // 0xf06d mu (lower) + 957, // 0xf06e nu (lower) + 959, // 0xf06f omnicron (lower) + 960, // 0xf070 pi (lower) + 952, // 0xf071 theta (lower) + 961, // 0xf072 rho (lower) + 963, // 0xf073 sigma (lower) + 964, // 0xf074 tau (lower) + 965, // 0xf075 upsilon (lower) + 982, // 0xf076 piv (lower) + 969, // 0xf077 omega (lower) + 958, // 0xf078 xi (lower) + 968, // 0xf079 psi (lower) + 950, // 0xf07a zeta (lower) + '{', // 0xf07b braceleft + '|', // 0xf07c bar + '}', // 0xf07d braceright + 8764, // 0xf07e similar '~' + ' ', // 0xf07f not defined + }; + + private static final int symbolMap_f0a0[] = { + 8364, // 0xf0a0 not defined / euro symbol + 978, // 0xf0a1 upsilon1 (upper) + 8242, // 0xf0a2 minute + 8804, // 0xf0a3 lessequal + 8260, // 0xf0a4 fraction + 8734, // 0xf0a5 infinity + 402, // 0xf0a6 florin + 9827, // 0xf0a7 club + 9830, // 0xf0a8 diamond + 9829, // 0xf0a9 heart + 9824, // 0xf0aa spade + 8596, // 0xf0ab arrowboth + 8591, // 0xf0ac arrowleft + 8593, // 0xf0ad arrowup + 8594, // 0xf0ae arrowright + 8595, // 0xf0af arrowdown + 176, // 0xf0b0 degree + 177, // 0xf0b1 plusminus + 8243, // 0xf0b2 second + 8805, // 0xf0b3 greaterequal + 215, // 0xf0b4 multiply + 181, // 0xf0b5 proportional + 8706, // 0xf0b6 partialdiff + 8729, // 0xf0b7 bullet + 247, // 0xf0b8 divide + 8800, // 0xf0b9 notequal + 8801, // 0xf0ba equivalence + 8776, // 0xf0bb approxequal + 8230, // 0xf0bc ellipsis + 9168, // 0xf0bd arrowvertex + 9135, // 0xf0be arrowhorizex + 8629, // 0xf0bf carriagereturn + 8501, // 0xf0c0 aleph + 8475, // 0xf0c1 Ifraktur + 8476, // 0xf0c2 Rfraktur + 8472, // 0xf0c3 weierstrass + 8855, // 0xf0c4 circlemultiply + 8853, // 0xf0c5 circleplus + 8709, // 0xf0c6 emptyset + 8745, // 0xf0c7 intersection + 8746, // 0xf0c8 union + 8835, // 0xf0c9 propersuperset + 8839, // 0xf0ca reflexsuperset + 8836, // 0xf0cb notsubset + 8834, // 0xf0cc propersubset + 8838, // 0xf0cd reflexsubset + 8712, // 0xf0ce element + 8713, // 0xf0cf notelement + 8736, // 0xf0d0 angle + 8711, // 0xf0d1 gradient + 174, // 0xf0d2 registerserif + 169, // 0xf0d3 copyrightserif + 8482, // 0xf0d4 trademarkserif + 8719, // 0xf0d5 product + 8730, // 0xf0d6 radical + 8901, // 0xf0d7 dotmath + 172, // 0xf0d8 logicalnot + 8743, // 0xf0d9 logicaland + 8744, // 0xf0da logicalor + 8660, // 0xf0db arrowdblboth + 8656, // 0xf0dc arrowdblleft + 8657, // 0xf0dd arrowdblup + 8658, // 0xf0de arrowdblright + 8659, // 0xf0df arrowdbldown + 9674, // 0xf0e0 lozenge + 9001, // 0xf0e1 angleleft + 174, // 0xf0e2 registersans + 169, // 0xf0e3 copyrightsans + 8482, // 0xf0e4 trademarksans + 8721, // 0xf0e5 summation + 9115, // 0xf0e6 parenlefttp + 9116, // 0xf0e7 parenleftex + 9117, // 0xf0e8 parenleftbt + 9121, // 0xf0e9 bracketlefttp + 9122, // 0xf0ea bracketleftex + 9123, // 0xf0eb bracketleftbt + 9127, // 0xf0ec bracelefttp + 9128, // 0xf0ed braceleftmid + 9129, // 0xf0ee braceleftbt + 9130, // 0xf0ef braceex + ' ', // 0xf0f0 not defined + 9002, // 0xf0f1 angleright + 8747, // 0xf0f2 integral + 8992, // 0xf0f3 integraltp + 9134, // 0xf0f4 integralex + 8993, // 0xf0f5 integralbt + 9118, // 0xf0f6 parenrighttp + 9119, // 0xf0f7 parenrightex + 9120, // 0xf0f8 parenrightbt + 9124, // 0xf0f9 bracketrighttp + 9125, // 0xf0fa bracketrightex + 9126, // 0xf0fb bracketrightbt + 9131, // 0xf0fc bracerighttp + 9132, // 0xf0fd bracerightmid + 9133, // 0xf0fe bracerightbt + ' ', // 0xf0ff not defined + }; + + + // Could be replaced with org.apache.commons.lang3.StringUtils#join + @Internal + public static String join(Object[] array, String separator) { + if (array == null || array.length == 0) return ""; + StringBuilder sb = new StringBuilder(); + sb.append(array[0]); + for (int i = 1; i < array.length; i++) { + sb.append(separator).append(array[i]); + } + return sb.toString(); + } + + @Internal + public static String join(Object[] array) { + if (array == null) return ""; + StringBuilder sb = new StringBuilder(); + for (Object o : array) { + sb.append(o); + } + return sb.toString(); + } + + @Internal + public static String join(String separator, Object... array) { + return join(array, separator); + } + + /** + * Count number of occurrences of needle in haystack + * Has same signature as org.apache.commons.lang3.StringUtils#countMatches + * + * @param haystack the CharSequence to check, may be null + * @param needle the character to count the quantity of + * @return the number of occurrences, 0 if the CharSequence is null + */ + public static int countMatches(CharSequence haystack, char needle) { + if (haystack == null) return 0; + int count = 0; + final int length = haystack.length(); + for (int i = 0; i < length; i++) { + if (haystack.charAt(i) == needle) { + count++; + } + } + return count; + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPasswordHelper.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPasswordHelper.java index 471a48c713..dccc9c552b 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPasswordHelper.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFPasswordHelper.java @@ -29,6 +29,8 @@ import javax.xml.namespace.QName; import org.apache.poi.poifs.crypt.CryptoFunctions; import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.util.Internal; +import org.apache.poi.util.LocaleUtil; +import org.apache.poi.util.StringUtil; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; @@ -130,7 +132,7 @@ public final class XSSFPasswordHelper { if (prefix == null || prefix.isEmpty()) { return new QName(name); } else { - return new QName(prefix+Character.toUpperCase(name.charAt(0))+name.substring(1)); + return new QName(prefix + StringUtil.toUpperCase(name.charAt(0)) + name.substring(1)); } } } diff --git a/src/resources/devtools/forbidden-signatures.txt b/src/resources/devtools/forbidden-signatures.txt index a95abb7174..c96b08b6c6 100644 --- a/src/resources/devtools/forbidden-signatures.txt +++ b/src/resources/devtools/forbidden-signatures.txt @@ -102,8 +102,10 @@ java.lang.Character#codePointAt(char[],int) @ Implicit end offset is error-prone @defaultMessage specify a locale when using toUpperCase / to LowerCase -#java.lang.Character#toLowerCase(char) -#java.lang.Character#toUpperCase(char) +java.lang.Character#isLowerCase(char) +java.lang.Character#isUpperCase(char) +java.lang.Character#toLowerCase(char) +java.lang.Character#toUpperCase(char) java.lang.String#toLowerCase() java.lang.String#toUpperCase() diff --git a/src/scratchpad/src/org/apache/poi/hwpf/dev/RecordUtil.java b/src/scratchpad/src/org/apache/poi/hwpf/dev/RecordUtil.java index 9cfa1324e9..6ecb5c33df 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/dev/RecordUtil.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/dev/RecordUtil.java @@ -18,6 +18,7 @@ package org.apache.poi.hwpf.dev; import org.apache.poi.util.Internal; +import org.apache.poi.util.StringUtil; /** * Helper functions for the record transformations. Used during model classes @@ -172,7 +173,7 @@ public class RecordUtil { StringBuilder fieldName = new StringBuilder(); toIdentifier( name, fieldName ); - fieldName.setCharAt( 0, Character.toUpperCase( fieldName.charAt( 0 ) ) ); + fieldName.setCharAt( 0, toUpperCase( fieldName.charAt(0) ) ); pad( fieldName, padTo ); return fieldName.toString(); @@ -183,7 +184,7 @@ public class RecordUtil StringBuilder result = new StringBuilder(); result.append( type ); result = pad( result, padTo ); - result.setCharAt( 0, Character.toUpperCase( result.charAt( 0 ) ) ); + result.setCharAt( 0, toUpperCase( result.charAt( 0 ) ) ); return result.toString(); } @@ -202,7 +203,7 @@ public class RecordUtil if ( name.charAt( i ) == ' ' ) fieldName.append( '_' ); else - fieldName.append( Character.toUpperCase( name.charAt( i ) ) ); + fieldName.append( toUpperCase( name.charAt( i ) ) ); } } @@ -211,12 +212,16 @@ public class RecordUtil for ( int i = 0; i < name.length(); i++ ) { if ( name.charAt( i ) == ' ' ) - fieldName.append( Character.toUpperCase( name.charAt( ++i ) ) ); + fieldName.append( toUpperCase( name.charAt( ++i ) ) ); else fieldName.append( name.charAt( i ) ); } } + private static char toUpperCase(char c) { + return StringUtil.toUpperCase(c).charAt(0); + } + public RecordUtil() { } -- 2.39.5