/*
* 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.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.api.component.sememe.version.dynamicSememe;
//~--- JDK imports ------------------------------------------------------------
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.UUID;
//~--- non-JDK imports --------------------------------------------------------
/**
* Copyright Notice
*
* This is a work of the U.S. Government and is not subject to copyright
* protection in the United States. Foreign copyrights may apply.
*
* 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.
*/
import org.jvnet.hk2.annotations.Contract;
import org.slf4j.LoggerFactory;
import sh.isaac.api.Get;
import sh.isaac.api.chronicle.ObjectChronologyType;
import sh.isaac.api.component.sememe.SememeType;
import sh.isaac.api.component.sememe.version.dynamicSememe.dataTypes.DynamicSememeArray;
import sh.isaac.api.component.sememe.version.dynamicSememe.dataTypes.DynamicSememeString;
import sh.isaac.api.component.sememe.version.dynamicSememe.dataTypes.DynamicSememeUUID;
import sh.isaac.api.coordinate.StampCoordinate;
import sh.isaac.api.coordinate.TaxonomyCoordinate;
//~--- interfaces -------------------------------------------------------------
/**
* {@link DynamicSememeUtility}
*
* This class exists as an interface primarily to allow classes in ochre-api and ochre-impl to have access to these methods
* that need to be implemented further down the dependency tree (with access to metadata, etc)
*
* Code in ochre-util and ochre-api will access the impl via HK2.
*
* @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a>
*/
@Contract
public interface DynamicSememeUtility {
/**
* This will return the column index configuration that will mark each supplied column that is indexable, for indexing.
* Returns null, if no columns need indexing.
*
* @param columns the columns
* @return the dynamic sememe array
*/
public DynamicSememeArray<DynamicSememeData> configureColumnIndexInfo(DynamicSememeColumnInfo[] columns);
/**
* Configure dynamic sememe definition data for column.
*
* @param ci the ci
* @return the dynamic sememe data[]
*/
public DynamicSememeData[] configureDynamicSememeDefinitionDataForColumn(DynamicSememeColumnInfo ci);
/**
* Configure dynamic sememe restriction data.
*
* @param referencedComponentRestriction the referenced component restriction
* @param referencedComponentSubRestriction the referenced component sub restriction
* @return the dynamic sememe data[]
*/
public DynamicSememeData[] configureDynamicSememeRestrictionData(ObjectChronologyType referencedComponentRestriction,
SememeType referencedComponentSubRestriction);
/**
* Creates the dynamic string data.
*
* @param value the value
* @return the dynamic sememe string
*/
public DynamicSememeString createDynamicStringData(String value);
/**
* Creates the dynamic UUID data.
*
* @param value the value
* @return the dynamic sememe UUID
*/
public DynamicSememeUUID createDynamicUUIDData(UUID value);
/**
* Convenience method to read all of the extended details of a DynamicSememeAssemblage.
*
* @param assemblageNidOrSequence the assemblage nid or sequence
* @return the dynamic sememe usage description
*/
public DynamicSememeUsageDescription readDynamicSememeUsageDescription(int assemblageNidOrSequence);
/**
* validate that the proposed dynamicSememeData aligns with the definition. This also fills in default values,
* as necessary, if the data[] contains 'nulls' and the column is specified with a default value.
*
* @param dsud the dsud
* @param data the data
* @param referencedComponentNid the referenced component nid
* @param stampCoordinate - optional - column specific validators may be skipped if this is not provided
* @param taxonomyCoordinate - optional - column specific validators may be skipped if this is not provided
* @throws IllegalArgumentException the illegal argument exception
* @throws InvalidParameterException - if anything fails validation
*/
public default void validate(DynamicSememeUsageDescription dsud,
DynamicSememeData[] data,
int referencedComponentNid,
StampCoordinate stampCoordinate,
TaxonomyCoordinate taxonomyCoordinate)
throws IllegalArgumentException {
// Make sure the referenced component meets the ref component restrictions, if any are present.
if ((dsud.getReferencedComponentTypeRestriction() != null) &&
(dsud.getReferencedComponentTypeRestriction() != ObjectChronologyType.UNKNOWN_NID)) {
final ObjectChronologyType requiredType = dsud.getReferencedComponentTypeRestriction();
final ObjectChronologyType foundType = Get.identifierService()
.getChronologyTypeForNid(referencedComponentNid);
if (requiredType != foundType) {
throw new IllegalArgumentException("The referenced component must be of type " + requiredType +
", but a " + foundType + " was passed");
}
if ((requiredType == ObjectChronologyType.SEMEME) &&
(dsud.getReferencedComponentTypeSubRestriction() != null) &&
(dsud.getReferencedComponentTypeSubRestriction() != SememeType.UNKNOWN)) {
final SememeType requiredSememeType = dsud.getReferencedComponentTypeSubRestriction();
final SememeType foundSememeType = Get.sememeService()
.getSememe(referencedComponentNid)
.getSememeType();
if (requiredSememeType != foundSememeType) {
throw new IllegalArgumentException("The referenced component must be a sememe of type " +
requiredSememeType + ", but a " + foundSememeType + " was passed");
}
}
}
if (data == null) {
data = new DynamicSememeData[] {};
}
// specifically allow < - we don't need the trailing columns, if they were defined as optional.
if (data.length > dsud.getColumnInfo().length) {
throw new IllegalArgumentException(
"The Assemblage concept: " + dsud.getDynamicSememeName() + " specifies " + dsud.getColumnInfo().length +
" columns of data, while the provided data contains " + data.length +
" columns. The data size array must not exeed the sememe definition." +
" (the data column count may be less, if the missing columns are defined as optional)");
}
int lastColumnWithDefaultValue = 0;
for (int i = 0; i < dsud.getColumnInfo().length; i++) {
if (dsud.getColumnInfo()[i]
.getDefaultColumnValue() != null) {
lastColumnWithDefaultValue = i;
}
}
if (lastColumnWithDefaultValue + 1 > data.length) {
// We need to lengthen the data array, to make room to add the default value
data = Arrays.copyOf(data, lastColumnWithDefaultValue);
}
for (int i = 0; i < dsud.getColumnInfo().length; i++) {
final DynamicSememeData defaultValue = dsud.getColumnInfo()[i]
.getDefaultColumnValue();
if ((defaultValue != null) && (data[i] == null)) {
data[i] = defaultValue;
}
}
// If they provided less columns, make sure the remaining columns are all optional
for (int i = data.length; i < dsud.getColumnInfo().length; i++) {
if (dsud.getColumnInfo()[i]
.isColumnRequired()) {
throw new IllegalArgumentException("No data was supplied for column '" +
dsud.getColumnInfo()[i].getColumnName() + "' [" + (i + 1) + "(index " +
i + ")] but the column is specified as a required column");
}
}
for (int dataColumn = 0; dataColumn < data.length; dataColumn++) {
final DynamicSememeColumnInfo dsci = dsud.getColumnInfo()[dataColumn];
if (data[dataColumn] == null) {
if (dsci.isColumnRequired()) {
throw new IllegalArgumentException("No data was supplied for column " + (dataColumn + 1) +
" but the column is specified as a required column");
}
} else {
final DynamicSememeDataType allowedDT = dsci.getColumnDataType();
if ((data[dataColumn] != null) &&
(allowedDT != DynamicSememeDataType.POLYMORPHIC) &&
(data[dataColumn].getDynamicSememeDataType() != allowedDT)) {
throw new IllegalArgumentException("The supplied data for column " + dataColumn + " is of type " +
data[dataColumn].getDynamicSememeDataType() +
" but the assemblage concept declares that it must be " + allowedDT);
}
if ((dsci.getValidator() != null) && (dsci.getValidator().length > 0)) {
try {
for (int i = 0; i < dsci.getValidator().length; i++) {
boolean rethrow = false;
try {
if (!dsci.getValidator()[i]
.passesValidator(data[dataColumn],
dsci.getValidatorData()[i],
stampCoordinate,
taxonomyCoordinate)) {
rethrow = true;
throw new IllegalArgumentException(
"The supplied data for column " + dataColumn +
" does not pass the assigned validator(s) for this dynamic sememe. Data: " +
data[dataColumn].dataToString() + " Validator: " + dsci.getValidator()[i].name() +
" Validator Data: " + dsci.getValidatorData()[i].dataToString());
}
} catch (final IllegalArgumentException e) {
if (rethrow) {
throw e;
} else {
LoggerFactory.getLogger(DynamicSememeUtility.class)
.debug("Couldn't execute validator due to missing coordiantes");
}
}
}
} catch (final IllegalArgumentException e) {
throw e;
} catch (final RuntimeException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
}
}
}
}