/** * Parses a phone number from the {@code candidate} using {@link PhoneNumberUtil#parse} and * verifies it matches the requested {@link #leniency}. If parsing and verification succeed, a * corresponding {@link PhoneNumberMatch} is returned, otherwise this method returns null. * * @param candidate the candidate match * @param offset the offset of {@code candidate} within {@link #text} * @return the parsed and validated phone number match, or null */ private PhoneNumberMatch parseAndVerify(String candidate, int offset) { try { // Check the candidate doesn't contain any formatting which would indicate that it really // isn't a phone number. if (!MATCHING_BRACKETS.MatchWhole(candidate).Success || PUB_PAGES.Match(candidate).Success) { return(null); } // If leniency is set to VALID or stricter, we also want to skip numbers that are surrounded // by Latin alphabetic characters, to skip cases like abc8005001234 or 8005001234def. if (leniency.CompareTo(PhoneNumberUtil.Leniency.VALID) >= 0) { // If the candidate is not at the start of the text, and does not start with phone-number // punctuation, check the previous character. if (offset > 0 && !LEAD_CLASS.MatchBeginning(candidate).Success) { char previousChar = text[offset - 1]; // We return null if it is a latin letter or an invalid punctuation symbol. if (isInvalidPunctuationSymbol(previousChar) || isLatinLetter(previousChar)) { return(null); } } int lastCharIndex = offset + candidate.Length; if (lastCharIndex < text.Length) { char nextChar = text[lastCharIndex]; if (isInvalidPunctuationSymbol(nextChar) || isLatinLetter(nextChar)) { return(null); } } } PhoneNumber number = phoneUtil.parseAndKeepRawInput(candidate, preferredRegion); // Check Israel * numbers: these are a special case in that they are four-digit numbers that // our library supports, but they can only be dialled with a leading *. Since we don't // actually store or detect the * in our phone number library, this means in practice we // detect most four digit numbers as being valid for Israel. We are considering moving these // numbers to ShortNumberInfo instead, in which case this problem would go away, but in the // meantime we want to restrict the false matches so we only allow these numbers if they are // preceded by a star. We enforce this for all leniency levels even though these numbers are // technically accepted by isPossibleNumber and isValidNumber since we consider it to be a // deficiency in those methods that they accept these numbers without the *. // TODO: Remove this or make it significantly less hacky once we've decided how to // handle these short codes going forward in ShortNumberInfo. We could use the formatting // rules for instance, but that would be slower. if (phoneUtil.getRegionCodeForCountryCode(number.getCountryCode()).Equals("IL") && phoneUtil.getNationalSignificantNumber(number).Length == 4 && (offset == 0 || (offset > 0 && text[offset - 1] != '*'))) { // No match. return(null); } if (leniency.verify(number, candidate, phoneUtil)) { // We used parseAndKeepRawInput to create this number, but for now we don't return the extra // values parsed. TODO: stop clearing all values here and switch all users over // to using rawInput() rather than the rawString() of PhoneNumberMatch. number.clearCountryCodeSource(); number.clearRawInput(); number.clearPreferredDomesticCarrierCode(); return(new PhoneNumberMatch(offset, candidate, number)); } } catch (NumberParseException) { // ignore and continue } return(null); }