sh.isaac.model.semantic.DynamicUsageDescriptionImpl.java Source code

Java tutorial

Introduction

Here is the source code for sh.isaac.model.semantic.DynamicUsageDescriptionImpl.java

Source

/* 
 * 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.model.semantic;

//~--- JDK imports ------------------------------------------------------------

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;

//~--- non-JDK imports --------------------------------------------------------

import org.apache.commons.lang3.StringUtils;

import sh.isaac.api.Get;
import sh.isaac.api.bootstrap.TermAux;
import sh.isaac.api.chronicle.LatestVersion;
import sh.isaac.api.chronicle.ObjectChronologyType;
import sh.isaac.api.collections.LruCache;
import sh.isaac.api.component.concept.ConceptChronology;
import sh.isaac.api.chronicle.VersionType;
import sh.isaac.api.component.semantic.version.dynamic.DynamicColumnInfo;
import sh.isaac.api.component.semantic.version.dynamic.DynamicDataType;
import sh.isaac.api.component.semantic.version.dynamic.DynamicValidatorType;
import sh.isaac.api.constants.DynamicConstants;
import sh.isaac.model.configuration.StampCoordinates;
import sh.isaac.api.component.semantic.version.DescriptionVersion;
import sh.isaac.api.component.semantic.SemanticChronology;
import sh.isaac.api.component.semantic.version.DynamicVersion;
import sh.isaac.api.component.semantic.version.dynamic.DynamicData;
import sh.isaac.api.component.semantic.version.dynamic.DynamicUsageDescription;
import sh.isaac.api.component.semantic.version.dynamic.types.DynamicArray;
import sh.isaac.api.component.semantic.version.dynamic.types.DynamicString;

//~--- classes ----------------------------------------------------------------

/**
 * See {@link DynamicUsageDescription}.
 *
 * @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a>
 */
public class DynamicUsageDescriptionImpl implements DynamicUsageDescription {
    /** The Constant logger. */
    protected static final Logger logger = Logger.getLogger(DynamicUsageDescription.class.getName());

    /** The cache. */
    private static LruCache<Integer, DynamicUsageDescriptionImpl> cache = new LruCache<Integer, DynamicUsageDescriptionImpl>(
            25);

    //~--- fields --------------------------------------------------------------

    /** The refex usage descriptor sequence. */
    int refexUsageDescriptorNid;

    /** The sememe usage description. */
    String sememeUsageDescription;

    /** The name. */
    String name;

    /** The refex column info. */
    DynamicColumnInfo[] refexColumnInfo;

    /** The referenced component type restriction. */
    ObjectChronologyType referencedComponentTypeRestriction;

    /** The referenced component type sub restriction. */
    VersionType referencedComponentTypeSubRestriction;

    //~--- constructors --------------------------------------------------------

    /**
     * Instantiates a new dynamic element usage description impl.
     */
    private DynamicUsageDescriptionImpl() {
        // For use by the mock static method
    }

