Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
Merge pull request #8 from daschJulia/25562-fix_phoneNumbers_removed_…
Browse files Browse the repository at this point in the history
…numbers

update PhoneNumberFormatter
  • Loading branch information
contargo-development authored Jun 25, 2019
2 parents 0cfa3ba + a0b8e87 commit b4c2f28
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 66 deletions.
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Contargo Types
===============

## v0.17.3

* fix PhoneNumberFormatter, the numbers was formatted by the google library and then splitted into country code, areaCode
and connectionNumber. If the Number does have connectionNumbers with mor than on empty space splitted only the first part
was used.

## v0.17.2

* fix broken tests from PhoneNumbers.
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>net.contargo</groupId>
<artifactId>contargo-types</artifactId>
<version>0.17.2</version>
<version>0.17.3</version>
<packaging>jar</packaging>

<name>Contargo Types</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public List<ValidationResult> checkCompleteness(final ContactInformation contact
List<ValidationResult> messages = new ArrayList<>();

final boolean missingEmail = StringUtils.isEmpty(contactInformation.getEmail());
final boolean missingMobile = !contactInformation.getMobile().isPhoneNumber();
final boolean missingPhone = !contactInformation.getPhone().isPhoneNumber();
final boolean missingMobile = !contactInformation.getMobile().canBeFormatted();
final boolean missingPhone = !contactInformation.getPhone().canBeFormatted();

if (missingEmail && missingMobile) {
messages.add(ValidationResult.MISSING_EMAIL);
Expand Down
27 changes: 23 additions & 4 deletions src/main/java/net/contargo/types/telephony/PhoneNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ public final class PhoneNumber implements Loggable {
private Country country;
private final String rawPhoneNumber;
private String phoneExtension;
private boolean isMobile = false;

public PhoneNumber() {

this.rawPhoneNumber = "";
}


public PhoneNumber(String rawPhoneNumber) {

Expand Down Expand Up @@ -89,7 +96,7 @@ public Optional<String> getInternationalFormatOfPhoneNumber() {

logger().info("formatting phone number: {} into international phone number.", getPhoneNumber());

if (StringUtils.isBlank(rawPhoneNumber) || containsOnlyZeros(rawPhoneNumber)) {
if (StringUtils.isBlank(rawPhoneNumber) || containsOnlyZeros()) {
logger().warn("Not able to parse phone number of {}", rawPhoneNumber);

return Optional.empty();
Expand Down Expand Up @@ -117,15 +124,27 @@ private String getPhoneNumber() {
}


public boolean isPhoneNumber() {
public boolean canBeFormatted() {

return getInternationalFormatOfPhoneNumber().isPresent();
}


private boolean containsOnlyZeros(String number) {
public boolean containsOnlyZeros() {

return rawPhoneNumber.matches("0+$");
}


public boolean isMobile() {

return isMobile;
}


public void setMobile(boolean mobile) {

return number.matches("0+$");
isMobile = mobile;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@

import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType;
import com.google.i18n.phonenumbers.Phonenumber;

import net.contargo.types.Loggable;

import org.apache.commons.lang.StringUtils;

import java.util.Arrays;


/**
* Handles the formatting of telephone numbers.
*
* @author Robin Jayasinghe - [email protected]
* @author Julia Dasch - [email protected]
*/
public class PhoneNumberFormatter {
public class PhoneNumberFormatter implements Loggable {

private static PhoneNumberFormatter instance = null;

Expand Down Expand Up @@ -67,22 +72,29 @@ public String parseAndFormatToE164Format(final String phoneNumber) throws PhoneN
public String parseAndFormatToDIN5008(final String phoneNumber) throws PhoneNumberFormattingException {

// strip all whitespace and line breaks from input
final String phoneNumberWithoutWhitespace = trimPhoneNumber(phoneNumber);
final String trimPhoneNumber = trimPhoneNumber(phoneNumber);

// pre-validation and formatting with libphonenumber
final Phonenumber.PhoneNumber parsedNumber = parsePhoneNumber(phoneNumber, phoneNumberWithoutWhitespace);
final Phonenumber.PhoneNumber parsedNumber = parsePhoneNumber(phoneNumber, trimPhoneNumber);
final String formattedNumber = phoneNumberUtil.format(parsedNumber,
PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL);

// extract primitives from libphonenumber parse result
final String nationalNumber = String.valueOf(parsedNumber.getNationalNumber());
final int countryCode = parsedNumber.getCountryCode();
final PhoneNumberUtil.PhoneNumberType phoneNumberType = PhoneNumberUtil.getInstance()
.getNumberType(parsedNumber);
final PhoneNumberType phoneNumberType = getPhoneNumberType(phoneNumber);

// trigger the actual formatting
return formatDIN5008(countryCode, nationalNumber, formattedNumber,
phoneNumberType.equals(PhoneNumberUtil.PhoneNumberType.FIXED_LINE));
String formatDIN5008 = formatDIN5008(countryCode, nationalNumber, formattedNumber,
phoneNumberType.equals(PhoneNumberType.FIXED_LINE));

if (formatDIN5008.length() >= trimPhoneNumber.length()) {
return formatDIN5008;
} else {
throw new PhoneNumberFormattingException(String.format(
"can not format number. the formatted phone number('%s') has less digits than the incoming phone number('%s')",
formatDIN5008, phoneNumber));
}
}


Expand Down Expand Up @@ -152,8 +164,7 @@ public String getNationalSignificantNumber(final String phoneNumber) throws Phon
*
* @throws PhoneNumberFormattingException if the phoneNumber cannot be parsed
*/
public PhoneNumberUtil.PhoneNumberType getPhoneNumberType(final String phoneNumber)
throws PhoneNumberFormattingException {
public PhoneNumberType getPhoneNumberType(final String phoneNumber) throws PhoneNumberFormattingException {

Phonenumber.PhoneNumber parsedNumber = parsePhoneNumber(phoneNumber, trimPhoneNumber(phoneNumber));

Expand All @@ -179,34 +190,32 @@ private Phonenumber.PhoneNumber parsePhoneNumber(String phoneNumber, String phon
private String formatDIN5008(final int countryCode, final String nationalNumber, final String preFormattedNumber,
final boolean isFixedLine) throws PhoneNumberFormattingException {

final AreaCodeAndConnectionNumber areaCodeAndConnectionNumber;
final NationalNumber areaCodeAndConnectionNumber;

if (isFixedLine) {
areaCodeAndConnectionNumber = getAreaCodeAndConnectionNumberFromFixedNumber(preFormattedNumber);
areaCodeAndConnectionNumber = getNationalNumberFromFixedNumber(preFormattedNumber);
} else {
areaCodeAndConnectionNumber = getAreaCodeAndConnectionNumberFromMobileNumber(nationalNumber);
areaCodeAndConnectionNumber = getNationalNumberFromMobileNumber(nationalNumber);
}

return chunkAndFormatResult(countryCode, areaCodeAndConnectionNumber);
}


private AreaCodeAndConnectionNumber getAreaCodeAndConnectionNumberFromMobileNumber(String nationalNumber) {

AreaCodeAndConnectionNumber areaCodeAndConnectionNumber;
private NationalNumber getNationalNumberFromMobileNumber(String nationalNumber) {

if (nationalNumber.length() > 4) { // only for numbers longer than 4 digits
areaCodeAndConnectionNumber = new AreaCodeAndConnectionNumber(nationalNumber.substring(0, 3),
nationalNumber.substring(3, nationalNumber.length()).replaceAll("\\D", ""));

String connectionNumber = nationalNumber.substring(3).replaceAll("\\D", "");

return new NationalNumber(nationalNumber.substring(0, 3), connectionNumber);
} else { // for the shorter numbers
areaCodeAndConnectionNumber = new AreaCodeAndConnectionNumber("", nationalNumber);
return new NationalNumber("", nationalNumber);
}

return areaCodeAndConnectionNumber;
}


private AreaCodeAndConnectionNumber getAreaCodeAndConnectionNumberFromFixedNumber(String preFormattedNumber)
private NationalNumber getNationalNumberFromFixedNumber(String preFormattedNumber)
throws PhoneNumberFormattingException {

// fist element should be the country code
Expand All @@ -216,26 +225,29 @@ private AreaCodeAndConnectionNumber getAreaCodeAndConnectionNumberFromFixedNumbe
throw new PhoneNumberFormattingException(String.format("Could not extract anything from input number: %s",
preFormattedNumber));
} else if (splittedPreformattedNumber.length <= 2) { // two elements -> take the second element as connection
return new AreaCodeAndConnectionNumber("", splittedPreformattedNumber[1].replaceAll("\\D+", ""));
return new NationalNumber("", splittedPreformattedNumber[1].replaceAll("\\D+", ""));
} else { // more than one element -> take the second as area code and the third as connection number (first is country code)
return new AreaCodeAndConnectionNumber(preFormattedNumber.split(" ")[1],
splittedPreformattedNumber[2].replaceAll("\\D+", ""));

String[] connectionNumbers = Arrays.copyOfRange(splittedPreformattedNumber, 2,
splittedPreformattedNumber.length);
String connectionNumber = StringUtils.join(connectionNumbers, "");

return new NationalNumber(splittedPreformattedNumber[1], connectionNumber.replaceAll("\\D+", ""));
}
}


private String chunkAndFormatResult(int countryCode, AreaCodeAndConnectionNumber areaCodeAndConnectionNumber) {
private String chunkAndFormatResult(int countryCode, NationalNumber nationalNumber) {

logger().debug("chunk the number:'{} {} {}' into blocks of 4 digits. the last block can be 1-4 digits",
countryCode, nationalNumber.areaCode, nationalNumber.connectionNumber);

// chunk the number into blocks of 4 digits. the last block can be 1-4 digits
final String chunkedConnectionNumber = String.join(" ",
areaCodeAndConnectionNumber.connectionNumber.split("(?<=\\G.{4})"));
final String chunkedConnectionNumber = String.join(" ", nationalNumber.connectionNumber.split("(?<=\\G.{4})"));

if (StringUtils.isBlank(areaCodeAndConnectionNumber.areaCode)) {
if (StringUtils.isBlank(nationalNumber.areaCode)) {
return String.format("+%d %s", countryCode, chunkedConnectionNumber);
} else {
// target format
return String.format("+%d %s %s", countryCode, areaCodeAndConnectionNumber.areaCode,
chunkedConnectionNumber);
return String.format("+%d %s %s", countryCode, nationalNumber.areaCode, chunkedConnectionNumber);
}
}

Expand All @@ -249,17 +261,21 @@ private String chunkAndFormatResult(int countryCode, AreaCodeAndConnectionNumber
*/
private String trimPhoneNumber(String phoneNumber) {

logger().debug("trim number:'{}' remove spaces, new Lines, '/', '-' and '()'.", phoneNumber);

String phoneNumberWithoutNewLine = phoneNumber.replaceAll("\\s+", "").replaceAll("\n", "");
String phoneNumberWithoutBraces = phoneNumberWithoutNewLine.replaceAll("\\(", "").replaceAll("\\)", "");

return phoneNumberWithoutNewLine.replaceAll("/", "").replaceAll("-", "");
return phoneNumberWithoutBraces.replaceAll("/", "").replaceAll("-", "");
}

private class AreaCodeAndConnectionNumber {
// National number contains areaCode and connectionNumber
private class NationalNumber {

public final String areaCode;
public final String connectionNumber;

private AreaCodeAndConnectionNumber(String areaCode, String connectionNumber) {
private NationalNumber(String areaCode, String connectionNumber) {

this.areaCode = areaCode;
this.connectionNumber = connectionNumber;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package net.contargo.types.telephony.validation;

public enum PhoneNumberValidationResult {

ZERO_NUMBER,
CAN_NOT_FORMATTED,
TOO_LARGE
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.List;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

Expand All @@ -14,7 +17,9 @@
* @author Robin Jayasinghe - [email protected]
* @author Olle Törnström - [email protected]
*/
public class ValidPhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
public class ValidPhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, PhoneNumber> {

private static final int PHONE_NUMBER_SIZE = 64;

@Override
public void initialize(ValidPhoneNumber a) {
Expand All @@ -24,21 +29,73 @@ public void initialize(ValidPhoneNumber a) {


@Override
public boolean isValid(String value, ConstraintValidatorContext cvc) {

PhoneNumber phoneNumber = new PhoneNumber(value);
public boolean isValid(PhoneNumber value, ConstraintValidatorContext cvc) {

// We must assume non-null, non-empty is validated elsewhere
if (StringUtils.isBlank(value) || phoneNumber.getRawPhoneNumber().trim().isEmpty()) {
if (value == null) {
return true;
}

return phoneNumber.isPhoneNumber() && !isPhoneNumberOnlyZeros(phoneNumber);
List<PhoneNumberValidationResult> validationResults = checkPhoneNumber(value);

validationResults.forEach(validationResult -> {
final String messageTemplate;
String propertyName = "phone";

if (value.isMobile()) {
propertyName = "mobile";
}

switch (validationResult) {
case ZERO_NUMBER:
messageTemplate = "{ZERO_NUMBER}";
break;

case CAN_NOT_FORMATTED:
messageTemplate = "{CAN_NOT_FORMATTED}";
break;

case TOO_LARGE:
messageTemplate = "{TOO_LARGE}";
break;

default:
throw new IllegalArgumentException("unknown validation result for contact info");
}

reportConstraintViolation(cvc, messageTemplate, propertyName);
});

return validationResults.isEmpty();
}


private List<PhoneNumberValidationResult> checkPhoneNumber(final PhoneNumber phoneNumber) {

List<PhoneNumberValidationResult> phoneNumberValidationResults = new ArrayList<>();

if (!StringUtils.isBlank(phoneNumber.getRawPhoneNumber())) {
if (phoneNumber.containsOnlyZeros()) {
phoneNumberValidationResults.add(PhoneNumberValidationResult.ZERO_NUMBER);
}

if (!phoneNumber.canBeFormatted()) {
phoneNumberValidationResults.add(PhoneNumberValidationResult.CAN_NOT_FORMATTED);
}

if (phoneNumber.getRawPhoneNumber().length() > PHONE_NUMBER_SIZE) {
phoneNumberValidationResults.add(PhoneNumberValidationResult.TOO_LARGE);
}
}

return phoneNumberValidationResults;
}


private boolean isPhoneNumberOnlyZeros(PhoneNumber phoneNumber) {
private void reportConstraintViolation(final ConstraintValidatorContext context, final String messageTemplate,
final String propertyName) {

return phoneNumber.getRawPhoneNumber().matches("0+$");
context.buildConstraintViolationWithTemplate(messageTemplate)
.addPropertyNode(propertyName).addConstraintViolation();
}
}
Loading

0 comments on commit b4c2f28

Please sign in to comment.