EMail.java
/*
* Copyright (C) 2020-2023 Dipl.-Inform. Kai Hofmann. All rights reserved!
*/
package de.powerstat.validation.values;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Pattern;
import de.powerstat.validation.interfaces.IValueObject;
/**
* Electronic mail.
*
* Probably DSGVO relevant.
*
* TODO Hostname exists?
* TODO email exists check
*/
public final class EMail implements Comparable<EMail>, IValueObject
{
/* *
* Cache for singletons.
*/
// private static final Map<String, EMail> CACHE = new WeakHashMap<>();
/**
* Local part regexp.
*/
private static final Pattern LOCAL_REGEXP = Pattern.compile("^[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+$"); //$NON-NLS-1$
/**
* EMail.
*/
private final String email;
/**
* EMails domain part.
*/
private final Hostname domainPart;
/**
* EMails local part.
*/
private final String localPart;
/**
* Constructor.
*
* Comments, double quotes and UTF-8 characters within the emails local part are not yet supported.
*
* @param email EMail
* @throws NullPointerException if email is null
* @throws IllegalArgumentException if email is not an supported email address
*/
private EMail(final String email)
{
super();
Objects.requireNonNull(email, "email"); //$NON-NLS-1$
if ((email.length() < 6) || (email.length() > 254))
{
throw new IllegalArgumentException("To short or long for an email address"); //$NON-NLS-1$
}
final String[] parts = email.split("@"); //$NON-NLS-1$
if (parts.length != 2)
{
throw new IllegalArgumentException("Not an email address, missing or to much @"); //$NON-NLS-1$
}
if (parts[0].length() > 64)
{
throw new IllegalArgumentException("Local part greater than 64 characters"); //$NON-NLS-1$
}
if (parts[1].charAt(0) == '[')
{
parts[1] = (parts[1].toLowerCase(Locale.getDefault()).startsWith("[ipv6:")) ? parts[1].substring(6) : parts[1].substring(1); //$NON-NLS-1$
if (!parts[1].endsWith("]")) //$NON-NLS-1$
{
throw new IllegalArgumentException("Missing end of IPv4/IPv6 address"); //$NON-NLS-1$
}
parts[1] = parts[1].substring(0, parts[1].length() - 1);
}
this.domainPart = Hostname.of(parts[1]); // Check hostname and store for isReachable
if ((parts[0].charAt(0) == '(') || (parts[0].charAt(parts[0].length() - 1) == ')'))
{
throw new IllegalArgumentException("Comments in email addresses are not supported"); //$NON-NLS-1$
}
if (parts[0].indexOf('"') > -1)
{
throw new IllegalArgumentException("Double quotes in email addresses are not supported"); //$NON-NLS-1$
}
if ((parts[0].charAt(0) == '.') || (parts[0].charAt(parts[0].length() - 1) == '.'))
{
throw new IllegalArgumentException("A dot is not allowed at start or end of an emails local part"); //$NON-NLS-1$
}
if (parts[0].contains("..")) //$NON-NLS-1$
{
throw new IllegalArgumentException("Two or more dots behind each other are not allowed within an emails local part"); //$NON-NLS-1$
}
if (!EMail.LOCAL_REGEXP.matcher(parts[0]).matches())
{
throw new IllegalArgumentException("Illegal character found in emails local part or unsupported UTF-8 character"); //$NON-NLS-1$
}
this.localPart = parts[0]; // Store for check receiver
this.email = email;
}
/**
* EMail factory.
*
* @param email EMail
* @return EMail object
*/
public static EMail of(final String email)
{
/*
synchronized (EMail.class)
{
EMail obj = EMail.CACHE.get(email);
if (obj != null)
{
return obj;
}
obj = new EMail(email);
EMail.CACHE.put(email, obj);
return obj;
}
*/
return new EMail(email);
}
/**
* Returns the value of this EMail as a string.
*
* @return The text value represented by this object after conversion to type string.
*/
@Override
public String stringValue()
{
return this.email;
}
/**
* Get emails domain part string.
*
* @return Domain part string
*/
public String getDomainPart()
{
return this.domainPart.stringValue();
}
/**
* Get emails reverse domain part string.
*
* @return Reverse domain part string
*/
public String getReverseDomainPart()
{
return this.domainPart.getReverseHostname();
}
/**
* Get emails local part string.
*
* @return Local part string
*/
public String getLocalPart()
{
return this.localPart;
}
/**
* Calculate hash code.
*
* @return Hash
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return this.email.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 EMail))
{
return false;
}
final EMail other = (EMail)obj;
return this.email.equals(other.email);
}
/**
* Returns the string representation of this EMail.
*
* The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
*
* "EMail[email=user@example.com]"
*
* @return String representation of this EMail
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
final var builder = new StringBuilder();
builder.append("EMail[email=").append(this.email).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)
*/
@Override
public int compareTo(final EMail obj)
{
Objects.requireNonNull(obj, "obj"); //$NON-NLS-1$
return this.email.compareTo(obj.email); // TODO hostname, username
}
}