@Override
    protected Number parse(String value, DecimalFormat formatter) {
        // check and replace currency symbol
        if (value.indexOf(CURRENCY_SYMBOL) >= 0) {
            value = value.replace(CURRENCY_SYMBOL_STR, formatter.getDecimalFormatSymbols().getCurrencySymbol());
        }

        // between JDK8 and 11 some space characters became non breaking space '\u00A0'
        if (formatter.getPositivePrefix().indexOf(Constants.NON_BREAKING_SPACE) >= 0) {
            value = SPACE_PATTERN.matcher(value).replaceAll(Constants.NON_BREAKING_SPACE_STR);
        }

        // Initial parse of the value
        Number parsedValue = super.parse(value, formatter);
        if (parsedValue != null) {
            return parsedValue;
        }

        // Re-parse using a pattern without the currency symbol
        String pattern = formatter.toPattern();
        if (pattern.indexOf(CURRENCY_SYMBOL) >= 0) {
            StringBuilder buffer = new StringBuilder(pattern.length());
            for (int i = 0; i < pattern.length(); i++) {
                if (pattern.charAt(i) != CURRENCY_SYMBOL) {
                    buffer.append(pattern.charAt(i));
                }
            }
            DecimalFormat copyFormatter = (DecimalFormat) formatter.clone();
            copyFormatter.applyPattern(buffer.toString());
            parsedValue = super.parse(value, copyFormatter);
        }
        return parsedValue;
    }
<p>
Parse the value with the specified <code>Format</code>.
</p>
<p>
This implementation is lenient whether the currency symbol is present or not. The default <code>NumberFormat</code> behavior is for the parsing to "fail"
if the currency symbol is missing. This method re-parses with a format without the currency symbol if it fails initially.
</p>

@param value The value to be parsed.
@param formatter The Format to parse the value with.
@return The parsed value if valid or <code>null</code> if invalid.
primefaces/src/main/java/org/primefaces/util/CurrencyValidator.java