Skip to content

Commit

Permalink
Email address format validation (#312)
Browse files Browse the repository at this point in the history
* Email address format validation

* Update regx for email format

* Allowing empty email in email format validation to avoid schematron validation duplication
  • Loading branch information
wangf1122 authored Mar 27, 2023
1 parent 0a60118 commit 8aa2267
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 14 deletions.
19 changes: 19 additions & 0 deletions src/main/java/ca/gc/schema/iso19139hnap/util/XslUtilHnap.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class XslUtilHnap {
Expand Down Expand Up @@ -254,4 +256,21 @@ String removeFromUrl(ArrayList<NodeInfo> parametersToRemove, String url) {

return result;
}

/**
* Validate if email format. Empty email is allowed based on parameter of allowEmptyEmail.
* See more info: https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
* @param emailAddress email address to validate the pattern
* @param allowEmptyEmail allow empty email input. The empty email was handled within the schematron already. This flag allows to ignore repeat checking.
* @return boolean
*/
public static boolean isEmailFormat(String emailAddress, boolean allowEmptyEmail) {
if (allowEmptyEmail && org.apache.commons.lang3.StringUtils.isEmpty(emailAddress)) {
return true;
}

Pattern pattern = Pattern.compile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$");
Matcher matcher = pattern.matcher(emailAddress);
return matcher.matches();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,6 @@
<ECThesaurusOrg>Thesaurus cited responsible party organisation is required</ECThesaurusOrg>
<ECThesaurusRole>Thesaurus cited responsible party role is required</ECThesaurusRole>
<ECThesaurusEmail>Thesaurus cited responsible party email is required</ECThesaurusEmail>

<ElectronicMailFormat>Electronic mail address format is invalid (for example [email protected])</ElectronicMailFormat>
</strings>
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@
<ResourceDescriptionContentType>Online resource description content type is not valid. Valid values are: Web Service,Service Web,Dataset,Données,API,Application,Supporting Document,Document de soutien</ResourceDescriptionContentType>
<ResourceDescriptionFormat>Online resource description format is not valid. Valid values are:</ResourceDescriptionFormat>
<ResourceDescriptionLanguage>Online resource description language is not valid. Should be a comma separated values of ISO-LANG-3 codes</ResourceDescriptionLanguage>

<ElectronicMailFormat>Electronic mail address format is invalid (for example [email protected])</ElectronicMailFormat>
</strings>
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,6 @@
<ECThesaurusOrg>Le nom de l’organisation responsable du thésaurus est requis</ECThesaurusOrg>
<ECThesaurusRole>Le rôle du responsable du thesaurus est requis</ECThesaurusRole>
<ECThesaurusEmail>Le courriel du responsable du thésaurus est requis</ECThesaurusEmail>

<ElectronicMailFormat>Le format de l'adresse électronique n'est pas valide (par exemple [email protected])</ElectronicMailFormat>
</strings>
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@
<ResourceDescriptionContentType>Le type de contenu dans la description de la ressource en ligne n'est pas valide. Les valeurs valides sont : Web Service,Service Web,Dataset,Données,API,Application,Supporting Document,Document de soutien</ResourceDescriptionContentType>
<ResourceDescriptionFormat>Le format dans la description de la ressource en ligne n’est pas valide. Les valeurs valides sont : </ResourceDescriptionFormat>
<ResourceDescriptionLanguage>La langue dans la description de la ressource en ligne n’est pas valide. Devrait être des valeurs de code ISO-LANG-3 séparées par des virgules</ResourceDescriptionLanguage>

<ElectronicMailFormat>Le format de l'adresse électronique n'est pas valide (par exemple [email protected])</ElectronicMailFormat>
</strings>
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
/>
</xsl:function>


<!-- =============================================================
EC schematron rules for multilingual validation in metadata editor:
============================================================= -->
Expand Down Expand Up @@ -348,17 +349,23 @@

<!-- Contact - Electronic Mail -->
<sch:rule context="//gmd:contact/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress">
<sch:let name="emailAddress" value="string(gco:CharacterString)" />
<sch:let name="emailAddressOtherLang" value="string(gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale=concat('#', $altLanguageId)])" />

<sch:let name="missing" value="not(string(gco:CharacterString))
<sch:let name="missing" value="not($emailAddress)
or (@gco:nilReason)" />

<sch:let name="missingOtherLang" value="not(string(gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale=concat('#', $altLanguageId)]))" />
<sch:let name="missingOtherLang" value="not($emailAddressOtherLang)" />

<sch:let name="isEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddress, true())"/>
<sch:let name="isOtherLangEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddressOtherLang, true())"/>

<sch:assert
test="not($missing) and not($missingOtherLang)"

>$loc/strings/ContactElectronicMail</sch:assert>

<sch:assert test="string($isEmailAddressFormat) ='true' and string($isOtherLangEmailAddressFormat) ='true'">$loc/strings/ElectronicMailFormat</sch:assert>
</sch:rule>


Expand Down Expand Up @@ -570,17 +577,24 @@
<sch:rule context="//gmd:identificationInfo/*/gmd:citation/*/gmd:citedResponsibleParty/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress
|//*[@gco:isoType='gmd:MD_DataIdentification']/gmd:citation/*/gmd:citedResponsibleParty/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress
|//*[@gco:isoType='srv:SV_ServiceIdentification']/gmd:citation/*/gmd:citedResponsibleParty/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress">
<sch:let name="emailAddress" value="string(gco:CharacterString)" />
<sch:let name="emailAddressOtherLang" value="string(gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale=concat('#', $altLanguageId)])" />

<sch:let name="missing" value="not(string(gco:CharacterString))
<sch:let name="missing" value="not($emailAddress)
or (@gco:nilReason)" />

<sch:let name="missingOtherLang" value="not(string(gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale=concat('#', $altLanguageId)]))" />
<sch:let name="missingOtherLang" value="not($emailAddressOtherLang)" />

<sch:let name="isEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddress, true())"/>
<sch:let name="isOtherLangEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddressOtherLang, true())"/>

<sch:assert
test="not($missing) and not($missingOtherLang)"

>$loc/strings/CitedResponsiblePartyElectronicMail</sch:assert>

<sch:assert test="string($isEmailAddressFormat) ='true' and string($isOtherLangEmailAddressFormat) ='true'">$loc/strings/ElectronicMailFormat</sch:assert>

</sch:rule>


Expand Down Expand Up @@ -673,14 +687,22 @@

<sch:let name="emailPresent" value="count(gmd:MD_Keywords/gmd:thesaurusName/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty/gmd:contactInfo/gmd:CI_Contact/gmd:address/gmd:CI_Address/gmd:electronicMailAddress) > 0" />

<sch:let name="missingEmail" value="not(string(gmd:MD_Keywords/gmd:thesaurusName/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty/gmd:contactInfo/gmd:CI_Contact/gmd:address/gmd:CI_Address/gmd:electronicMailAddress/gco:CharacterString))
<sch:let name="emailAddress" value="string(gmd:MD_Keywords/gmd:thesaurusName/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty/gmd:contactInfo/gmd:CI_Contact/gmd:address/gmd:CI_Address/gmd:electronicMailAddress/gco:CharacterString)" />
<sch:let name="emailAddressOtherLang" value="string(gmd:MD_Keywords/gmd:thesaurusName/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty/gmd:contactInfo/gmd:CI_Contact/gmd:address/gmd:CI_Address/gmd:electronicMailAddress/gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale=concat('#', $altLanguageId)])" />

<sch:let name="missingEmail" value="not($emailAddress)
or (@gco:nilReason)" />

<sch:let name="missingEmailOtherLang" value="not(string(gmd:MD_Keywords/gmd:thesaurusName/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty/gmd:contactInfo/gmd:CI_Contact/gmd:address/gmd:CI_Address/gmd:electronicMailAddress/gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale=concat('#', $altLanguageId)]))" />
<sch:let name="missingEmailOtherLang" value="not($emailAddressOtherLang)" />

<sch:let name="isEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddress, true())"/>
<sch:let name="isOtherLangEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddressOtherLang, true())"/>

<sch:assert
test="not($thesaurusNamePresent) or ($thesaurusNamePresent and (not($emailPresent) or ($emailPresent and not($missingEmail) and not($missingEmailOtherLang))))"
>$loc/strings/ECThesaurusEmail</sch:assert>

<sch:assert test="string($isEmailAddressFormat) ='true' and string($isOtherLangEmailAddressFormat) ='true'">$loc/strings/ElectronicMailFormat</sch:assert>
</sch:rule>

<!-- Supplemental information -->
Expand Down Expand Up @@ -973,17 +995,24 @@

<!-- Distributor - Electronic Mail -->
<sch:rule context="//gmd:distributionInfo/*/gmd:distributor/gmd:MD_Distributor/gmd:distributorContact/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress">
<sch:let name="emailAddress" value="string(gco:CharacterString)" />
<sch:let name="emailAddressOtherLang" value="string(gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale=concat('#', $altLanguageId)])" />

<sch:let name="missing" value="not(string(gco:CharacterString))
<sch:let name="missing" value="not($emailAddress)
or (@gco:nilReason)" />

<sch:let name="missingOtherLang" value="not(string(gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale=concat('#', $altLanguageId)]))" />
<sch:let name="missingOtherLang" value="not($emailAddressOtherLang)" />

<sch:let name="isEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddress, true())"/>
<sch:let name="isOtherLangEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddressOtherLang, true())"/>

<sch:assert
test="not($missing) and not($missingOtherLang)"

>$loc/strings/DistributorElectronicMail</sch:assert>

<sch:assert test="string($isEmailAddressFormat) ='true' and string($isOtherLangEmailAddressFormat) ='true'">$loc/strings/ElectronicMailFormat</sch:assert>

</sch:rule>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
/>
</xsl:function>


<!-- =============================================================
EC schematron rules for multilingual validation in metadata editor:
============================================================= -->
Expand Down Expand Up @@ -220,15 +221,20 @@

<!-- Contact - Electronic Mail -->
<sch:rule context="//gmd:contact/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress">
<sch:let name="emailAddress" value="string(gco:CharacterString)" />

<sch:let name="missing" value="not(string(gco:CharacterString))
<sch:let name="missing" value="not($emailAddress)
or (@gco:nilReason)" />

<sch:let name="isEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddress, true())"/>

<sch:assert
test="not($missing)"

>$loc/strings/ContactElectronicMail</sch:assert>

<sch:assert test="string($isEmailAddressFormat) ='true'">$loc/strings/ElectronicMailFormat</sch:assert>

</sch:rule>
</sch:pattern>

Expand Down Expand Up @@ -318,15 +324,17 @@
|//*[@gco:isoType='gmd:MD_DataIdentification']/gmd:citation/*/gmd:citedResponsibleParty/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress
|//*[@gco:isoType='srv:SV_ServiceIdentification']/gmd:citation/*/gmd:citedResponsibleParty/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress">

<sch:let name="missing" value="not(string(gco:CharacterString))
or (@gco:nilReason)" />

<sch:let name="emailAddress" value="string(gco:CharacterString)" />
<sch:let name="missing" value="not($emailAddress) or (@gco:nilReason)" />
<sch:let name="isEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddress, true())"/>

<sch:assert
test="not($missing)"

>$loc/strings/CitedResponsiblePartyElectronicMail</sch:assert>

<sch:assert test="string($isEmailAddressFormat) ='true'">$loc/strings/ElectronicMailFormat</sch:assert>

</sch:rule>

<!-- Keywords -->
Expand Down Expand Up @@ -394,12 +402,17 @@

<sch:let name="emailPresent" value="count(gmd:MD_Keywords/gmd:thesaurusName/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty/gmd:contactInfo/gmd:CI_Contact/gmd:address/gmd:CI_Address/gmd:electronicMailAddress) > 0" />

<sch:let name="missingEmail" value="not(string(gmd:MD_Keywords/gmd:thesaurusName/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty/gmd:contactInfo/gmd:CI_Contact/gmd:address/gmd:CI_Address/gmd:electronicMailAddress/gco:CharacterString))
<sch:let name="emailAddress" value="string(gmd:MD_Keywords/gmd:thesaurusName/gmd:CI_Citation/gmd:citedResponsibleParty/gmd:CI_ResponsibleParty/gmd:contactInfo/gmd:CI_Contact/gmd:address/gmd:CI_Address/gmd:electronicMailAddress/gco:CharacterString)" />
<sch:let name="missingEmail" value="not($emailAddress)
or (@gco:nilReason)" />
<sch:let name="isEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddress, true())"/>

<sch:assert
test="not($thesaurusNamePresent) or ($thesaurusNamePresent and (not($emailPresent) or ($emailPresent and not($missingEmail))))"
>$loc/strings/ECThesaurusEmail</sch:assert>

<sch:assert test="string($isEmailAddressFormat) ='true'">$loc/strings/ElectronicMailFormat</sch:assert>

</sch:rule>

<!-- Note (Other constraints) -->
Expand Down Expand Up @@ -555,14 +568,19 @@
<!-- Distributor - Electronic Mail -->
<sch:rule context="//gmd:distributionInfo/*/gmd:distributor/gmd:MD_Distributor/gmd:distributorContact/*/gmd:contactInfo/*/gmd:address/gmd:CI_Address/gmd:electronicMailAddress">

<sch:let name="missing" value="not(string(gco:CharacterString))
<sch:let name="emailAddress" value="string(gco:CharacterString)" />
<sch:let name="missing" value="not($emailAddress)
or (@gco:nilReason)" />
<sch:let name="isEmailAddressFormat" value="XslUtilHnap:isEmailFormat($emailAddress, true())"/>

<sch:assert
test="not($missing)"

>$loc/strings/DistributorElectronicMail</sch:assert>


<sch:assert test="string($isEmailAddressFormat) ='true'">$loc/strings/ElectronicMailFormat</sch:assert>

</sch:rule>

</sch:pattern>
Expand Down
18 changes: 18 additions & 0 deletions src/test/java/ca/gc/schema/iso19139hnap/util/XslUtilHnapTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import java.util.ArrayList;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
* Tests for {@link XslUtilHnap} class.
Expand Down Expand Up @@ -133,6 +135,22 @@ public void removeFromUrl() {

}

@Test
public void testIsEmailAddress() {
assertTrue(XslUtilHnap.isEmailFormat("o'brian.test@localhost", false));
assertTrue(XslUtilHnap.isEmailFormat("o'[email protected]", false));

assertFalse(XslUtilHnap.isEmailFormat("o'[email protected].", false));
assertFalse(XslUtilHnap.isEmailFormat("o'brian.test@localhost.", false));
assertFalse(XslUtilHnap.isEmailFormat("o'brian.test@", false));
assertFalse(XslUtilHnap.isEmailFormat("o'brian.test", false));

assertFalse(XslUtilHnap.isEmailFormat("",false));
assertTrue(XslUtilHnap.isEmailFormat("",true));
assertTrue(XslUtilHnap.isEmailFormat(null,true));

}

class MyTinyNodeImpl extends TinyNodeImpl {

private String value;
Expand Down

0 comments on commit 8aa2267

Please sign in to comment.