BuildingNr.java
/*
* Copyright (C) 2020-2023 Dipl.-Inform. Kai Hofmann. All rights reserved!
*/
package de.powerstat.validation.values;
import java.util.Objects;
import java.util.regex.Pattern;
import de.powerstat.validation.interfaces.IValueObject;
/**
* Address Building number.
*
* Possibly DSGVO relevant.
*
* TODO optimize constructor/compareTo
*/
public final class BuildingNr implements Comparable<BuildingNr>, IValueObject
{
/* *
* Logger.
*/
// private static final Logger LOGGER = LogManager.getLogger(BuildingNr.class);
/* *
* Cache for singletons.
*/
// private static final Map<String, BuildingNr> CACHE = new WeakHashMap<>();
/**
* Building nr regexp.
*/
private static final Pattern BUILDINGNR_REGEXP = Pattern.compile("^(\\d{1,5})(([-/])(\\d{1,5}))?( (\\d{1,3})/(\\d{1,3}))?( ([a-z]))?$"); //$NON-NLS-1$
/**
* Maximum known building nr in the world.
*/
private static final int MAX_KNOWN_BUILDING_NR = 29999;
/**
* Building number.
*/
private final String buildingNr;
/**
* Constructor.
*
* @param buildingNr Building number
* @throws NullPointerException if buildingNr is null
* @throws IllegalArgumentException if buildingNr is not an correct buildingNr
*/
private BuildingNr(final String buildingNr)
{
super();
Objects.requireNonNull(buildingNr, "buildingNr"); //$NON-NLS-1$
if ((buildingNr.length() < 1) || (buildingNr.length() > 21))
{
throw new IllegalArgumentException("BuildingNr with wrong length"); //$NON-NLS-1$
}
final var matcher = BuildingNr.BUILDINGNR_REGEXP.matcher(buildingNr);
if (!matcher.matches())
{
throw new IllegalArgumentException("BuildingNr with wrong format"); //$NON-NLS-1$
}
// group 1: building nr (from) 42: 42
// group 4: building nr (to) 42-43: 43
// group 6: numerator 3/4: 3
// group 7: denominator 3/4: 4
// group 8: alphabetic character: a
if (Integer.parseInt(matcher.group(1)) > BuildingNr.MAX_KNOWN_BUILDING_NR)
{
throw new IllegalArgumentException("BuildingNr > " + BuildingNr.MAX_KNOWN_BUILDING_NR); //$NON-NLS-1$
}
if ((matcher.group(4) != null) && (Integer.compare(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(4))) >= 0))
{
throw new IllegalArgumentException("BuildingNr from >= BuildingNr to"); //$NON-NLS-1$
}
if ((matcher.group(7) != null) && (Integer.compare(Integer.parseInt(matcher.group(6)), Integer.parseInt(matcher.group(7))) > 0))
{
throw new IllegalArgumentException("BuildingNr numerator > denominator"); //$NON-NLS-1$
}
this.buildingNr = buildingNr;
}
/**
* BuildingNr factory.
*
* @param buildingNr Building number
* @return BuildingNr object
*/
public static BuildingNr of(final String buildingNr)
{
/*
synchronized (BuildingNr.class)
{
BuildingNr obj = BuildingNr.CACHE.get(buildingNr);
if (obj != null)
{
return obj;
}
obj = new BuildingNr(buildingNr);
BuildingNr.CACHE.put(buildingNr, obj);
return obj;
}
*/
return new BuildingNr(buildingNr);
}
/**
* Returns the value of this BuildingNr as a string.
*
* @return The text value represented by this object after conversion to type string.
*/
@Override
public String stringValue()
{
return this.buildingNr;
}
/**
* Calculate hash code.
*
* @return Hash
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return this.buildingNr.hashCode();
}
/**
* Is equal with another object.
*
* @param obj Object
* @return true when equal, false otherwise
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof BuildingNr))
{
return false;
}
final BuildingNr other = (BuildingNr)obj;
return this.buildingNr.equals(other.buildingNr);
}
/**
* Returns the string representation of this BuildingNr.
*
* The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
*
* "BuildingNr[buildingNr=42]"
*
* @return String representation of this BuildingNr
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
final var builder = new StringBuilder(23);
builder.append("BuildingNr[buildingNr=").append(this.buildingNr).append(']'); //$NON-NLS-1$
return builder.toString();
}
/**
* Compare with another object.
*
* @param obj Object to compare with
* @return 0: equal; 1: greater; -1: smaller
* @see java.lang.Comparable#compareTo(java.lang.Object)
*
* TODO optimize (do regexp in constructor)
*/
@Override
public int compareTo(final BuildingNr obj)
{
Objects.requireNonNull(obj, "obj"); //$NON-NLS-1$
final var matcher1 = BuildingNr.BUILDINGNR_REGEXP.matcher(this.buildingNr);
final var matcher2 = BuildingNr.BUILDINGNR_REGEXP.matcher(obj.buildingNr);
/* boolean result1 = */ matcher1.matches();
/* boolean result2 = */ matcher2.matches();
// group 1: building nr (from) 42: 42
// group 4: building nr (to) 42-43: 43
// group 6: numerator 3/4: 3
// group 7: denominator 3/4: 4
// group 8: alphabetic character: a
int result = Integer.compare(Integer.parseInt(matcher1.group(1)), Integer.parseInt(matcher2.group(1)));
if (result == 0)
{
if ((matcher1.group(7) != null) || (matcher2.group(7) != null)) // 3/4
{
if (matcher1.group(7) == null)
{
return -1;
}
if (matcher2.group(7) == null)
{
return 1;
}
if (matcher1.group(7).compareTo(matcher2.group(7)) != 0)
{
throw new IllegalStateException("BuildingNrs do not have the same denominator"); //$NON-NLS-1$
}
result = Integer.compare(Integer.parseInt(matcher1.group(6)), Integer.parseInt(matcher2.group(6)));
if (result != 0)
{
return result;
}
}
if ((matcher1.group(8) != null) || (matcher2.group(8) != null)) // a-z
{
if (matcher1.group(8) == null)
{
result = -1;
}
else if (matcher2.group(8) == null)
{
result = 1;
}
else
{
result = matcher1.group(8).compareTo(matcher2.group(8));
}
}
}
return result;
}
}