diff --git a/src/main/java/org/filteredpush/qc/metadata/DwCMetadataDQ.java b/src/main/java/org/filteredpush/qc/metadata/DwCMetadataDQ.java index 2dbeead..9104bf3 100644 --- a/src/main/java/org/filteredpush/qc/metadata/DwCMetadataDQ.java +++ b/src/main/java/org/filteredpush/qc/metadata/DwCMetadataDQ.java @@ -18,6 +18,8 @@ */ package org.filteredpush.qc.metadata; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -49,8 +51,11 @@ import org.datakurator.ffdq.api.result.ComplianceValue; import org.datakurator.ffdq.api.result.IssueValue; import org.datakurator.ffdq.model.ResultState; +import org.filteredpush.qc.metadata.util.LSID; import org.filteredpush.qc.metadata.util.MetadataSingleton; import org.filteredpush.qc.metadata.util.MetadataUtils; +import org.filteredpush.qc.metadata.util.RFC8141URN; +import org.filteredpush.qc.metadata.util.URNFormatException; /** * Provides implementation of TDWG BDQ TG2 OTHER tests (related to metadata found in Record-level @@ -63,25 +68,25 @@ * #99 15f78619-811a-4c6f-997a-a4c7888ad849 VALIDATION_LICENSE_NOTEMPTY * #47 c486546c-e6e5-48a7-b286-eba7f5ca56c4 VALIDATION_OCCURRENCEID_NOTEMPTY * #117 eb4a17f6-6bea-4cdd-93dd-d5a7e9d1eccf VALIDATION_OCCURRENCESTATUS_NOTEMPTY - * #104 42408a00-bf71-4892-a399-4325e2bc1fb8 VALIDATION_BASISOFRECORD_STANDARD * #116 7af25f1e-a4e2-4ff4-b161-d1f25a5c3e47 VALIDATION_OCCURRENCESTATUS_STANDARD + * #104 42408a00-bf71-4892-a399-4325e2bc1fb8 VALIDATION_BASISOFRECORD_STANDARD * #91 cdaabb0d-a863-49d0-bc0f-738d771acba5 VALIDATION_DCTYPE_STANDARD * #38 3136236e-04b6-49ea-8b34-a65f25e3aba1 VALIDATION_LICENSE_STANDARD * #41 bd385eeb-44a2-464b-a503-7abe407ef904 AMENDMENT_DCTYPE_STANDARDIZED * 63 07c28ace-561a-476e-a9b9-3d5ad6e35933 AMENDMENT_BASISOFRECORD_STANDARDIZED - * + * #75 96667a0a-ae59-446a-bbb0-b7f2b0ca6cf5 AMENDMENT_OCCURRENCESTATUS_ASSUMEDDEFAULT + * #115 f8f3a093-042c-47a3-971a-a482aaaf3b75 AMENDMENT_OCCURRENCESTATUS_STANDARDIZED + * #277 5424e933-bee7-4125-839e-d8743ea69f93 VALIDATION_PATHWAY_STANDARD + * #285 4833a522-12eb-4fe0-b4cf-7f7a337a6048 VALIDATION_TYPESTATUS_STANDARD * * TODO: Implement: * * 29 fecaa8a3-bbd8-4c5a-a424-13c37c4bb7b1 ISSUE_ANNOTATION_NOTEMPTY * 133 dcbe5bd2-42a0-4aab-bb4d-8f148c6490f8 AMENDMENT_LICENSE_STANDARDIZED - * 75 96667a0a-ae59-446a-bbb0-b7f2b0ca6cf5 AMENDMENT_OCCURRENCESTATUS_ASSUMEDDEFAULT - * 115 f8f3a093-042c-47a3-971a-a482aaaf3b75 AMENDMENT_OCCURRENCESTATUS_STANDARDIZED - * 23 3cfe9ab4-79f8-4afd-8da5-723183ef16a3 VALIDATION_OCCURRENCEID_STANDARD - * 46 3f335517-f442-4b98-b149-1e87ff16de45 VALIDATION_SCIENTIFICNAME_FOUND * * And the following supplementary tests: * + * #23 VALIDATION_OCCURRENCEID_STANDARD 3cfe9ab4-79f8-4afd-8da5-723183ef16a3 * #235 VALIDATION_LIFESTAGE_NOTEMPTY 34b9eec9-03d5-4dc9-94b7-5b05ddcaaa87 * #270 VALIDATION_LIFESTAGE_STANDARD be40d19e-1fe7-42ed-b9d0-961f4cf3eb6a * #225 VALIDATION_DISPOSITION_NOTEMPTY b4c17611-2703-474f-b46a-93b08ecfee16 @@ -104,6 +109,9 @@ public class DwCMetadataDQ { private static final Log logger = LogFactory.getLog(DwCMetadataDQ.class); + /** + * a list of dc:type literal values. + */ protected static final String dcTypeLiterals = "Collection,Dataset,Event,Image,InteractiveResource,MovingImage,PhysicalObject,Service,Software,Sound,StillImage,Text"; /** @@ -454,28 +462,112 @@ public static DQResponse validationOccurrencestatusNotempty(@Ac } /** - * Does the value of dwc:occurrenceID occur in bdq:SourceAuthority? + * Does dwc:occurrenceID contain a valid identifier?? * * Provides: 23 VALIDATION_OCCURRENCEID_STANDARD - * Version: 2023-09-17 + * Version: 2024-04-02 * * @param occurrenceID the provided dwc:occurrenceID to evaluate as ActedUpon. * @return DQResponse the response of type ComplianceValue to return */ - @Validation(label="VALIDATION_OCCURRENCEID_STANDARD", description="Does the value of dwc:occurrenceID occur in bdq:SourceAuthority?") + @Validation(label="VALIDATION_OCCURRENCEID_STANDARD", description="Does dwc:occurrenceID contain a valid identifier?") @Provides("3cfe9ab4-79f8-4afd-8da5-723183ef16a3") - @ProvidesVersion("https://rs.tdwg.org/bdq/terms/3cfe9ab4-79f8-4afd-8da5-723183ef16a3/2023-09-17") - @Specification("INTERNAL_PREREQUISITES_NOT_MET if dwc:occurrenceID is EMPTY; COMPLIANT if the value of dwc:occurrenceID follows a format commonly associated with globally unique identifiers (GUIDs); otherwise NOT_COMPLIANT ") + @ProvidesVersion("https://rs.tdwg.org/bdq/terms/3cfe9ab4-79f8-4afd-8da5-723183ef16a3/2024-04-02") + @Specification("INTERNAL_PREREQUISITES_NOT_MET if dwc:ocurrenceID is EMPTY; COMPLIANT if (1) dwc:occurrenceID is a validly formed LSID, or (2) dwc:occurrenceID is a validly formed URN with at least NID and NSS present, or (3) dwc:occurrenceID is in the form scope:value, or (4) dwc:occurrenceID is a validly formed URI with host and path where path consists of more than just \"/\"; otherwise NOT_COMPLIANT.") public static DQResponse validationOccurrenceidStandard( @ActedUpon("dwc:occurrenceID") String occurrenceID ) { DQResponse result = new DQResponse(); - //TODO: Implement specification - // INTERNAL_PREREQUISITES_NOT_MET if dwc:occurrenceID is EMPTY; - // COMPLIANT if the value of dwc:occurrenceID follows a format - // commonly associated with globally unique identifiers (GUIDs); - // otherwise NOT_COMPLIANT + // Specification + // INTERNAL_PREREQUISITES_NOT_MET if dwc:ocurrenceID is EMPTY; COMPLIANT if (1) + // dwc:occurrenceID is a validly formed LSID, or (2) dwc:occurrenceID is a + // validly formed URN with at least NID and NSS present, or (3) dwc:occurrenceID + // is in the form scope:value, or (4) dwc:occurrenceID is a validly formed URI + // with host and path where path consists of more than just "/"; otherwise + // NOT_COMPLIANT. + + if (MetadataUtils.isEmpty(occurrenceID)) { + result.addComment("No value provided for scientificNameId."); + result.setResultState(ResultState.INTERNAL_PREREQUISITES_NOT_MET); + } else if (occurrenceID.matches("^[0-9]+$")) { + result.addComment("Provided occurrenceID ["+ occurrenceID +"] is a bare integer without an authority and this is incomplete."); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.NOT_COMPLIANT); + } else { + try { + RFC8141URN urn = new RFC8141URN(occurrenceID); + if (urn.getNid().equalsIgnoreCase("lsid")) { + try { + LSID lsid = new LSID(occurrenceID); + lsid.getAuthority(); + lsid.getNamespace(); + lsid.getObjectID(); + result.addComment("Provided occurrenceID recognized as an LSID."); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.COMPLIANT); + } catch (URNFormatException e2) { + logger.debug(e2.getMessage()); + result.addComment("Provided value for occurrenceID ["+occurrenceID+"] claims to be an lsid, but is not correctly formatted as such."); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.NOT_COMPLIANT); + } + } else { + logger.debug(urn.getNid()); + logger.debug(urn.getNss()); + if (urn.getNid().length()>0 && urn.getNss().length()>0) { + result.addComment("Provided occurrenceID recognized as an URN."); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.COMPLIANT); + } else { + result.addComment("Provided occurrenceID appears to be a URN, but doesn't have both NID and NSS"); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.NOT_COMPLIANT); + } + } + } catch (URNFormatException e) { + logger.debug(e.getMessage()); + if (occurrenceID.toLowerCase().matches("^[a-z]+:[0-9]+$")) { + result.addComment("Provided occurrenceID ["+occurrenceID+"] matches the pattern scope:value where value is an integer."); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.COMPLIANT); + } else if (occurrenceID.toLowerCase().matches("^[a-z]+:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$")) { + result.addComment("Provided occurrenceID ["+occurrenceID+"] matches the pattern scope:value where value is a uuid."); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.COMPLIANT); + } else { + try { + URI uri = new URI(occurrenceID); + logger.debug(uri.getScheme()); + logger.debug(uri.getAuthority()); + logger.debug(uri.getHost()); + logger.debug(uri.getPath()); + if (uri.getHost()!=null && uri.getPath()!=null + && uri.getHost().length()>0 && uri.getPath().length()>0 + && !uri.getPath().equals("/")) { + if (uri.getHost().equalsIgnoreCase("www.gbif.org") && uri.getPath().equals("/occurrence/")) { + result.addComment("Provided occurrenceID recognized as GBIF occurrence URL, but lacks the ID ["+occurrenceID+"]"); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.NOT_COMPLIANT); + } else { + result.addComment("Provided occurrenceID recognized as an URI with host, and path."); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.COMPLIANT); + } + } else { + result.addComment("Provided occurrenceID may be a URI, but doesn't have host and path ["+occurrenceID+"]"); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.NOT_COMPLIANT); + } + } catch (URISyntaxException e1) { + logger.debug(e1); + result.addComment("Provided value for occurrenceID ["+occurrenceID+"] is not a LSID, URN, URI, or identifier in the form scope:value."); + result.setResultState(ResultState.RUN_HAS_RESULT); + result.setValue(ComplianceValue.NOT_COMPLIANT); + } + } + } + } return result; } @@ -672,6 +764,7 @@ public static DQResponse amendmentDctypeStandardized( * Version: 2023-07-24 * * @param basisOfRecord the provided dwc:basisOfRecord to evaluate as ActedUpon. + * @param sourceAuthority the bdq:sourceAuthority to consult. * @return DQResponse the response of type AmendmentValue to return */ @Amendment(label="AMENDMENT_BASISOFRECORD_STANDARDIZED", description="Propose amendment to the value of dwc:basisOfRecord using bdq:sourceAuthority.") @@ -810,34 +903,52 @@ public static DQResponse amendmentBasisofrecordStandardized( /** * Propose an amendment of the value of dwc:occurrenceStatus to the default parameter value if dwc:occurrenceStatus, dwc:individualCount and dwc:organismQuantity are empty. * - * Provides: AMENDMENT_OCCURRENCESTATUS_ASSUMEDDEFAULT + * Provides: 75 AMENDMENT_OCCURRENCESTATUS_ASSUMEDDEFAULT * Version: 2023-09-18 * * @param occurrenceStatus the provided dwc:occurrenceStatus to evaluate as ActedUpon. * @param individualCount the provided dwc:individualCount to evaluate as Consulted. * @param organismQuantity the provided dwc:organismQuantity to evaluate as Consulted. + * @param defaultOccurrenceStatus the value to use as the default dwc:occurrenceStatus. * @return DQResponse the response of type AmendmentValue to return */ @Amendment(label="AMENDMENT_OCCURRENCESTATUS_ASSUMEDDEFAULT", description="Propose an amendment of the value of dwc:occurrenceStatus to the default parameter value if dwc:occurrenceStatus, dwc:individualCount and dwc:organismQuantity are empty.") @Provides("96667a0a-ae59-446a-bbb0-b7f2b0ca6cf5") @ProvidesVersion("https://rs.tdwg.org/bdq/terms/96667a0a-ae59-446a-bbb0-b7f2b0ca6cf5/2023-09-18") @Specification("FILLED_IN the value of dwc:occurrenceStatus using the Parameter value if dwc:occurrence.Status, dwc:individualCount and dwc:organismQuantity are EMPTY; otherwise NOT_AMENDED dwc:occurrenceStatus default = 'present'") - public DQResponse amendmentOccurrencestatusAssumeddefault( + public static DQResponse amendmentOccurrencestatusAssumeddefault( @ActedUpon("dwc:occurrenceStatus") String occurrenceStatus, @Consulted("dwc:individualCount") String individualCount, - @Consulted("dwc:organismQuantity") String organismQuantity + @Consulted("dwc:organismQuantity") String organismQuantity, + @ActedUpon("bdq:defaultOccurrenceStatus") String defaultOccurrenceStatus ) { DQResponse result = new DQResponse(); - //TODO: Implement specification + // Specification // FILLED_IN the value of dwc:occurrenceStatus using the Parameter // value if dwc:occurrence.Status, dwc:individualCount and // dwc:organismQuantity are EMPTY; otherwise NOT_AMENDED dwc:occurrenceStatus // default = "present" - //TODO: Parameters. This test is defined as parameterized. - // dwc:occurrenceStatus + // TODO: ahead of specification + // Parameters. This test is defined as parameterized. + // bdq:defaultOccurrenceStatus + if (MetadataUtils.isEmpty(defaultOccurrenceStatus)) { + defaultOccurrenceStatus = "present"; + } + + if (MetadataUtils.isEmpty(occurrenceStatus) && MetadataUtils.isEmpty(individualCount) && MetadataUtils.isEmpty(organismQuantity)) { + result.addComment("Set dwc:occurrenceStatus to the default value " + defaultOccurrenceStatus); + result.setResultState(ResultState.FILLED_IN); + Map newValues = new HashMap<>(); + newValues.put("dwc:occurrenceStatus", defaultOccurrenceStatus) ; + result.setValue(new AmendmentValue(newValues)); + } else { + result.addComment("dwc:occurrenceStatus not changed, at least one of dwc:occurrenceStatus, dwc:individualCount, or dwc:organismQuantity contains a value."); + result.setResultState(ResultState.NOT_AMENDED); + } + return result; } @@ -895,17 +1006,17 @@ public static DQResponse validationDctypeStandard( /** * Propose amendment to the value of dwc:occurrenceStatus using bdq:sourceAuthority. * - * Provides: AMENDMENT_OCCURRENCESTATUS_STANDARDIZED - * Version: 2023-09-18 + * Provides: 115 AMENDMENT_OCCURRENCESTATUS_STANDARDIZED + * Version: 2024-07-26 * * @param occurrenceStatus the provided dwc:occurrenceStatus to evaluate as ActedUpon. * @return DQResponse the response of type AmendmentValue to return */ @Amendment(label="AMENDMENT_OCCURRENCESTATUS_STANDARDIZED", description="Propose amendment to the value of dwc:occurrenceStatus using bdq:sourceAuthority.") @Provides("f8f3a093-042c-47a3-971a-a482aaaf3b75") - @ProvidesVersion("https://rs.tdwg.org/bdq/terms/f8f3a093-042c-47a3-971a-a482aaaf3b75/2023-09-18") + @ProvidesVersion("https://rs.tdwg.org/bdq/terms/f8f3a093-042c-47a3-971a-a482aaaf3b75/2024-07-26") @Specification("EXTERNAL_PREREQUISITES_NOT_MET if the bdq:sourceAuthority is not available; INTERNAL_PREREQUISITES_NOT_MET if dwc:ocurrenceStatus is EMPTY; AMENDED the value of dwc:occurrenceStatus if could be unambiguously interpreted as a value in bdq:sourceAuthority; otherwise NOT_AMENDED bdq:sourceAuthority = 'Darwin Core occurrenceStatus' {https://dwc.tdwg.org/list/#dwc_occurrenceStatus} {dwc:occurrenceStatus vocabulary [https://rs.gbif.org/vocabulary/gbif/occurrence_status_2020-07-15.xml]}") - public DQResponse amendmentOccurrencestatusStandardized( + public static DQResponse amendmentOccurrencestatusStandardized( @ActedUpon("dwc:occurrenceStatus") String occurrenceStatus ) { DQResponse result = new DQResponse(); @@ -920,9 +1031,44 @@ public DQResponse amendmentOccurrencestatusStandardized( // {dwc:occurrenceStatus vocabulary [https://rs.gbif.org/vocabulary/gbif/occurrence_status_2020-07-15.xml]} // - //TODO: Parameters. This test is defined as parameterized. - // dwc:occurrenceStatus vocabulary - + if (MetadataUtils.isEmpty(occurrenceStatus)) { + result.setResultState(ResultState.INTERNAL_PREREQUISITES_NOT_MET); + result.addComment("Provided dwc:occurrenceStatus is empty"); + } else if (occurrenceStatus.equals("present")) { + result.setResultState(ResultState.NOT_AMENDED); + result.addComment("Provided dwc:occurrenceStatus is a valid value"); + } else if (occurrenceStatus.equals("absent")) { + result.setResultState(ResultState.NOT_AMENDED); + result.addComment("Provided dwc:occurrenceStatus is a valid value"); + } else if (!occurrenceStatus.equals("present") && occurrenceStatus.trim().toLowerCase().equals("present")) { + result.setResultState(ResultState.AMENDED); + Map values = new HashMap<>(); + values.put("dwc:occurrenceStatus", "present") ; + result.setValue(new AmendmentValue(values)); + result.addComment("Provided dwc:occurrenceStatus interpreted as present"); + } else if (!occurrenceStatus.equals("absent") && occurrenceStatus.trim().toLowerCase().equals("absent")) { + result.setResultState(ResultState.AMENDED); + Map values = new HashMap<>(); + values.put("dwc:occurrenceStatus", "absent") ; + result.setValue(new AmendmentValue(values)); + result.addComment("Provided dwc:occurrenceStatus interpreted as absent"); + } else if (occurrenceStatus.trim().equals("1")) { + result.setResultState(ResultState.AMENDED); + Map values = new HashMap<>(); + values.put("dwc:occurrenceStatus", "present") ; + result.setValue(new AmendmentValue(values)); + result.addComment("Provided dwc:occurrenceStatus interpreted as present"); + } else if (occurrenceStatus.trim().equals("0")) { + result.setResultState(ResultState.AMENDED); + Map values = new HashMap<>(); + values.put("dwc:occurrenceStatus", "absent") ; + result.setValue(new AmendmentValue(values)); + result.addComment("Provided dwc:occurrenceStatus interpreted as absent"); + } else { + result.setResultState(ResultState.NOT_AMENDED); + result.addComment("Provided dwc:occurrenceStatus ["+occurrenceStatus+"] not interpretable"); + } + return result; } @@ -1208,6 +1354,7 @@ public DQResponse amendmentDegreeofestablishmentStandardized( * Version: 2024-02-09 * * @param pathway the provided dwc:pathway to evaluate as ActedUpon. + * @param sourceAuthority the bdq:sourceAuthority to consult. * @return DQResponse the response of type ComplianceValue to return */ @Validation(label="VALIDATION_PATHWAY_STANDARD", description="Does the value of dwc:pathway occur in bdq:sourceAuthority?") @@ -1401,22 +1548,24 @@ public DQResponse amendmentSexStandardized( /** * Does the value of dwc:typeStatus occur in bdq:sourceAuthority? * - * Provides: VALIDATION_TYPESTATUS_STANDARD + * Provides: 285 VALIDATION_TYPESTATUS_STANDARD * Version: 2024-02-09 * * @param typeStatus the provided dwc:typeStatus to evaluate as ActedUpon. + * @param sourceAuthority the bdq:sourceAuthority to consult. * @return DQResponse the response of type ComplianceValue to return */ @Validation(label="VALIDATION_TYPESTATUS_STANDARD", description="Does the value of dwc:typeStatus occur in bdq:sourceAuthority?") @Provides("4833a522-12eb-4fe0-b4cf-7f7a337a6048") @ProvidesVersion("https://rs.tdwg.org/bdq/terms/4833a522-12eb-4fe0-b4cf-7f7a337a6048/2024-02-09") @Specification("EXTERNAL_PREREQUISITES_NOT_MET if the bdq:sourceAuthority is not available; INTERNAL_PREREQUISITES_NOT_MET if dwc:typeStatus is EMPTY; COMPLIANT if the value of dwc:typeStatus is in the bdq:sourceAuthority; otherwise NOT_COMPLIANT. bdq:sourceAuthority default = 'Darwin Core typeStatus' {[https://dwc.tdwg.org/list/#dwc_typeStatus]} {dwc:typeStatus vocabulary API [(https://gbif.github.io/parsers/apidocs/org/gbif/api/vocabulary/TypeStatus.html]}") - public DQResponse validationTypestatusStandard( - @ActedUpon("dwc:typeStatus") String typeStatus + public static DQResponse validationTypestatusStandard( + @ActedUpon("dwc:typeStatus") String typeStatus, + @Parameter(name="bdq:sourceAuthority") String sourceAuthority ) { DQResponse result = new DQResponse(); - //TODO: Implement specification + // Specification // EXTERNAL_PREREQUISITES_NOT_MET if the bdq:sourceAuthority // is not available; INTERNAL_PREREQUISITES_NOT_MET if dwc:typeStatus // is EMPTY; COMPLIANT if the value of dwc:typeStatus is in @@ -1427,7 +1576,36 @@ public DQResponse validationTypestatusStandard( //TODO: Parameters. This test is defined as parameterized. // bdq:sourceAuthority - + if (MetadataUtils.isEmpty(typeStatus)) { + result.addComment("No Value provided for dwc:typeStatus"); + result.setResultState(ResultState.INTERNAL_PREREQUISITES_NOT_MET); + } else { + if (MetadataUtils.isEmpty(sourceAuthority)) { + sourceAuthority = "GBIF TypeStatus Vocabulary"; + } + try { + MetadataSourceAuthority sourceAuthorityObject = new MetadataSourceAuthority(sourceAuthority); + if (!MetadataSingleton.getInstance().isLoaded()) { + result.addComment("Error accessing sourceAuthority: " + MetadataSingleton.getInstance().getLoadError() ); + result.setResultState(ResultState.EXTERNAL_PREREQUISITES_NOT_MET); + } else { + result.setResultState(ResultState.RUN_HAS_RESULT); + if (MetadataSingleton.getInstance().getTypeStatusValues().containsKey(typeStatus)) { + result.addComment("Provided value of dwc:typeStatus found in the sourceAuthority"); + result.setValue(ComplianceValue.COMPLIANT); + } else { + result.addComment("Provided value of dwc:typeStatus [" + typeStatus + "] not found in the sourceAuthority"); + result.setValue(ComplianceValue.NOT_COMPLIANT); + } + } + } catch (SourceAuthorityException e) { + result.addComment("Error with specified bdq:sourceAuthority ["+ sourceAuthority +"]: " + e.getMessage()); + result.setResultState(ResultState.EXTERNAL_PREREQUISITES_NOT_MET); + } catch (Exception e) { + result.addComment("Error evaluating dwc:typeStatus: " + e.getMessage()); + result.setResultState(ResultState.EXTERNAL_PREREQUISITES_NOT_MET); + } + } return result; } @@ -1462,6 +1640,8 @@ public DQResponse amendmentTypestatusStandardized( //TODO: Parameters. This test is defined as parameterized. // bdq:sourceAuthority + + return result; } @@ -1650,6 +1830,7 @@ public DQResponse amendmentPreparationsStandardized( * Version: 2024-02-09 * * @param lifeStage the provided dwc:lifeStage to evaluate as ActedUpon. + * @param sourceAuthority the bdq:sourceAuthority to consult. * @return DQResponse the response of type ComplianceValue to return */ @Validation(label="VALIDATION_LIFESTAGE_STANDARD", description="Does the value of dwc:lifeStage occur in bdq:sourceAuthority?") diff --git a/src/main/java/org/filteredpush/qc/metadata/EnumMetadataSourceAuthority.java b/src/main/java/org/filteredpush/qc/metadata/EnumMetadataSourceAuthority.java index a65d36f..acfec00 100644 --- a/src/main/java/org/filteredpush/qc/metadata/EnumMetadataSourceAuthority.java +++ b/src/main/java/org/filteredpush/qc/metadata/EnumMetadataSourceAuthority.java @@ -24,15 +24,31 @@ */ public enum EnumMetadataSourceAuthority { + /** + * GBIF LifeStage vocabulary + */ GBIF_LIFESTAGE, + /** + * GBIF Pathway vocabulary + */ GBIF_PATHWAY, + /** + * GBIF Type Status vocabulary + */ + GBIF_TYPESTATUS, + /** + * Darwin Core Class Names + */ DWC_BASISOFRECORD, + /** + * Invalid Source Authority + */ INVALID; /** - *

getName.

+ * getName get the name of the enumerated object. * - * @return a {@link java.lang.String} object. + * @return the name of the object. */ public String getName() { // return the exact name of the enum instance. diff --git a/src/main/java/org/filteredpush/qc/metadata/MetadataSourceAuthority.java b/src/main/java/org/filteredpush/qc/metadata/MetadataSourceAuthority.java index 8b6891d..9389cba 100644 --- a/src/main/java/org/filteredpush/qc/metadata/MetadataSourceAuthority.java +++ b/src/main/java/org/filteredpush/qc/metadata/MetadataSourceAuthority.java @@ -67,12 +67,22 @@ public MetadataSourceAuthority(String authorityString) throws SourceAuthorityExc if (authorityString==null) { authorityString = ""; } if (authorityString.toUpperCase().equals("DARWIN CORE BASISOFRECORD")) { this.authority = EnumMetadataSourceAuthority.DWC_BASISOFRECORD; + } else if (authorityString.toUpperCase().equals("GBIF LIFESTAGE VOCABULARY")) { this.authority = EnumMetadataSourceAuthority.GBIF_LIFESTAGE; } else if (authorityString.equals("https://api.gbif.org/v1/vocabularies/LifeStage")) { - this.authority = EnumMetadataSourceAuthority.GBIF_LIFESTAGE; + this.authority = EnumMetadataSourceAuthority.GBIF_LIFESTAGE; + } else if (authorityString.toUpperCase().equals("GBIF PATHWAY VOCABULARY")) { this.authority = EnumMetadataSourceAuthority.GBIF_PATHWAY; + } else if (authorityString.equals("https://api.gbif.org/v1/vocabularies/Pathway")) { + this.authority = EnumMetadataSourceAuthority.GBIF_PATHWAY; + + } else if (authorityString.toUpperCase().equals("GBIF TYPESTATUS VOCABULARY")) { + this.authority = EnumMetadataSourceAuthority.GBIF_TYPESTATUS; + } else if (authorityString.equals("https://api.gbif.org/v1/vocabularies/TypeStatus")) { + this.authority = EnumMetadataSourceAuthority.GBIF_TYPESTATUS; + } else if (authorityString.toUpperCase().startsWith("HTTPS://INVALID/")) { this.authority = EnumMetadataSourceAuthority.INVALID; } else { diff --git a/src/main/java/org/filteredpush/qc/metadata/services/GbifService.java b/src/main/java/org/filteredpush/qc/metadata/services/GbifService.java index 5496a01..635fd30 100644 --- a/src/main/java/org/filteredpush/qc/metadata/services/GbifService.java +++ b/src/main/java/org/filteredpush/qc/metadata/services/GbifService.java @@ -47,10 +47,15 @@ public class GbifService { private static final String gbifApiEndpoint = "https://api.gbif.org/v1/"; + /** + * default constructor + */ public GbifService() { init(); } - + /** + * set up class instance + */ private void init() { //ClientBuilder clientBuilder = new ClientBuilder().withUrl(gbifApiEndpoint); @@ -58,6 +63,13 @@ private void init() { } + /** + * Load a GBIF vocabulary + * + * @param vocabulary the name of the vocabulary to load + * @return a Map String, List String where the keys are vocabulary terms and the values are lists of + * possible alternative names for the term. + */ public Map> loadVocabulary(String vocabulary) { HashMap> result = new HashMap(); @@ -136,6 +148,10 @@ public Map> loadVocabulary(String vocabulary) { return result; } + /** + * Main class for testing during development + * @param args arguments, none expected + */ public static void main( String[] args ) { GbifService test = new GbifService(); Map> pathway = test.loadVocabulary("Pathway"); diff --git a/src/main/java/org/filteredpush/qc/metadata/util/LSID.java b/src/main/java/org/filteredpush/qc/metadata/util/LSID.java new file mode 100644 index 0000000..e4f7420 --- /dev/null +++ b/src/main/java/org/filteredpush/qc/metadata/util/LSID.java @@ -0,0 +1,144 @@ +/** + * LSID.java + * + * Copyright 2022 President and Fellows of Harvard College + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.filteredpush.qc.metadata.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Utility class for testing strings that may be LSIDs. + * + * @author mole + * @version $Id: $Id + */ +public class LSID extends RFC8141URN { + + private static final Log logger = LogFactory.getLog(LSID.class); + + private String authority; + private String namespace; + private String objectID; + private String version; + + /** + *

Constructor for LSID.

+ * + * @param urn a {@link java.lang.String} object. + * @throws org.filteredpush.qc.metadata.util.URNFormatException if any. + */ + public LSID(String urn) throws URNFormatException { + super(urn); + // LSID specification https://www.omg.org/cgi-bin/doc?dtc/04-05-01.pdf + // has authority:namespace:objectidenfification with optional :revisionidentification + // where authority is usually an internet domain name or is a unique string and, + // namespace, objectidentification, and revisionidentification are + // specified as "alphanumeric sequence", but the examples therin include non-alphanumeric + // characters -., so using PCHAR without : for each._ + String chars = PCHAR.replace(":", ""); + logger.debug(chars); + logger.debug(nss); + String[] bits = nss.split(":"); + if (bits.length<3 || bits.length > 4) { + throw new URNFormatException("Not a validly formatted LSID"); + } + if (bits[0].length()<1 || bits[1].length()<1 || bits[2].length()<1) { + throw new URNFormatException("Not a validly formatted LSID"); + } + authority = bits[0]; + namespace = bits[1]; + objectID = bits[2]; + if (bits.length==4) { + version = bits[3]; + } else { + version = null; + } + } + + /** + *

Getter for the field authority.

+ * + * @return the authority + */ + public String getAuthority() { + return authority; + } + + /** + *

Setter for the field authority.

+ * + * @param authority the authority to set + */ + public void setAuthority(String authority) { + this.authority = authority; + } + + /** + *

Getter for the field namespace.

+ * + * @return the namespace + */ + public String getNamespace() { + return namespace; + } + + /** + *

Setter for the field namespace.

+ * + * @param namespace the namespace to set + */ + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + /** + *

Getter for the field objectID.

+ * + * @return the objectID + */ + public String getObjectID() { + return objectID; + } + + /** + *

Setter for the field objectID.

+ * + * @param objectID the objectID to set + */ + public void setObjectID(String objectID) { + this.objectID = objectID; + } + + /** + *

Getter for the field version.

+ * + * @return the version + */ + public String getVersion() { + return version; + } + + /** + *

Setter for the field version.

+ * + * @param version the version to set + */ + public void setVersion(String version) { + this.version = version; + } + +} diff --git a/src/main/java/org/filteredpush/qc/metadata/util/MetadataSingleton.java b/src/main/java/org/filteredpush/qc/metadata/util/MetadataSingleton.java index c2290c1..2b4a4fe 100644 --- a/src/main/java/org/filteredpush/qc/metadata/util/MetadataSingleton.java +++ b/src/main/java/org/filteredpush/qc/metadata/util/MetadataSingleton.java @@ -49,6 +49,9 @@ public class MetadataSingleton { private Map> pathwayTerms = new HashMap>(); private Map pathwayValues = new HashMap(); + private Map> typeStatusTerms = new HashMap>(); + private Map typeStatusValues = new HashMap(); + private MetadataSingleton() { init(); } @@ -90,6 +93,17 @@ private void init() { } } + typeStatusTerms = gbif.loadVocabulary("TypeStatus"); + keys = typeStatusTerms.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + List values = typeStatusTerms.get(key); + Iterator i = values.iterator(); + while (i.hasNext()) { + typeStatusValues.put(i.next(), key); + } + } + loaded = true; loadError = ""; } catch (Exception e) { @@ -97,17 +111,41 @@ private void init() { } } + /** + * get the lifeStage key:value pairs + * + * @return the map of lifeStage values from the vocabulary + */ public Map getLifeStageValues() { return lifeStageValues; } + /** + * get the pathway key:value pairs + * + * @return the map of pathway values from the vocabulary + */ public Map getPathwayValues() { return pathwayValues; } + /** + * get the typeStatus key:value pairs + * + * @return the map of typeStatus values from the vocabulary + */ + public Map getTypeStatusValues() { + return typeStatusValues; + } + /** + * @return true if vocabularies have been loaded + */ public Boolean isLoaded() { return loaded; } + /** + * @return any load error message + */ public String getLoadError() { return loadError; } diff --git a/src/main/java/org/filteredpush/qc/metadata/util/RFC8141URN.java b/src/main/java/org/filteredpush/qc/metadata/util/RFC8141URN.java new file mode 100644 index 0000000..daeb345 --- /dev/null +++ b/src/main/java/org/filteredpush/qc/metadata/util/RFC8141URN.java @@ -0,0 +1,210 @@ +/** + * RFC8141URN.java + * + * Copyright 2022 President and Fellows of Harvard College + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.filteredpush.qc.metadata.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Utility class for working with URNs + * + * @author mole + * + * Test for RFC8141 compliant URNs https://tools.ietf.org/html/rfc8141 + * @version $Id: $Id + */ +public class RFC8141URN { + + + protected String nid; // namespace id + protected String nss; // namepace specific string + + /** + *

Constructor for RFC8141URN.

+ * + * @param urn a {@link java.lang.String} object. + * @throws org.filteredpush.qc.metadata.util.URNFormatException if any. + */ + public RFC8141URN(String urn) throws URNFormatException { + if (RFC8141URN.isRFC8141URN(urn)) { + Pattern urn_pattern = Pattern.compile(URN_RFC8141); + Matcher matcher = urn_pattern.matcher(urn); + if (matcher.matches()) { + setNid(matcher.group(2)); + setNss(matcher.group(3)); + } + } else { + throw new URNFormatException("Not a valid URN"); + } + } + + + /** + *

Getter for the field nid.

+ * + * @return the nid Namespace identifier + */ + public String getNid() { + return nid; + } + + /** + *

Setter for the field nid.

+ * + * @param nid the namespace identifer to set + */ + public void setNid(String nid) { + this.nid = nid; + } + + + /** + *

Getter for the field nss.

+ * + * @return the nss Namespace specific string + */ + public String getNss() { + return nss; + } + + + /** + *

Setter for the field nss.

+ * + * @param nss the namespace specific string to set + */ + public void setNss(String nss) { + this.nss = nss; + } + + + private static final Log logger = LogFactory.getLog(RFC8141URN.class); + + /* + namestring = assigned-name + [ rq-components ] + [ "#" f-component ] + assigned-name = "urn" ":" NID ":" NSS + NID = (alphanum) 0*30(ldh) (alphanum) + ldh = alphanum / "-" + NSS = pchar *(pchar / "/") + rq-components = [ "?+" r-component ] + [ "?=" q-component ] + r-component = pchar *( pchar / "/" / "?" ) + q-component = pchar *( pchar / "/" / "?" ) + f-component = fragment + */ + + /** + * pchar from rfc3986 + */ + public static String PCHAR = "[A-Za-z0-9\\-\\._~!\\$&'\\(\\)\\*\\+,;=:@]|(%[0-9A-Fa-f]{2})"; + + /** + * fragment from rfc3986 + */ + public static String FRAGMENT = "((" + PCHAR + ")|/|\\?)*"; + + /** + * Namespace Specific String (NSS) + * + * pchar *(pchar / "/") + */ + private static String NSS = "(" + PCHAR + ")((" + PCHAR + ")|/)*"; + + /** + * Namespace Identifier (NID) + * + * (alphanum) 0*30(ldh) (alphanum) + */ + private static String NID = "[A-Za-z0-9][A-Za-z0-9\\-]{0,30}[A-Za-z0-9]"; + + /** + * assigned-name + * + * "urn" ":" NID ":" NSS + * (where urn is case insenstive) + */ + private static String ASSIGNED_NAME = "([uU][rR][nN]):(" + NID + "):(" + NSS + ")"; + + /** + * r-component and q-component, share same definition + * + * pchar *( pchar / "/" / "?" ) + */ + private static String RQ_COMPONENT = "(" + PCHAR + ")" + FRAGMENT; + + /** + * rq-components + * + * [ "?+" r-component ] [ "?=" q-component ] + * + */ + private static String RQ_COMPONENTS = + "((\\?\\+)(" + RQ_COMPONENT + "))?((\\?=)(" + RQ_COMPONENT + "))?"; + + /** + * Regular expression to match RFC 8141 URNs. + */ + public static String URN_RFC8141 = "^" + ASSIGNED_NAME + RQ_COMPONENTS + "(#" + FRAGMENT + ")?$"; + + /** + *

isRFC8141URN.

+ * + * @param stringToTest a {@link java.lang.String} object. + * @return a boolean. + */ + public static boolean isRFC8141URN(String stringToTest) { + boolean result = false; + if (stringToTest!=null) { + Pattern urn_pattern = Pattern.compile(URN_RFC8141); + Matcher matcher = urn_pattern.matcher(stringToTest); + result = matcher.matches(); + String nid = null; + String nss = null; + if (result) { + for (int i=1; i<=matcher.groupCount(); i++) { + logger.debug(matcher.group(i)); + nid = matcher.group(2); + nss = matcher.group(3); + } + } + if (nid==null) { + result = false; + } + if (nss==null) { + result = false; + } + // additional section 51. and 5.2 rules for formal and informal namespaces + if (nid!=null && nid.startsWith("X-")) { + result = false; // formerly permitted experimental namespace RFC3406 + } + if (nid!=null && nid.startsWith("urn-")) { + if (!nid.matches("^urn-[1-9][0-9]*$")) { + result = false; // not a correctly formatted informal URN namespace. + } + } + } + return result; + } + +} + diff --git a/src/main/java/org/filteredpush/qc/metadata/util/URNFormatException.java b/src/main/java/org/filteredpush/qc/metadata/util/URNFormatException.java new file mode 100644 index 0000000..99ce635 --- /dev/null +++ b/src/main/java/org/filteredpush/qc/metadata/util/URNFormatException.java @@ -0,0 +1,43 @@ +/** + * URNFormatException.java + * + * Copyright 2022 President and Fellows of Harvard College + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.filteredpush.qc.metadata.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + *

URNFormatException class.

+ * + * @author mole + * @version $Id: $Id + */ +public class URNFormatException extends Exception { + + private static final long serialVersionUID = -9093102475674980588L; + private static final Log logger = LogFactory.getLog(URNFormatException.class); + + /** + *

Constructor for URNFormatException.

+ * + * @param message a {@link java.lang.String} object. + */ + public URNFormatException(String message) { + super(message); + } + +} diff --git a/src/test/java/org/filteredpush/qc/metadata/DwCMetadataDQTest.java b/src/test/java/org/filteredpush/qc/metadata/DwCMetadataDQTest.java index bb8f20d..b8f1e78 100644 --- a/src/test/java/org/filteredpush/qc/metadata/DwCMetadataDQTest.java +++ b/src/test/java/org/filteredpush/qc/metadata/DwCMetadataDQTest.java @@ -87,19 +87,13 @@ public void issueDatageneralizationsNotempty() { assertEquals(IssueValue.POTENTIAL_ISSUE.getLabel(), response.getValue().getLabel()); } - /** - * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#validationOccurrenceidStandard(java.lang.String)}. - */ - @Test - public void testValidationOccurrenceidStandard() { - fail("Not yet implemented"); - } + /** * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#validationLicenseStandard(java.lang.String)}. */ @Test - public void validationLicenseStandard() { + public void testValidationLicenseStandard() { String license = "foo"; DQResponse result = DwCMetadataDQDefaults.validationLicenseStandard(license); logger.debug(result.getComment()); @@ -397,13 +391,7 @@ public void testAmendmentBasisofrecordStandardized() { assertNotNull(result.getComment()); } - /** - * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#amendmentOccurrencestatusAssumeddefault(java.lang.String)}. - */ - @Test - public void testAmendmentOccurrencestatusAssumeddefault() { - fail("Not yet implemented"); - } + /** * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#validationDctypeStandard(java.lang.String)}. @@ -569,13 +557,7 @@ public void testValidationBasisofrecordStandard() { assertEquals(ComplianceValue.COMPLIANT.getLabel(), result.getValue().getLabel()); } - /** - * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#amendmentOccurrencestatusStandardized(java.lang.String)}. - */ - @Test - public void testAmendmentOccurrencestatusStandardized() { - fail("Not yet implemented"); - } + /** * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#validationOccurrencestatusStandard(java.lang.String)}. @@ -643,13 +625,7 @@ public void testValidationOccurrencestatusNotempty() { } - /** - * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#amendmentLicenseStandardized(java.lang.String)}. - */ - @Test - public void testAmendmentLicenseStandardized() { - fail("Not yet implemented"); - } + /** * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#validationPathwayNotempty(java.lang.String)}. @@ -936,7 +912,7 @@ public void testValidationLifestageStandard() { * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#validationPathwayStandard(java.lang.String)}. */ @Test - public void validationPathwayStandard() { + public void testValidationPathwayStandard() { String pathway = "foo"; DQResponse result = DwCMetadataDQ.validationPathwayStandard(pathway,"GBIF Pathway Vocabulary"); logger.debug(result.getComment()); @@ -987,4 +963,153 @@ public void validationPathwayStandard() { } + + + /** + * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#validationOccurrenceidStandard(java.lang.String)}. + */ + @Test + public void testValidationOccurrenceidStandard() { + // Specification + // INTERNAL_PREREQUISITES_NOT_MET if dwc:ocurrenceID is EMPTY; COMPLIANT if (1) + // dwc:occurrenceID is a validly formed LSID, or (2) dwc:occurrenceID is a + // validly formed URN with at least NID and NSS present, or (3) dwc:occurrenceID + // is in the form scope:value, or (4) dwc:occurrenceID is a validly formed URI + // with host and path where path consists of more than just "/"; otherwise + // NOT_COMPLIANT. + + String occurrenceID = "foo"; + DQResponse result = DwCMetadataDQDefaults.validationOccurrenceidStandard(occurrenceID); + logger.debug(result.getComment()); + assertEquals(ResultState.RUN_HAS_RESULT.getLabel(), result.getResultState().getLabel()); + assertEquals(ComplianceValue.NOT_COMPLIANT.getLabel(), result.getValue().getLabel()); + assertNotNull(result.getComment()); + + occurrenceID = ""; + result = DwCMetadataDQDefaults.validationOccurrenceidStandard(occurrenceID); + logger.debug(result.getComment()); + assertEquals(ResultState.INTERNAL_PREREQUISITES_NOT_MET.getLabel(), result.getResultState().getLabel()); + assertNull(result.getValue()); + + occurrenceID = "https://creativecommons.org/licenses/by-sa/4.0/"; + result = DwCMetadataDQDefaults.validationOccurrenceidStandard(occurrenceID); + logger.debug(result.getComment()); + assertEquals(ResultState.RUN_HAS_RESULT.getLabel(), result.getResultState().getLabel()); + assertEquals(ComplianceValue.COMPLIANT.getLabel(), result.getValue().getLabel()); + assertNotNull(result.getComment()); + + occurrenceID = "urn:catalog:MCZ:Orn:1"; + result = DwCMetadataDQDefaults.validationOccurrenceidStandard(occurrenceID); + logger.debug(result.getComment()); + assertEquals(ResultState.RUN_HAS_RESULT.getLabel(), result.getResultState().getLabel()); + assertEquals(ComplianceValue.COMPLIANT.getLabel(), result.getValue().getLabel()); + assertNotNull(result.getComment()); + + occurrenceID = "scope:121415"; + result = DwCMetadataDQDefaults.validationOccurrenceidStandard(occurrenceID); + logger.debug(result.getComment()); + assertEquals(ResultState.RUN_HAS_RESULT.getLabel(), result.getResultState().getLabel()); + assertEquals(ComplianceValue.COMPLIANT.getLabel(), result.getValue().getLabel()); + assertNotNull(result.getComment()); + + occurrenceID = "urn:lsid:marinespecies.org:taxname:137205"; + result = DwCMetadataDQDefaults.validationOccurrenceidStandard(occurrenceID); + logger.debug(result.getComment()); + assertEquals(ResultState.RUN_HAS_RESULT.getLabel(), result.getResultState().getLabel()); + assertEquals(ComplianceValue.COMPLIANT.getLabel(), result.getValue().getLabel()); + assertNotNull(result.getComment()); + + occurrenceID = "urn:uuid:32803335-d5eb-4e5c-ab2e-8a451ae8e23a"; + result = DwCMetadataDQDefaults.validationOccurrenceidStandard(occurrenceID); + logger.debug(result.getComment()); + assertEquals(ResultState.RUN_HAS_RESULT.getLabel(), result.getResultState().getLabel()); + assertEquals(ComplianceValue.COMPLIANT.getLabel(), result.getValue().getLabel()); + assertNotNull(result.getComment()); + + } + + /** + * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#amendmentOccurrencestatusAssumeddefault(java.lang.String)}. + */ + @Test + public void testAmendmentOccurrencestatusAssumeddefault() { + // FILLED_IN the value of dwc:occurrenceStatus using the Parameter + // value if dwc:occurrence.Status, dwc:individualCount and + // dwc:organismQuantity are EMPTY; otherwise NOT_AMENDED dwc:occurrenceStatus + // default = "present" + String occurrenceStatus = ""; + String individualCount = "1"; + String organismQuantity = ""; + DQResponse result = DwCMetadataDQ.amendmentOccurrencestatusAssumeddefault(occurrenceStatus, individualCount, organismQuantity, null); + logger.debug(result.getComment()); + assertEquals(ResultState.NOT_AMENDED.getLabel(), result.getResultState().getLabel()); + assertNull(result.getValue()); + assertNotNull(result.getComment()); + + occurrenceStatus = ""; + individualCount = ""; + organismQuantity = ""; + result = DwCMetadataDQ.amendmentOccurrencestatusAssumeddefault(occurrenceStatus, individualCount, organismQuantity, null); + logger.debug(result.getComment()); + assertEquals(ResultState.FILLED_IN.getLabel(), result.getResultState().getLabel()); + assertNotNull(result.getValue()); + assertEquals("present", result.getValue().getObject().get("dwc:occurrenceStatus")); + assertNotNull(result.getComment()); + + } + + /** + * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#amendmentOccurrencestatusStandardized(java.lang.String)}. + */ + @Test + public void testAmendmentOccurrencestatusStandardized() { + String occurrenceStatus = ""; + DQResponse result = DwCMetadataDQ.amendmentOccurrencestatusStandardized(occurrenceStatus); + logger.debug(result.getComment()); + assertEquals(ResultState.INTERNAL_PREREQUISITES_NOT_MET.getLabel(), result.getResultState().getLabel()); + assertNull(result.getValue()); + assertNotNull(result.getComment()); + + occurrenceStatus = "1"; + result = DwCMetadataDQ.amendmentOccurrencestatusStandardized(occurrenceStatus); + logger.debug(result.getComment()); + assertEquals(ResultState.AMENDED.getLabel(), result.getResultState().getLabel()); + assertNotNull(result.getValue()); + assertEquals("present", result.getValue().getObject().get("dwc:occurrenceStatus")); + assertNotNull(result.getComment()); + + occurrenceStatus = " Present"; + result = DwCMetadataDQ.amendmentOccurrencestatusStandardized(occurrenceStatus); + logger.debug(result.getComment()); + assertEquals(ResultState.AMENDED.getLabel(), result.getResultState().getLabel()); + assertNotNull(result.getValue()); + assertEquals("present", result.getValue().getObject().get("dwc:occurrenceStatus")); + assertNotNull(result.getComment()); + + occurrenceStatus = "0"; + result = DwCMetadataDQ.amendmentOccurrencestatusStandardized(occurrenceStatus); + logger.debug(result.getComment()); + assertEquals(ResultState.AMENDED.getLabel(), result.getResultState().getLabel()); + assertNotNull(result.getValue()); + assertEquals("absent", result.getValue().getObject().get("dwc:occurrenceStatus")); + assertNotNull(result.getComment()); + + occurrenceStatus = "abnsent"; + result = DwCMetadataDQ.amendmentOccurrencestatusStandardized(occurrenceStatus); + logger.debug(result.getComment()); + assertEquals(ResultState.NOT_AMENDED.getLabel(), result.getResultState().getLabel()); + assertNull(result.getValue()); + assertNotNull(result.getComment()); + + } + + /** + * Test method for {@link org.filteredpush.qc.metadata.DwCMetadataDQ#amendmentLicenseStandardized(java.lang.String)}. + */ + @Test + public void testAmendmentLicenseStandardized() { + // TODO: Implement + // fail("Not yet implemented"); + } + }