/**
* =============================================================================
*
* ORCID (R) Open Source
* http://orcid.org
*
* Copyright (c) 2012-2014 ORCID, Inc.
* Licensed under an MIT-Style License (MIT)
* http://orcid.org/open-source-license
*
* This copyright and license information (including a link to the full license)
* shall be included in its entirety in all copies or substantial portion of
* the software.
*
* =============================================================================
*/
package org.orcid.core.manager.impl;
import java.io.IOException;
import java.util.List;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.commons.lang3.StringUtils;
import org.orcid.core.cli.ValidateOrcidMessage;
import org.orcid.core.exception.OrcidValidationException;
import org.orcid.core.manager.ValidationBehaviour;
import org.orcid.core.manager.ValidationManager;
import org.orcid.jaxb.model.message.ContactDetails;
import org.orcid.jaxb.model.message.Email;
import org.orcid.jaxb.model.message.Funding;
import org.orcid.jaxb.model.message.FundingList;
import org.orcid.jaxb.model.message.OrcidActivities;
import org.orcid.jaxb.model.message.OrcidBio;
import org.orcid.jaxb.model.message.OrcidMessage;
import org.orcid.jaxb.model.message.OrcidProfile;
import org.orcid.jaxb.model.message.OrcidWork;
import org.orcid.jaxb.model.message.OrcidWorks;
import org.orcid.jaxb.model.message.WorkTitle;
import org.orcid.pojo.ajaxForm.PojoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/**
*
* @author Will Simpson
*
*/
public class ValidationManagerImpl implements ValidationManager {
private ValidationBehaviour validationBehaviour = ValidationBehaviour.LOG_WARNING;
private String version = OrcidMessage.DEFAULT_VERSION;
private boolean requireOrcidProfile;
private boolean validateTitle = false;
private boolean validateOnlyOnePrimaryEmail = false;
private boolean validateWorksHaveExternalIds = false;
private boolean validateFundingHaveExternalIds = false;
private Schema schema;
private static final Logger LOG = LoggerFactory.getLogger(ValidationManagerImpl.class);
@Override
public void setValidationBehaviour(ValidationBehaviour validationBehaviour) {
this.validationBehaviour = validationBehaviour;
}
public void setVersion(String version) {
this.version = version;
}
public void setRequireOrcidProfile(boolean requireOrcidProfile) {
this.requireOrcidProfile = requireOrcidProfile;
}
public void setValidateTitle(boolean validateTitle) {
this.validateTitle = validateTitle;
}
public boolean isValidateOnlyOnePrimaryEmail() {
return validateOnlyOnePrimaryEmail;
}
public void setValidateOnlyOnePrimaryEmail(boolean validateOnlyOnePrimaryEmail) {
this.validateOnlyOnePrimaryEmail = validateOnlyOnePrimaryEmail;
}
public boolean isValidateWorksHaveExternalIds() {
return validateWorksHaveExternalIds;
}
public void setValidateWorksHaveExternalIds(boolean validateWorksHaveExternalIds) {
this.validateWorksHaveExternalIds = validateWorksHaveExternalIds;
}
public boolean isValidateFundingHaveExternalIds() {
return validateFundingHaveExternalIds;
}
public void setValidateFundingHaveExternalIds(boolean validateFundingHaveExternalIds) {
this.validateFundingHaveExternalIds = validateFundingHaveExternalIds;
}
@Override
public void validateMessage(OrcidMessage orcidMessage) {
if (ValidationBehaviour.IGNORE.equals(validationBehaviour)) {
return;
}
doMessageVersionValidation(orcidMessage);
doWorkTypeValidation(orcidMessage);
doSchemaValidation(orcidMessage);
doCustomValidation(orcidMessage);
}
@Override
public void validateBioMessage(OrcidMessage orcidMessage) {
if (ValidationBehaviour.IGNORE.equals(validationBehaviour)) {
return;
}
doMessageVersionValidation(orcidMessage);
doSchemaValidation(orcidMessage);
}
private void doMessageVersionValidation(OrcidMessage orcidMessage) {
if (orcidMessage != null) {
if (PojoUtil.isEmpty(orcidMessage.getMessageVersion())) {
handleError("Message version is required");
}
}
}
private void doWorkTypeValidation(OrcidMessage orcidMessage) {
if (orcidMessage == null || orcidMessage.getOrcidProfile() == null || orcidMessage.getOrcidProfile().getOrcidActivities() == null
|| orcidMessage.getOrcidProfile().getOrcidActivities().getOrcidWorks() == null
|| orcidMessage.getOrcidProfile().getOrcidActivities().getOrcidWorks().getOrcidWork() == null
|| orcidMessage.getOrcidProfile().getOrcidActivities().getOrcidWorks().getOrcidWork().isEmpty())
return;
List<OrcidWork> works = orcidMessage.getOrcidProfile().getOrcidActivities().getOrcidWorks().getOrcidWork();
for (OrcidWork work : works) {
if (work.getWorkType() == null)
if (work.getWorkTitle() != null && work.getWorkTitle().getTitle() != null && !PojoUtil.isEmpty(work.getWorkTitle().getTitle().getContent()))
handleError("work-type is missing or invalid for work: '" + work.getWorkTitle().getTitle().getContent() + "'");
else
handleError("work-type is missing or invalid");
}
}
protected void doSchemaValidation(OrcidMessage orcidMessage) {
Validator validator = createValidator();
if (validator != null) {
try {
validator.validate(orcidMessage.toSource());
} catch (SAXException e) {
handleError("ORCID message is invalid", e, orcidMessage);
} catch (IOException e) {
handleError("Unable to read ORCID message", e, orcidMessage);
}
}
}
public void doCustomValidation(OrcidMessage orcidMessage) {
try {
checkMessage(orcidMessage);
} catch (OrcidValidationException e) {
handleError("Custom validation found a problem", e, orcidMessage);
}
}
private void checkMessage(OrcidMessage orcidMessage) {
OrcidProfile orcidProfile = orcidMessage != null ? orcidMessage.getOrcidProfile() : null;
if (orcidProfile == null) {
if (requireOrcidProfile) {
throw new OrcidValidationException("There must be an orcid-profile element");
}
} else {
checkBio(orcidProfile.getOrcidBio());
checkActivities(orcidProfile.getOrcidActivities());
}
}
private void checkBio(OrcidBio orcidBio) {
if (orcidBio != null) {
checkContactDetails(orcidBio.getContactDetails());
}
}
private void checkContactDetails(ContactDetails contactDetails) {
if (contactDetails != null) {
List<Email> emailList = contactDetails.getEmail();
int primaryCount = 0;
for (Email email : emailList) {
if (email.isPrimary()) {
primaryCount++;
}
}
if (primaryCount > 1) {
throw new OrcidValidationException("There must not be more than one primary email");
}
if (validateOnlyOnePrimaryEmail) {
if (primaryCount == 0)
throw new OrcidValidationException("There must be at least one primary email");
}
}
}
private void checkActivities(OrcidActivities orcidActivities) {
if (orcidActivities != null) {
OrcidWorks works = orcidActivities.getOrcidWorks();
if (works != null && works.getOrcidWork() != null && !works.getOrcidWork().isEmpty()) {
checkWorks(works.getOrcidWork());
}
FundingList funding = orcidActivities.getFundings();
if (funding != null && funding.getFundings() != null && !funding.getFundings().isEmpty()) {
checkFunding(funding.getFundings());
}
}
}
private void checkWorks(List<OrcidWork> orcidWork) {
for (OrcidWork work : orcidWork) {
checkWork(work);
}
}
public void checkWork(OrcidWork orcidWork) {
if (validateTitle) {
WorkTitle title = orcidWork.getWorkTitle();
if (title == null || title.getTitle() == null || StringUtils.isEmpty(title.getTitle().getContent())) {
throw new OrcidValidationException("Invalid Title: title cannot be null nor emtpy");
}
}
if (validateWorksHaveExternalIds) {
if (orcidWork.getWorkExternalIdentifiers() == null || orcidWork.getWorkExternalIdentifiers().getWorkExternalIdentifier() == null
|| orcidWork.getWorkExternalIdentifiers().getWorkExternalIdentifier().isEmpty()) {
throw new OrcidValidationException("Invalid work: Works added using message version 1.2_rc5 or greater must contain at least one external identifier");
}
}
}
private void checkFunding(List<Funding> fundings) {
for (Funding funding : fundings) {
checkFunding(funding);
}
}
private void checkFunding(Funding funding) {
if (validateFundingHaveExternalIds) {
if (funding.getFundingExternalIdentifiers() == null || funding.getFundingExternalIdentifiers().getFundingExternalIdentifier() == null
|| funding.getFundingExternalIdentifiers().getFundingExternalIdentifier().isEmpty()) {
throw new OrcidValidationException(
"Invalid funding: Funding added using message version 1.2_rc5 or greater must contain at least one external identifier");
}
}
if (funding.getStartDate() != null) {
if (!PojoUtil.isEmpty(funding.getStartDate().getMonth()) && PojoUtil.isEmpty(funding.getStartDate().getYear())) {
throw new OrcidValidationException("Invalid funding: Invalid start date");
}
}
if (funding.getEndDate() != null) {
if (!PojoUtil.isEmpty(funding.getEndDate().getMonth()) && PojoUtil.isEmpty(funding.getEndDate().getYear())) {
throw new OrcidValidationException("Invalid funding: Invalid end date");
}
}
}
private void initSchema() {
if (schema == null) {
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
try {
schema = factory.newSchema(ValidateOrcidMessage.class.getResource("/orcid-message-" + version + ".xsd"));
} catch (SAXException e) {
handleError("Error initializing schema", e);
}
}
}
private Validator createValidator() {
initSchema();
if (schema == null) {
handleError("Unable to create validator because schema is null");
}
return schema.newValidator();
}
private void handleError(String message) {
handleError(message, null, null);
}
private void handleError(String message, Throwable t) {
handleError(message, t, null);
}
private void handleError(String message, Throwable t, OrcidMessage orcidMessage) {
switch (validationBehaviour) {
case IGNORE:
break;
case LOG_INFO:
LOG.info(message, t);
break;
case LOG_WARNING:
LOG.warn(message, t);
break;
case LOG_ERROR:
LOG.error(message, t);
break;
case LOG_INFO_WITH_XML:
LOG.info(message, t);
if (orcidMessage != null) {
LOG.info("ORCID message is: {}", orcidMessage);
}
break;
case LOG_WARNING_WITH_XML:
LOG.warn(message, t);
if (orcidMessage != null) {
LOG.warn("ORCID message is: {}", orcidMessage);
}
break;
case LOG_ERROR_WITH_XML:
LOG.error(message, t);
if (orcidMessage != null) {
LOG.error("ORCID message is: {}", orcidMessage);
}
break;
case THROW_VALIDATION_EXCEPTION:
if (t instanceof OrcidValidationException) {
throw (OrcidValidationException) t;
} else {
throw new OrcidValidationException(message, t);
}
default:
throw new RuntimeException("Unknown validation behaviour: " + validationBehaviour);
}
}
}