diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index d0d42e7199e..1dd4c2feca7 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -47,6 +47,7 @@ import java.io.ObjectStreamField; import java.io.Serializable; import java.text.NumberFormat; import java.text.MessageFormat; +import java.text.ParsePosition; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.spi.LocaleNameProvider; @@ -63,7 +64,6 @@ import sun.util.locale.LocaleExtensions; import sun.util.locale.LocaleMatcher; import sun.util.locale.LocaleSyntaxException; import sun.util.locale.LocaleUtils; -import sun.util.locale.ParseStatus; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleResources; import sun.util.locale.provider.LocaleServiceProviderPool; @@ -1919,7 +1919,8 @@ public final class Locale implements Cloneable, Serializable { * @since 1.7 */ public static Locale forLanguageTag(String languageTag) { - LanguageTag tag = LanguageTag.parse(languageTag, null); + LanguageTag tag = LanguageTag.parse( + languageTag, new ParsePosition(0), true); InternalLocaleBuilder bldr = new InternalLocaleBuilder(); bldr.setLanguageTag(tag); BaseLocale base = bldr.getBaseLocale(); @@ -2789,11 +2790,8 @@ public final class Locale implements Cloneable, Serializable { * @see Locale#forLanguageTag(String) */ public Builder setLanguageTag(String languageTag) { - ParseStatus sts = new ParseStatus(); - LanguageTag tag = LanguageTag.parse(languageTag, sts); - if (sts.isError()) { - throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex()); - } + LanguageTag tag = LanguageTag.parse( + languageTag, new ParsePosition(0), false); localeBuilder.setLanguageTag(tag); return this; } diff --git a/src/java.base/share/classes/sun/util/locale/LanguageTag.java b/src/java.base/share/classes/sun/util/locale/LanguageTag.java index 0c38290c131..5c905372c5a 100644 --- a/src/java.base/share/classes/sun/util/locale/LanguageTag.java +++ b/src/java.base/share/classes/sun/util/locale/LanguageTag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -31,6 +31,7 @@ */ package sun.util.locale; +import java.text.ParsePosition; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -180,14 +181,10 @@ public class LanguageTag { * privateuse = "x" 1*("-" (1*8alphanum)) * */ - public static LanguageTag parse(String languageTag, ParseStatus sts) { - if (sts == null) { - sts = new ParseStatus(); - } else { - sts.reset(); - } - + public static LanguageTag parse(String languageTag, ParsePosition pp, + boolean lenient) { StringTokenIterator itr; + var errorMsg = new StringBuilder(); // Check if the tag is a legacy language tag String[] gfmap = LEGACY.get(LocaleUtils.toLowerString(languageTag)); @@ -201,23 +198,29 @@ public class LanguageTag { LanguageTag tag = new LanguageTag(); // langtag must start with either language or privateuse - if (tag.parseLanguage(itr, sts)) { - tag.parseExtlangs(itr, sts); - tag.parseScript(itr, sts); - tag.parseRegion(itr, sts); - tag.parseVariants(itr, sts); - tag.parseExtensions(itr, sts); + if (tag.parseLanguage(itr, pp)) { + tag.parseExtlangs(itr, pp); + tag.parseScript(itr, pp); + tag.parseRegion(itr, pp); + tag.parseVariants(itr, pp); + tag.parseExtensions(itr, pp, errorMsg); } - tag.parsePrivateuse(itr, sts); - if (!itr.isDone() && !sts.isError()) { + tag.parsePrivateuse(itr, pp, errorMsg); + + if (!itr.isDone() && pp.getErrorIndex() == -1) { String s = itr.current(); - sts.errorIndex = itr.currentStart(); + pp.setErrorIndex(itr.currentStart()); if (s.isEmpty()) { - sts.errorMsg = "Empty subtag"; + errorMsg.append("Empty subtag"); } else { - sts.errorMsg = "Invalid subtag: " + s; + errorMsg.append("Invalid subtag: ").append(s); } } + + if (!lenient && pp.getErrorIndex() != -1) { + throw new IllformedLocaleException(errorMsg.toString(), pp.getErrorIndex()); + } + return tag; } @@ -225,8 +228,8 @@ public class LanguageTag { // Language subtag parsers // - private boolean parseLanguage(StringTokenIterator itr, ParseStatus sts) { - if (itr.isDone() || sts.isError()) { + private boolean parseLanguage(StringTokenIterator itr, ParsePosition pp) { + if (itr.isDone() || pp.getErrorIndex() != -1) { return false; } @@ -236,15 +239,15 @@ public class LanguageTag { if (isLanguage(s)) { found = true; language = s; - sts.parseLength = itr.currentEnd(); + pp.setIndex(itr.currentEnd()); itr.next(); } return found; } - private boolean parseExtlangs(StringTokenIterator itr, ParseStatus sts) { - if (itr.isDone() || sts.isError()) { + private boolean parseExtlangs(StringTokenIterator itr, ParsePosition pp) { + if (itr.isDone() || pp.getErrorIndex() != -1) { return false; } @@ -260,7 +263,7 @@ public class LanguageTag { extlangs = new ArrayList<>(3); } extlangs.add(s); - sts.parseLength = itr.currentEnd(); + pp.setIndex(itr.currentEnd()); itr.next(); if (extlangs.size() == 3) { @@ -272,8 +275,8 @@ public class LanguageTag { return found; } - private boolean parseScript(StringTokenIterator itr, ParseStatus sts) { - if (itr.isDone() || sts.isError()) { + private boolean parseScript(StringTokenIterator itr, ParsePosition pp) { + if (itr.isDone() || pp.getErrorIndex() != -1) { return false; } @@ -283,15 +286,15 @@ public class LanguageTag { if (isScript(s)) { found = true; script = s; - sts.parseLength = itr.currentEnd(); + pp.setIndex(itr.currentEnd()); itr.next(); } return found; } - private boolean parseRegion(StringTokenIterator itr, ParseStatus sts) { - if (itr.isDone() || sts.isError()) { + private boolean parseRegion(StringTokenIterator itr, ParsePosition pp) { + if (itr.isDone() || pp.getErrorIndex() != -1) { return false; } @@ -301,15 +304,15 @@ public class LanguageTag { if (isRegion(s)) { found = true; region = s; - sts.parseLength = itr.currentEnd(); + pp.setIndex(itr.currentEnd()); itr.next(); } return found; } - private boolean parseVariants(StringTokenIterator itr, ParseStatus sts) { - if (itr.isDone() || sts.isError()) { + private boolean parseVariants(StringTokenIterator itr, ParsePosition pp) { + if (itr.isDone() || pp.getErrorIndex() != -1) { return false; } @@ -325,15 +328,16 @@ public class LanguageTag { variants = new ArrayList<>(3); } variants.add(s); - sts.parseLength = itr.currentEnd(); + pp.setIndex(itr.currentEnd()); itr.next(); } return found; } - private boolean parseExtensions(StringTokenIterator itr, ParseStatus sts) { - if (itr.isDone() || sts.isError()) { + private boolean parseExtensions(StringTokenIterator itr, ParsePosition pp, + StringBuilder err) { + if (itr.isDone() || pp.getErrorIndex() != -1) { return false; } @@ -351,16 +355,16 @@ public class LanguageTag { s = itr.current(); if (isExtensionSubtag(s)) { sb.append(SEP).append(s); - sts.parseLength = itr.currentEnd(); + pp.setIndex(itr.currentEnd()); } else { break; } itr.next(); } - if (sts.parseLength <= start) { - sts.errorIndex = start; - sts.errorMsg = "Incomplete extension '" + singleton + "'"; + if (pp.getIndex() <= start) { + pp.setErrorIndex(start); + err.append("Incomplete extension '").append(singleton).append("'"); break; } @@ -376,8 +380,9 @@ public class LanguageTag { return found; } - private boolean parsePrivateuse(StringTokenIterator itr, ParseStatus sts) { - if (itr.isDone() || sts.isError()) { + private boolean parsePrivateuse(StringTokenIterator itr, ParsePosition pp, + StringBuilder err) { + if (itr.isDone() || pp.getErrorIndex() != -1) { return false; } @@ -395,15 +400,15 @@ public class LanguageTag { break; } sb.append(SEP).append(s); - sts.parseLength = itr.currentEnd(); + pp.setIndex(itr.currentEnd()); itr.next(); } - if (sts.parseLength <= start) { + if (pp.getIndex() <= start) { // need at least 1 private subtag - sts.errorIndex = start; - sts.errorMsg = "Incomplete privateuse"; + pp.setErrorIndex(start); + err.append("Incomplete privateuse"); } else { privateuse = sb.toString(); found = true; @@ -414,13 +419,8 @@ public class LanguageTag { } public static String caseFoldTag(String tag) { - ParseStatus sts = new ParseStatus(); - parse(tag, sts); - // Illegal tags - if (sts.errorMsg != null) { - throw new IllformedLocaleException(String.format("Ill formed tag:" + - " %s", sts.errorMsg)); - } + parse(tag, new ParsePosition(0), false); + // Legacy tags String potentialLegacy = tag.toLowerCase(Locale.ROOT); if (LEGACY.containsKey(potentialLegacy)) { diff --git a/src/java.base/share/classes/sun/util/locale/ParseStatus.java b/src/java.base/share/classes/sun/util/locale/ParseStatus.java deleted file mode 100644 index 91da6bf0f18..00000000000 --- a/src/java.base/share/classes/sun/util/locale/ParseStatus.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2010, 2011, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - ******************************************************************************* - * Copyright (C) 2010, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ -package sun.util.locale; - -public class ParseStatus { - int parseLength; - int errorIndex; - String errorMsg; - - public ParseStatus() { - reset(); - } - - public void reset() { - parseLength = 0; - errorIndex = -1; - errorMsg = null; - } - - public boolean isError() { - return (errorIndex >= 0); - } - - public int getErrorIndex() { - return errorIndex; - } - - public int getParseLength() { - return parseLength; - } - - public String getErrorMessage() { - return errorMsg; - } -}