    /**
     * Read the RefexUsageDescription data from the database for a given sequence or nid.
     *
     * Note that most users should call {@link #read(int)} instead, as that
     * utilizes a cache. This always reads directly from the DB.
     *
     * @param refexUsageDescriptorId sequence or NID of refexUsageDescriptor
     */
    @SuppressWarnings("unchecked")
    public DynamicUsageDescriptionImpl(int refexUsageDescriptorId) {
        final ConceptChronology assemblageConcept = Get.conceptService()
                .getConceptChronology(refexUsageDescriptorId);

        this.refexUsageDescriptorNid = assemblageConcept.getNid();

        final TreeMap<Integer, DynamicColumnInfo> allowedColumnInfo = new TreeMap<>();

        for (final SemanticChronology descriptionSememe : assemblageConcept.getConceptDescriptionList()) {
            @SuppressWarnings("rawtypes")
            final LatestVersion descriptionVersion = ((SemanticChronology) descriptionSememe)
                    .getLatestVersion(StampCoordinates.getDevelopmentLatestActiveOnly());

            if (descriptionVersion.isPresent()) {
                @SuppressWarnings("rawtypes")
                final DescriptionVersion ds = (DescriptionVersion) descriptionVersion.get();

                if (ds.getDescriptionTypeConceptNid() == TermAux.DEFINITION_DESCRIPTION_TYPE.getNid()) {
                    final Optional<SemanticChronology> nestesdSememe = Get.assemblageService()
                            .getSemanticChronologyStreamForComponentFromAssemblage(ds.getNid(),
                                    DynamicConstants.get().DYNAMIC_DEFINITION_DESCRIPTION.getNid())
                            .findAny();

                    if (nestesdSememe.isPresent()) {
                        this.sememeUsageDescription = ds.getText();
                    }
                    ;
                }

                if (ds.getDescriptionTypeConceptNid() == TermAux.FULLY_QUALIFIED_NAME_DESCRIPTION_TYPE.getNid()) {
                    this.name = ds.getText();
                }

                if ((this.sememeUsageDescription != null) && (this.name != null)) {
                    break;
                }
            }
        }

        if (StringUtils.isEmpty(this.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.assemblageService().getSemanticChronologyStreamForComponent(assemblageConcept.getNid())
                .forEach(sememe -> {
                    if (sememe.getVersionType() == VersionType.DYNAMIC) {
                        @SuppressWarnings("rawtypes")
                        final LatestVersion<? extends DynamicVersion> sememeVersion = ((SemanticChronology) sememe)
                                .getLatestVersion(StampCoordinates.getDevelopmentLatestActiveOnly());

                        if (sememeVersion.isPresent()) {
                            @SuppressWarnings("rawtypes")
                            final DynamicVersion ds = sememeVersion.get();
                            final DynamicData[] refexDefinitionData = ds.getData();

                            if (sememe.getAssemblageNid() == DynamicConstants.get().DYNAMIC_EXTENSION_DEFINITION
                                    .getNid()) {
                                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 DynamicData
                                // 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 DynamicValidatorType}, stored as a string array.
                                // col 6 (if present) is the validatorData for the validator in column 5, stored as a subtype of DynamicData
                                try {
                                    final int column = (Integer) refexDefinitionData[0].getDataObject();
                                    final UUID descriptionUUID = (UUID) refexDefinitionData[1].getDataObject();
                                    final DynamicDataType type = DynamicDataType
                                            .valueOf((String) refexDefinitionData[2].getDataObject());
                                    DynamicData defaultData = null;

                                    if (refexDefinitionData.length > 3) {
                                        defaultData = ((refexDefinitionData[3] == null) ? null
                                                : refexDefinitionData[3]);
                                    }

                                    if ((defaultData != null)
                                            && (type.getDynamicMemberClass() != refexDefinitionData[3]
                                                    .getDynamicDataType().getDynamicMemberClass())) {
                                        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());
                                    }

                                    DynamicValidatorType[] validators = null;
                                    DynamicData[] validatorsData = null;

                                    if (refexDefinitionData.length > 5) {
                                        if ((refexDefinitionData[5] != null)
                                                && ((DynamicArray<DynamicString>) refexDefinitionData[5])
                                                        .getDataArray().length > 0) {
                                            final DynamicArray<DynamicString> readValidators = (DynamicArray<DynamicString>) refexDefinitionData[5];

                                            validators = new DynamicValidatorType[readValidators
                                                    .getDataArray().length];

                                            for (int i = 0; i < validators.length; i++) {
                                                validators[i] = DynamicValidatorType.valueOf(
                                                        (String) readValidators.getDataArray()[i].getDataObject());
                                            }
                                        }

                                        if (refexDefinitionData.length > 6) {
                                            if ((refexDefinitionData[6] != null)
                                                    && ((DynamicArray<? extends DynamicData>) refexDefinitionData[6])
                                                            .getDataArray().length > 0) {
                                                final DynamicArray<? extends DynamicData> readValidatorsData = (DynamicArray<? extends DynamicData>) refexDefinitionData[6];

                                                validatorsData = new DynamicData[readValidatorsData
                                                        .getDataArray().length];

                                                if (validators != null) {
                                                    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 DynamicColumnInfo(assemblageConcept.getPrimordialUuid(), column,
                                                    descriptionUUID, type, defaultData, columnRequired, validators,
                                                    validatorsData, null));
                                } catch (final 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.getAssemblageNid() == DynamicConstants
                                    .get().DYNAMIC_SEMEME_REFERENCED_COMPONENT_RESTRICTION.getNid()) {
                                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 "
                                            + DynamicConstants.get().DYNAMIC_SEMEME_REFERENCED_COMPONENT_RESTRICTION
                                                    .getPrimaryName()
                                            + " 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 {
                                    final ObjectChronologyType type = ObjectChronologyType
                                            .parse(refexDefinitionData[0].getDataObject().toString(), false);

                                    if (type == ObjectChronologyType.UNKNOWN_NID) {
                                        // just ignore - it shouldn't have been saved that way anyway.
                                    } else {
                                        this.referencedComponentTypeRestriction = type;
                                    }
                                } catch (final 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 {
                                        final VersionType type = VersionType
                                                .parse(refexDefinitionData[1].getDataObject().toString(), false);

                                        if (type == VersionType.UNKNOWN) {
                                            // just ignore - it shouldn't have been saved that way anyway.
                                        } else {
                                            this.referencedComponentTypeSubRestriction = type;
                                        }
                                    } catch (final 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 {
                                    this.referencedComponentTypeSubRestriction = null;
                                }
                            }
                        }
                    }
                });
        this.refexColumnInfo = new DynamicColumnInfo[allowedColumnInfo.size()];

        int i = 0;

        for (final 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.");
            }

            this.refexColumnInfo[i++] = allowedColumnInfo.get(key);
        }
    }

    //~--- methods -------------------------------------------------------------

    /**
     * Equals.
     *
     * @param obj the obj
     * @return true, if successful
     * @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;
        }

        final DynamicUsageDescriptionImpl other = (DynamicUsageDescriptionImpl) obj;

        if (this.refexUsageDescriptorNid != other.refexUsageDescriptorNid) {
            return false;
        }

        return true;
    }

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

        result = prime * result + this.refexUsageDescriptorNid;
        return result;
    }

    /**
     * Invent DynamicUsageDescription 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
     * @return the dynamic element usage description
     */
    public static DynamicUsageDescription mockOrRead(SemanticChronology sememe) {
        final DynamicUsageDescriptionImpl dsud = new DynamicUsageDescriptionImpl();

        dsud.name = Get.conceptDescriptionText(sememe.getAssemblageNid());
        dsud.referencedComponentTypeRestriction = null;
        dsud.referencedComponentTypeSubRestriction = null;
        dsud.refexUsageDescriptorNid = sememe.getAssemblageNid();
        dsud.sememeUsageDescription = "-";

        switch (sememe.getVersionType()) {
        case COMPONENT_NID:
            dsud.refexColumnInfo = new DynamicColumnInfo[] { new DynamicColumnInfo(
                    Get.identifierService().getUuidPrimordialForNid(sememe.getAssemblageNid()).get(), 0,
                    DynamicConstants.get().DYNAMIC_DT_NID.getPrimordialUuid(), DynamicDataType.NID, null, true,
                    null, null, false) };
            break;

        case LONG:
            dsud.refexColumnInfo = new DynamicColumnInfo[] { new DynamicColumnInfo(
                    Get.identifierService().getUuidPrimordialForNid(sememe.getAssemblageNid()).get(), 0,
                    DynamicConstants.get().DYNAMIC_DT_LONG.getPrimordialUuid(), DynamicDataType.LONG, null, true,
                    null, null, false) };
            break;

        case DESCRIPTION:
        case STRING:
        case LOGIC_GRAPH:
            dsud.refexColumnInfo = new DynamicColumnInfo[] { new DynamicColumnInfo(
                    Get.identifierService().getUuidPrimordialForNid(sememe.getAssemblageNid()).get(), 0,
                    DynamicConstants.get().DYNAMIC_DT_STRING.getPrimordialUuid(), DynamicDataType.STRING, null,
                    true, null, null, false) };
            break;

        case MEMBER:
            dsud.refexColumnInfo = new DynamicColumnInfo[] {};
            break;

        case DYNAMIC:
            return read(sememe.getAssemblageNid());

        case UNKNOWN:
        default:
            throw new RuntimeException("Use case not yet supported");
        }

        return dsud;
    }

    /**
     * Read.
     *
     * @param assemblageNid the assemblage nid or sequence
     * @return the dynamic element usage description
     */
    public static DynamicUsageDescription read(int assemblageNid) {
        // 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 element.  Perhaps
        // implement a callback to clear the cache when we know a change of  a certain type happened instead?
        DynamicUsageDescriptionImpl temp = cache.get(assemblageNid);

        if (temp == null) {
            logger.log(Level.FINEST, "Cache miss on DynamicSememeUsageDescription Cache");
            temp = new DynamicUsageDescriptionImpl(assemblageNid);
            cache.put(assemblageNid, temp);
        }

        return temp;
    }

    //~--- get methods ---------------------------------------------------------

    /**
     * Gets the column info.
     *
     * @return the column info
     */

    /*
     *     @see sh.isaac.api.component.sememe.version.dynamicSememe.DynamicUsageDescription#getColumnInfo()
     */
    @Override
    public DynamicColumnInfo[] getColumnInfo() {
        if (this.refexColumnInfo == null) {
            this.refexColumnInfo = new DynamicColumnInfo[] {};
        }

        return this.refexColumnInfo;
    }

    /**
     * Test if dyn sememe.
     *
     * @param assemblageNid the assemblage nid 
     * @return true, if dynamic element
     */
    public static boolean isDynamicSememe(int assemblageNid) {
        try {
            read(assemblageNid);
            return true;
        } catch (final Exception e) {
            return false;
        }
    }

    /**
     * Gets the dynamic element name.
     *
     * @return the dynamic element name
     */

    /*
     *     @see sh.isaac.api.component.sememe.version.dynamicSememe.DynamicUsageDescription#getDyanmicSememeName()
     */
    @Override
    public String getDynamicName() {
        return this.name;
    }

    /**
     * Gets the dynamic element usage description.
     *
     * @return the dynamic element usage description
     */

    /*
     *     @see DynamicUsageDescription#getDynamicSememeUsageDescription()
     */
    @Override
    public String getDynamicUsageDescription() {
        return this.sememeUsageDescription;
    }

    /**
     * Gets the dynamic element usage descriptor sequence.
     *
     * @return the dynamic element usage descriptor sequence
     */

    /*
     *     @see DynamicUsageDescription#getDynamicSememeUsageDescriptorSequence()
     */
    @Override
    public int getDynamicUsageDescriptorSequence() {
        return this.refexUsageDescriptorNid;
    }

    /**
     * Gets the referenced component type restriction.
     *
     * @return the referenced component type restriction
     */

    /*
     *     @see sh.isaac.api.component.sememe.version.dynamicSememe.DynamicUsageDescription#getReferencedComponentTypeRestriction()
     */
    @Override
    public ObjectChronologyType getReferencedComponentTypeRestriction() {
        return this.referencedComponentTypeRestriction;
    }

    /**
     * Gets the referenced component type sub restriction.
     *
     * @return the referenced component type sub restriction
     */

    /*
     *     @see sh.isaac.api.component.sememe.version.dynamicSememe.DynamicUsageDescription#getReferencedComponentTypeSubRestriction()
     */
    @Override
    public VersionType getReferencedComponentTypeSubRestriction() {
        return this.referencedComponentTypeSubRestriction;
    }
}