gov.vha.isaac.ochre.impl.sememe.DynamicSememeUsageDescription.java Source code

Java tutorial

Introduction

Here is the source code for gov.vha.isaac.ochre.impl.sememe.DynamicSememeUsageDescription.java

Source

/**
 * 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.
 */
package gov.vha.isaac.ochre.impl.sememe;

import java.io.IOException;
import java.util.Optional;
import java.util.TreeMap;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.ihtsdo.otf.tcc.api.contradiction.ContradictionException;
import gov.vha.isaac.metadata.coordinates.StampCoordinates;
import gov.vha.isaac.metadata.source.IsaacMetadataAuxiliaryBinding;
import gov.vha.isaac.ochre.api.Get;
import gov.vha.isaac.ochre.api.chronicle.LatestVersion;
import gov.vha.isaac.ochre.api.chronicle.ObjectChronologyType;
import gov.vha.isaac.ochre.api.component.concept.ConceptChronology;
import gov.vha.isaac.ochre.api.component.sememe.SememeChronology;
import gov.vha.isaac.ochre.api.component.sememe.SememeType;
import gov.vha.isaac.ochre.api.component.sememe.version.DescriptionSememe;
import gov.vha.isaac.ochre.api.component.sememe.version.DynamicSememe;
import gov.vha.isaac.ochre.api.component.sememe.version.SememeVersion;
import gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeColumnInfo;
import gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeDataBI;
import gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeDataType;
import gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeUsageDescriptionBI;
import gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeValidatorType;
import gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.dataTypes.DynamicSememeArrayBI;
import gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.dataTypes.DynamicSememeStringBI;
import gov.vha.isaac.ochre.model.constants.IsaacMetadataConstants;

/**
 *
 * See {@link DynamicSememeUsageDescriptionBI} 
 *
 * @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a>
 */

public class DynamicSememeUsageDescription implements DynamicSememeUsageDescriptionBI {
    int refexUsageDescriptorSequence_;
    String sememeUsageDescription_;
    String name_;
    DynamicSememeColumnInfo[] refexColumnInfo_;
    ObjectChronologyType referencedComponentTypeRestriction_;
    SememeType referencedComponentTypeSubRestriction_;

    protected static final Logger logger = Logger.getLogger(DynamicSememeUsageDescription.class.getName());

    private static LRUDynamicSememeDescriptorCache<Integer, DynamicSememeUsageDescription> cache_ = new LRUDynamicSememeDescriptorCache<Integer, DynamicSememeUsageDescription>(
            25);

    /**
     * 
     * Test if dyn sememe
     * 
     * @param assemblageNidOrSequence
     * @return
     */
    public static boolean isDynamicSememe(int assemblageNidOrSequence) {
        if (assemblageNidOrSequence >= 0 || Get.identifierService()
                .getChronologyTypeForNid(assemblageNidOrSequence) == ObjectChronologyType.CONCEPT) {
            try {
                read(assemblageNidOrSequence);
                return true;
            } catch (Exception e) {
                return false;
            }
        } else {
            return false;
        }
    }

    public static DynamicSememeUsageDescription read(int assemblageNidOrSequence) {
        //TODO (artf231860) [REFEX] maybe? implement a mechanism to allow the cache to be updated... for now
        //cache is uneditable, and may be wrong, if the user changes the definition of a dynamic sememe.  Perhaps
        //implement a callback to clear the cache when we know a change of  a certain type happened instead?

        int sequence = Get.identifierService().getConceptSequence(assemblageNidOrSequence);

        DynamicSememeUsageDescription temp = cache_.get(sequence);
        if (temp == null) {
            logger.log(Level.FINEST, "Cache miss on DynamicSememeUsageDescription Cache");
            temp = new DynamicSememeUsageDescription(sequence);
            cache_.put(sequence, temp);
        }
        return temp;
    }

