diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index d317557cbb1..7390bc76b43 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -529,6 +529,24 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence return StringUTF16.codePointCountSB(value, beginIndex, endIndex); } + /** + * Returns the number of Unicode code points in + * this sequence. Unpaired surrogates count + * as one code point each. + * + * @return the number of Unicode code points in this String + * @since 26 + */ + public int codePointCount() { + byte coder = this.coder; + int count = this.count; + byte[] value = this.value; + if (isLatin1(coder)) { + return value.length; + } + return StringUTF16.codePointCount(value, count, value.length >> 1); + } + /** * Returns the index within this sequence that is offset from the * given {@code index} by {@code codePointOffset} code diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index a439a90761d..e67dff89633 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -9952,6 +9952,29 @@ class Character implements java.io.Serializable, Comparable, Constabl return n; } + /** + * Returns the number of Unicode code points in the text range of + * the specified char sequence. Unpaired surrogates count as one + * code point each. + * + * @param seq the char sequence + * @return the number of Unicode code points in the char sequence + * @throws NullPointerException if {@code seq} is null. + * @since 26 + */ + public static int codePointCount(CharSequence seq) { + final int length = seq.length(); + int n = length; + for (int i = 0; i < length; ) { + if (isHighSurrogate(seq.charAt(i++)) && i < length && + isLowSurrogate(seq.charAt(i))) { + n--; + i++; + } + } + return n; + } + /** * Returns the number of Unicode code points in a subarray of the * {@code char} array argument. The {@code offset} @@ -9976,6 +9999,20 @@ class Character implements java.io.Serializable, Comparable, Constabl return codePointCountImpl(a, offset, count); } + /** + * Returns the number of Unicode code points in the + * {@code char} array argument. Unpaired + * surrogates count as one code point each. + * + * @param a the {@code char} array + * @return the number of Unicode code points in the char array + * @throws NullPointerException if {@code a} is null. + * @since 26 + */ + public static int codePointCount(char[] a) { + return codePointCountImpl(a, 0, a.length); + } + static int codePointCountImpl(char[] a, int offset, int count) { int endIndex = offset + count; int n = count; diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 76566de089c..056fbeb637e 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -1714,6 +1714,21 @@ public final class String return StringUTF16.codePointCount(value, beginIndex, endIndex); } + /** + * Returns the number of Unicode code points in + * this {@code String}. Unpaired surrogates count + * as one code point each. + * + * @return the number of Unicode code points in this String + * @since 26 + */ + public int codePointCount() { + if (isLatin1()) { + return value.length; + } + return StringUTF16.codePointCount(value, 0, value.length >> 1); + } + /** * Returns the index within this {@code String} that is * offset from the given {@code index} by diff --git a/src/java.base/share/classes/java/lang/StringBuffer.java b/src/java.base/share/classes/java/lang/StringBuffer.java index 2546a84f39c..8b216e4549e 100644 --- a/src/java.base/share/classes/java/lang/StringBuffer.java +++ b/src/java.base/share/classes/java/lang/StringBuffer.java @@ -270,6 +270,14 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; return super.codePointCount(beginIndex, endIndex); } + /** + * @since 26 + */ + @Override + public synchronized int codePointCount() { + return super.codePointCount(); + } + /** * @throws IndexOutOfBoundsException {@inheritDoc} * @since 1.5 diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java index b7f03c1b0af..d66ac59d1a5 100644 --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -1658,7 +1658,7 @@ public final class Pattern String seq = src.substring(off, j); String nfd = Normalizer.normalize(seq, Normalizer.Form.NFD); off = j; - if (nfd.codePointCount(0, nfd.length()) > 1) { + if (nfd.codePointCount() > 1) { ch0 = nfd.codePointAt(0); ch1 = nfd.codePointAt(Character.charCount(ch0)); if (Character.getType(ch1) == Character.NON_SPACING_MARK) { @@ -4157,7 +4157,7 @@ loop: for(int x=0, offset=0; x { if (keySeq != null && keySeq.length() > 0) { char c = keySeq.charAt(0); if (c >= mapping.length) { - remaining[0] = Character.codePointCount(keySeq, 0, keySeq.length()); + remaining[0] = Character.codePointCount(keySeq); return null; } else { if (mapping[c] instanceof KeyMap) { diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java index b73daee66de..192b6fe1afb 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java @@ -790,8 +790,8 @@ class JdepsTask { String replacementApiTitle = getMessage("public.api.replacement.column.header"); log.format("%-40s %s%n", internalApiTitle, replacementApiTitle); log.format("%-40s %s%n", - "-".repeat(internalApiTitle.codePointCount(0, internalApiTitle.length())), - "-".repeat(replacementApiTitle.codePointCount(0, replacementApiTitle.length()))); + "-".repeat(internalApiTitle.codePointCount()), + "-".repeat(replacementApiTitle.codePointCount())); jdkInternals.entrySet() .forEach(e -> { String key = e.getKey(); diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java index 4700231a162..08b32232f26 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/Arguments.java @@ -817,7 +817,7 @@ public class Arguments { // Use code points to preserve non-ASCII chars StringBuilder sb = new StringBuilder(); - int codeLen = in.codePointCount(0, in.length()); + int codeLen = in.codePointCount(); int quoteChar = -1; for (int i = 0; i < codeLen; i++) { int code = in.codePointAt(i); diff --git a/test/jdk/java/lang/Character/Supplementary.java b/test/jdk/java/lang/Character/Supplementary.java index acb162ad947..9ea1823d6fd 100644 --- a/test/jdk/java/lang/Character/Supplementary.java +++ b/test/jdk/java/lang/Character/Supplementary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -334,6 +334,8 @@ public class Supplementary { /** * Test codePointCount(CharSequence, int, int) * codePointCount(char[], int, int, int, int) + * codePointCount(CharSequence) + * codePointCount(char[]) */ static void test04(String str) { int length = str.length(); @@ -347,9 +349,15 @@ public class Supplementary { checkCodePointCount(a, n, m); } + int n = Character.codePointCount(str); + int m = codePointCount(str); + checkCodePointCount(str, n, m); + n = Character.codePointCount(a); + checkCodePointCount(a, n, m); + // test special cases length = str.length(); - int n = Character.codePointCount(str, 0, 0); + n = Character.codePointCount(str, 0, 0); checkCodePointCount(str, n, 0); n = Character.codePointCount(str, length, length); checkCodePointCount(str, n, 0); diff --git a/test/jdk/java/lang/String/Supplementary.java b/test/jdk/java/lang/String/Supplementary.java index 3fc8c70bd5c..bb94bb88ec7 100644 --- a/test/jdk/java/lang/String/Supplementary.java +++ b/test/jdk/java/lang/String/Supplementary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -388,7 +388,7 @@ public class Supplementary { /** * Test codePointCount(int, int) * - * This test case assumes that + * This test case assumes that Character.codePointCount() & * Character.codePointCount(CharSequence, int, int) works * correctly. */ @@ -419,6 +419,10 @@ public class Supplementary { result, expected); } + int result = str.codePointCount(); + int expected = Character.codePointCount(); + check(result != expected, "substring:codePointCount()", result, expected); + // test exceptions testCodePointCount(null, 0, 0, NullPointerException.class); testCodePointCount(str, -1, length, IndexOutOfBoundsException.class); diff --git a/test/jdk/java/lang/StringBuilder/Supplementary.java b/test/jdk/java/lang/StringBuilder/Supplementary.java index a13b0f44fb2..f775107c088 100644 --- a/test/jdk/java/lang/StringBuilder/Supplementary.java +++ b/test/jdk/java/lang/StringBuilder/Supplementary.java @@ -215,11 +215,11 @@ public class Supplementary { } /** - * Test codePointCount(int, int) + * Test codePointCount(int, int) & codePointCount() * * This test case assumes that - * Character.codePointCount(CharSequence, int, int) works - * correctly. + * Character.codePointCount(CharSequence, int, int) & + * Character.codePointCount(CharSequence) works correctly. */ static void test5() { for (int i = 0; i < input.length; i++) { @@ -239,6 +239,11 @@ public class Supplementary { result, expected); } + + int result = sb.codePointCount(); + int expected = Character.codePointCount(sb); + check(result != expected, "codePointCount()", result, expected); + // test exceptions testCodePointCount(null, 0, 0, NullPointerException.class); testCodePointCount(sb, -1, length, IndexOutOfBoundsException.class);