    /**
     * Invent DynamicSememeUsageDescription info for other sememe types (that aren't dynamic), otherwise, calls
     * {@link #read(int)} if it is a dynamic sememe
     * @param sememe the sememe in question
     */
    public static DynamicSememeUsageDescription mockOrRead(SememeChronology<?> sememe) {
        DynamicSememeUsageDescription dsud = new DynamicSememeUsageDescription();
        dsud.name_ = Get.conceptDescriptionText(sememe.getAssemblageSequence());
        dsud.referencedComponentTypeRestriction_ = null;
        dsud.referencedComponentTypeSubRestriction_ = null;
        dsud.refexUsageDescriptorSequence_ = sememe.getAssemblageSequence();
        dsud.sememeUsageDescription_ = "-";

        switch (sememe.getSememeType()) {
        case COMPONENT_NID:
            dsud.refexColumnInfo_ = new DynamicSememeColumnInfo[] { new DynamicSememeColumnInfo(
                    Get.identifierService().getUuidPrimordialFromConceptSequence(sememe.getAssemblageSequence())
                            .get(),
                    0, IsaacMetadataAuxiliaryBinding.NID.getPrimodialUuid(), DynamicSememeDataType.NID, null, true,
                    null, null) };
            break;
        case LONG:
            dsud.refexColumnInfo_ = new DynamicSememeColumnInfo[] { new DynamicSememeColumnInfo(
                    Get.identifierService().getUuidPrimordialFromConceptSequence(sememe.getAssemblageSequence())
                            .get(),
                    0, IsaacMetadataAuxiliaryBinding.LONG.getPrimodialUuid(), DynamicSememeDataType.LONG, null,
                    true, null, null) };
            break;
        case DESCRIPTION:
        case STRING:
        case LOGIC_GRAPH:
            dsud.refexColumnInfo_ = new DynamicSememeColumnInfo[] { new DynamicSememeColumnInfo(
                    Get.identifierService().getUuidPrimordialFromConceptSequence(sememe.getAssemblageSequence())
                            .get(),
                    0, IsaacMetadataAuxiliaryBinding.STRING.getPrimodialUuid(), DynamicSememeDataType.STRING, null,
                    true, null, null) };
            break;
        case MEMBER:
            dsud.refexColumnInfo_ = new DynamicSememeColumnInfo[] {};
            break;
        case DYNAMIC:
            return read(sememe.getAssemblageSequence());
        case UNKNOWN:
        default:
            throw new RuntimeException("Use case not yet supported");
        }
        return dsud;
    }

    private DynamicSememeUsageDescription() {
        //For use by the mock static method
    }

    /**
     * Read the RefexUsageDescription data from the database for a given nid.
     * 
     * Note that most users should call {@link #read(int)} instead, as that utilizes a cache.
     * This always reads directly from the DB.
     * 
     * @param refexUsageDescriptorSequence
     * @throws IOException 
     * @throws ContradictionException 
     */
    @SuppressWarnings("unchecked")
    public DynamicSememeUsageDescription(int refexUsageDescriptorSequence) {
        refexUsageDescriptorSequence_ = refexUsageDescriptorSequence;
        TreeMap<Integer, DynamicSememeColumnInfo> allowedColumnInfo = new TreeMap<>();
        ConceptChronology<?> assemblageConcept = Get.conceptService().getConcept(refexUsageDescriptorSequence_);

        for (SememeChronology<? extends DescriptionSememe<?>> descriptionSememe : assemblageConcept
                .getConceptDescriptionList()) {
            @SuppressWarnings("rawtypes")
            Optional<LatestVersion<DescriptionSememe<?>>> descriptionVersion = ((SememeChronology) descriptionSememe)
                    .getLatestVersion(DescriptionSememe.class, StampCoordinates.getDevelopmentLatestActiveOnly());

            if (descriptionVersion.isPresent()) {
                @SuppressWarnings("rawtypes")
                DescriptionSememe ds = descriptionVersion.get().value();

                if (ds.getDescriptionTypeConceptSequence() == IsaacMetadataAuxiliaryBinding.DEFINITION_DESCRIPTION_TYPE
                        .getConceptSequence()) {
                    Optional<SememeChronology<? extends SememeVersion<?>>> nestesdSememe = Get.sememeService()
                            .getSememesForComponentFromAssemblage(ds.getNid(),
                                    IsaacMetadataConstants.DYNAMIC_SEMEME_DEFINITION_DESCRIPTION.getSequence())
                            .findAny();
                    if (nestesdSememe.isPresent()) {
                        sememeUsageDescription_ = ds.getText();
                    }
                    ;
                }
                if (ds.getDescriptionTypeConceptSequence() == IsaacMetadataAuxiliaryBinding.FULLY_SPECIFIED_NAME
                        .getConceptSequence()) {
                    name_ = ds.getText();
                }
                if (sememeUsageDescription_ != null && name_ != null) {
                    break;
                }

            }
        }

        if (StringUtils.isEmpty(sememeUsageDescription_)) {
            throw new RuntimeException("The Assemblage concept: " + assemblageConcept
                    + " is not correctly assembled for use as an Assemblage for "
                    + "a DynamicSememeData Refex Type.  It must contain a description of type Definition with an annotation of type "
                    + "DynamicSememe.DYNAMIC_SEMEME_DEFINITION_DESCRIPTION");
        }

        Get.sememeService().getSememesForComponent(assemblageConcept.getNid()).forEach(sememe -> {
            if (sememe.getSememeType() == SememeType.DYNAMIC) {
                @SuppressWarnings("rawtypes")
                Optional<LatestVersion<? extends DynamicSememe>> sememeVersion = ((SememeChronology) sememe)
                        .getLatestVersion(DynamicSememe.class, StampCoordinates.getDevelopmentLatestActiveOnly());

                if (sememeVersion.isPresent()) {
                    @SuppressWarnings("rawtypes")
                    DynamicSememe ds = sememeVersion.get().value();
                    DynamicSememeDataBI[] refexDefinitionData = ds.getData();

                    if (sememe.getAssemblageSequence() == IsaacMetadataConstants.DYNAMIC_SEMEME_EXTENSION_DEFINITION
                            .getSequence()) {
                        if (refexDefinitionData == null || refexDefinitionData.length < 3
                                || refexDefinitionData.length > 7) {
                            throw new RuntimeException("The Assemblage concept: " + assemblageConcept
                                    + " is not correctly assembled for use as an Assemblage for "
                                    + "a DynamicSememeData Refex Type.  It must contain at least 3 columns in the DynamicSememeDataBI attachment, and no more than 7.");
                        }

                        //col 0 is the column number, 
                        //col 1 is the concept with col name 
                        //col 2 is the column data type, stored as a string.
                        //col 3 (if present) is the default column data, stored as a subtype of DynamicSememeDataBI
                        //col 4 (if present) is a boolean field noting whether the column is required (true) or optional (false or null)
                        //col 5 (if present) is the validator {@link DynamicSememeValidatorType}, stored as a string array.
                        //col 6 (if present) is the validatorData for the validator in column 5, stored as a subtype of DynamicSememeDataBI
                        try {
                            int column = (Integer) refexDefinitionData[0].getDataObject();
                            UUID descriptionUUID = (UUID) refexDefinitionData[1].getDataObject();
                            DynamicSememeDataType type = DynamicSememeDataType
                                    .valueOf((String) refexDefinitionData[2].getDataObject());
                            DynamicSememeDataBI defaultData = null;
                            if (refexDefinitionData.length > 3) {
                                defaultData = (refexDefinitionData[3] == null ? null : refexDefinitionData[3]);
                            }

                            if (defaultData != null && type.getDynamicSememeMemberClass() != refexDefinitionData[3]
                                    .getDynamicSememeDataType().getDynamicSememeMemberClass()) {
                                throw new IOException("The Assemblage concept: " + assemblageConcept
                                        + " is not correctly assembled for use as an Assemblage for "
                                        + "a DynamicSememeData Refex Type.  The type of the column (column 3) must match the type of the defaultData (column 4)");
                            }

                            Boolean columnRequired = null;
                            if (refexDefinitionData.length > 4) {
                                columnRequired = (refexDefinitionData[4] == null ? null
                                        : (Boolean) refexDefinitionData[4].getDataObject());
                            }

                            DynamicSememeValidatorType[] validators = null;
                            DynamicSememeDataBI[] validatorsData = null;
                            if (refexDefinitionData.length > 5) {
                                if (refexDefinitionData[5] != null
                                        && ((DynamicSememeArrayBI<DynamicSememeStringBI>) refexDefinitionData[5])
                                                .getDataArray().length > 0) {
                                    DynamicSememeArrayBI<DynamicSememeStringBI> readValidators = (DynamicSememeArrayBI<DynamicSememeStringBI>) refexDefinitionData[5];
                                    validators = new DynamicSememeValidatorType[readValidators
                                            .getDataArray().length];
                                    for (int i = 0; i < validators.length; i++) {
                                        validators[i] = DynamicSememeValidatorType
                                                .valueOf((String) readValidators.getDataArray()[i].getDataObject());
                                    }
                                }
                                if (refexDefinitionData.length > 6) {
                                    if (refexDefinitionData[6] != null
                                            && ((DynamicSememeArrayBI<? extends DynamicSememeDataBI>) refexDefinitionData[6])
                                                    .getDataArray().length > 0) {
                                        DynamicSememeArrayBI<? extends DynamicSememeDataBI> readValidatorsData = (DynamicSememeArrayBI<? extends DynamicSememeDataBI>) refexDefinitionData[6];
                                        validatorsData = new DynamicSememeDataBI[readValidatorsData
                                                .getDataArray().length];
                                        for (int i = 0; i < validators.length; i++) {
                                            if (readValidatorsData.getDataArray()[i] != null) {
                                                validatorsData[i] = readValidatorsData.getDataArray()[i];
                                            } else {
                                                validatorsData[i] = null;
                                            }
                                        }
                                    }
                                }
                            }

                            allowedColumnInfo.put(column,
                                    new DynamicSememeColumnInfo(assemblageConcept.getPrimordialUuid(), column,
                                            descriptionUUID, type, defaultData, columnRequired, validators,
                                            validatorsData));
                        } catch (Exception e) {
                            throw new RuntimeException("The Assemblage concept: " + assemblageConcept
                                    + " is not correctly assembled for use as an Assemblage for "
                                    + "a DynamicSememeData Refex Type.  The first column must have a data type of integer, and the third column must be a string "
                                    + "that is parseable as a DynamicSememeDataType");
                        }
                    } else if (sememe
                            .getAssemblageSequence() == IsaacMetadataConstants.DYNAMIC_SEMEME_REFERENCED_COMPONENT_RESTRICTION
                                    .getSequence()) {
                        if (refexDefinitionData == null || refexDefinitionData.length < 1) {
                            throw new RuntimeException("The Assemblage concept: " + assemblageConcept
                                    + " is not correctly assembled for use as an Assemblage for "
                                    + "a DynamicSememeData Refex Type.  If it contains a "
                                    + IsaacMetadataConstants.DYNAMIC_SEMEME_REFERENCED_COMPONENT_RESTRICTION
                                            .getFSN()
                                    + " then it must contain a single column of data, of type string, parseable as a "
                                    + ObjectChronologyType.class.getName());
                        }

                        //col 0 is Referenced component restriction information - as a string. 
                        try {
                            ObjectChronologyType type = ObjectChronologyType
                                    .parse(refexDefinitionData[0].getDataObject().toString());
                            if (type == ObjectChronologyType.UNKNOWN_NID) {
                                //just ignore - it shouldn't have been saved that way anyway.
                            } else {
                                referencedComponentTypeRestriction_ = type;
                            }
                        } catch (Exception e) {
                            throw new RuntimeException("The Assemblage concept: " + assemblageConcept
                                    + " is not correctly assembled for use as an Assemblage for "
                                    + "a DynamicSememeData Refex Type.  The component type restriction annotation has an invalid value");
                        }

                        //col 1 is an optional Referenced component sub-restriction information - as a string.
                        if (refexDefinitionData.length > 1 && refexDefinitionData[1] != null) {
                            try {
                                SememeType type = SememeType
                                        .parse(refexDefinitionData[1].getDataObject().toString());
                                if (type == SememeType.UNKNOWN) {
                                    //just ignore - it shouldn't have been saved that way anyway.
                                } else {
                                    referencedComponentTypeSubRestriction_ = type;
                                }
                            } catch (Exception e) {
                                throw new RuntimeException("The Assemblage concept: " + assemblageConcept
                                        + " is not correctly assembled for use as an Assemblage for "
                                        + "a DynamicSememeData Refex Type.  The component type restriction annotation has an invalid value");
                            }
                        } else {
                            referencedComponentTypeSubRestriction_ = null;
                        }
                    }
                }

            }
        });

        refexColumnInfo_ = new DynamicSememeColumnInfo[allowedColumnInfo.size()];

        int i = 0;
        for (int key : allowedColumnInfo.keySet()) {
            if (key != i) {
                throw new RuntimeException("The Assemblage concept: " + assemblageConcept
                        + " is not correctly assembled for use as an Assemblage for "
                        + "a DynamicSememeData Refex Type.  It must contain sequential column numbers, with no gaps, which start at 0.");
            }
            refexColumnInfo_[i++] = allowedColumnInfo.get(key);
        }
    }

    /*
     * @see DynamicSememeUsageDescriptionBI#getDynamicSememeUsageDescriptorSequence()
     */
    @Override
    public int getDynamicSememeUsageDescriptorSequence() {
        return refexUsageDescriptorSequence_;
    }

    /*
     * @see DynamicSememeUsageDescriptionBI#getDynamicSememeUsageDescription()
     */
    @Override
    public String getDynamicSememeUsageDescription() {
        return sememeUsageDescription_;
    }

    /*
     * @see gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeUsageDescriptionBI#getDyanmicSememeName()
     */
    @Override
    public String getDyanmicSememeName() {
        return name_;
    }

    /*
     * @see gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeUsageDescriptionBI#getColumnInfo()
     */
    @Override
    public DynamicSememeColumnInfo[] getColumnInfo() {
        if (refexColumnInfo_ == null) {
            refexColumnInfo_ = new DynamicSememeColumnInfo[] {};
        }
        return refexColumnInfo_;
    }

    /*
     * @see gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeUsageDescriptionBI#getReferencedComponentTypeRestriction()
     */
    @Override
    public ObjectChronologyType getReferencedComponentTypeRestriction() {
        return referencedComponentTypeRestriction_;
    }

    /*
     * @see gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeUsageDescriptionBI#getReferencedComponentTypeSubRestriction()
     */
    @Override
    public SememeType getReferencedComponentTypeSubRestriction() {
        return referencedComponentTypeSubRestriction_;
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + refexUsageDescriptorSequence_;
        return result;
    }

    /**
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        DynamicSememeUsageDescription other = (DynamicSememeUsageDescription) obj;
        if (refexUsageDescriptorSequence_ != other.refexUsageDescriptorSequence_)
            return false;
        return true;
    }
}