gov.va.oia.terminology.converters.sharedUtils.propertyTypes.Property.java Source code

Java tutorial

Introduction

Here is the source code for gov.va.oia.terminology.converters.sharedUtils.propertyTypes.Property.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.va.oia.terminology.converters.sharedUtils.propertyTypes;

import gov.va.oia.terminology.converters.sharedUtils.EConceptUtility;
import gov.vha.isaac.ochre.api.component.sememe.version.dynamicSememe.DynamicSememeColumnInfo;
import gov.vha.isaac.ochre.model.constants.IsaacMetadataConstants;
import java.beans.PropertyVetoException;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.UUID;
import java.util.function.BiConsumer;
import org.apache.commons.lang3.StringUtils;
import org.ihtsdo.otf.tcc.api.coordinate.Status;
import org.ihtsdo.otf.tcc.dto.TtkConceptChronicle;
import org.ihtsdo.otf.tcc.dto.component.TtkUtils;

/**
 * 
 * {@link Property}
 *
 * The converters common code uses this property abstraction system to handle converting different property 
 * types in the WB, while maintaining consistency in how properties are represented.  Also handles advanced
 * cases where we do things like map a property to an existing WB property type, and then annotate the property 
 * instance with the terminology specific property info.
 * 
 * @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a>
 */
public class Property {
    private String sourcePropertyNameFSN_;
    private String sourcePropertyPreferredName_;
    private String sourcePropertyAltName_;
    private String sourcePropertyDefinition_;
    private boolean isDisabled_ = false;
    private int propertySubType_ = Integer.MAX_VALUE; //Used for subtypes of descriptions, at the moment - FSN, synonym, etc.
    private PropertyType owner_;
    private UUID propertyUUID = null;;
    private UUID useWBPropertyTypeInstead = null; //see comments in setter
    private DynamicSememeColumnInfo[] dataColumnsForDynamicRefex_ = null;

    private ArrayList<ConceptCreationNotificationListener> listeners_ = new ArrayList<>(1);

    /**
     * @param dataTypesForDynamicRefex - if null - will use the default information for the parent {@link PropertyType} - otherwise, 
     * uses as provided here (even if empty)
     */
    public Property(PropertyType owner, String sourcePropertyNameFSN, String sourcePropertyPreferredName,
            String sourcePropertyAltName, String sourcePropertyDefinition, boolean disabled, int propertySubType,
            DynamicSememeColumnInfo[] columnInforForDynamicRefex) {
        this.owner_ = owner;
        this.sourcePropertyNameFSN_ = sourcePropertyNameFSN;
        if (sourcePropertyNameFSN.equals(sourcePropertyPreferredName)) {
            this.sourcePropertyPreferredName_ = null;
        } else {
            this.sourcePropertyPreferredName_ = sourcePropertyPreferredName;
        }
        this.sourcePropertyAltName_ = sourcePropertyAltName;
        this.sourcePropertyDefinition_ = sourcePropertyDefinition;
        this.isDisabled_ = disabled;
        this.propertySubType_ = propertySubType;

        //if owner is null, have to delay this until the setOwner call
        //leave the assemblageConceptUUID null for now - it should be set to "getUUID()" but that isn't always ready
        //at the time this code runs.  We make sure it is set down below, in the getter.
        if (columnInforForDynamicRefex == null && owner != null && owner_.getDefaultColumnInfo() != null) {
            //Create a single required column, with the column name just set to 'value'
            dataColumnsForDynamicRefex_ = new DynamicSememeColumnInfo[] { new DynamicSememeColumnInfo(null, 0,
                    IsaacMetadataConstants.DYNAMIC_SEMEME_COLUMN_VALUE.getUUID(), owner_.getDefaultColumnInfo(),
                    null, true, null, null) };
        } else {
            dataColumnsForDynamicRefex_ = columnInforForDynamicRefex;
        }

        if (dataColumnsForDynamicRefex_ != null && !owner_.createAsDynamicRefex()) {
            throw new RuntimeException("Tried to attach dynamic sememe data where it isn't allowed.");
        }
    }

    public Property(PropertyType owner, String sourcePropertyNameFSN, String sourcePropertyPreferredName,
            int propertySubType) {
        this(owner, sourcePropertyNameFSN, sourcePropertyPreferredName, null, null, false, propertySubType, null);
    }

    public Property(PropertyType owner, String sourcePropertyNameFSN, String sourcePropertyPreferredName,
            boolean disabled) {
        this(owner, sourcePropertyNameFSN, sourcePropertyPreferredName, null, null, disabled, Integer.MAX_VALUE,
                null);
    }

    public Property(PropertyType owner, String sourcePropertyNameFSN, String sourcePropertyPreferredName) {
        this(owner, sourcePropertyNameFSN, sourcePropertyPreferredName, null, null, false, Integer.MAX_VALUE, null);
    }

    public Property(PropertyType owner, String sourcePropertyNameFSN) {
        this(owner, sourcePropertyNameFSN, sourcePropertyNameFSN, null, null, false, Integer.MAX_VALUE, null);
    }

    /**
     * owner must be set via the set method after using this constructor!
     */
    public Property(String sourcePropertyNameFSN, String sourcePropertyPreferredName,
            String sourcePropertyDefinition, UUID wbRelType) {
        this(null, sourcePropertyNameFSN, sourcePropertyPreferredName, sourcePropertyDefinition, null, false,
                Integer.MAX_VALUE, null);
        setWBPropertyType(wbRelType);
    }

    /**
     * owner must be set via the set method after using this constructor!
     */
    public Property(String sourcePropertyNameFSN, String sourcePropertyPreferredName, String sourcePropertyAltName,
            String sourcePropertyDefinition, UUID wbRelType) {
        this(null, sourcePropertyNameFSN, sourcePropertyPreferredName, sourcePropertyAltName,
                sourcePropertyDefinition, false, Integer.MAX_VALUE, null);
        setWBPropertyType(wbRelType);
    }

    public String getSourcePropertyNameFSN() {
        return sourcePropertyNameFSN_;
    }

    public String getSourcePropertyPreferredName() {
        return sourcePropertyPreferredName_;
    }

    public String getSourcePropertyAltName() {
        return sourcePropertyAltName_;
    }

    public String getSourcePropertyDefinition() {
        return sourcePropertyDefinition_;
    }

    /**
     * Normally, we just create the relation names as specified.  However, some, we map to 
     * other existing WB relationships, and put the source rel name on as an extension - for example
     * To enable the map case, set this (and use the appropriate addRelationship method)
     */
    public void setWBPropertyType(UUID wbRelType) {
        this.useWBPropertyTypeInstead = wbRelType;
    }

    public UUID getWBTypeUUID() {
        return useWBPropertyTypeInstead;
    }

    protected void setOwner(PropertyType owner) {
        this.owner_ = owner;

        if (dataColumnsForDynamicRefex_ == null && owner_.getDefaultColumnInfo() != null) {
            //Create a single required column, with the column name concept tied back to the assemblage concept itself.
            //leave the assemblageConceptUUID null for now - it should be set to "getUUID()" but that isn't always ready
            //at the time this code runs.  We make sure it is set down below, in the getter.
            dataColumnsForDynamicRefex_ = new DynamicSememeColumnInfo[] { new DynamicSememeColumnInfo(null, 0,
                    getUUID(), owner_.getDefaultColumnInfo(), null, true, null, null) };
        }
        if (dataColumnsForDynamicRefex_ != null && !owner_.createAsDynamicRefex()) {
            throw new RuntimeException("Tried to attach dynamic sememe data where it isn't allowed.");
        }

    }

    public UUID getUUID() {
        if (propertyUUID == null) {
            propertyUUID = owner_.getPropertyUUID(this.sourcePropertyNameFSN_);
        }
        return propertyUUID;
    }

    public boolean isDisabled() {
        return isDisabled_;
    }

    public void setPropertySubType(int value) {
        this.propertySubType_ = value;
    }

    public int getPropertySubType() {
        return propertySubType_;
    }

    public PropertyType getPropertyType() {
        return owner_;
    }

    public DynamicSememeColumnInfo[] getDataColumnsForDynamicRefex() {
        if (dataColumnsForDynamicRefex_ != null && dataColumnsForDynamicRefex_.length == 1
                && dataColumnsForDynamicRefex_[0].getAssemblageConcept() == null) {
            dataColumnsForDynamicRefex_[0].setAssemblageConcept(getUUID());
        }
        return dataColumnsForDynamicRefex_;
    }

    /**
     * Mechanism to allow registration for notification when the corresponding eConcept has been created for this property.
     * Callback occurs before the eConcept is written.  Useful for adding additional attributes to the eConcept.
     * @param listener
     */
    public void registerConceptCreationListener(ConceptCreationNotificationListener listener) {
        listeners_.add(listener);
    }

    /**
     * This is called just before a metadata concept is written when the typical loadMetaDataItems(...) sequence is used in the eConceptUtility.  
     * 
     * Any the created concept will be passed to any registered listeners before the concept is written.
     * @param concept
     */
    public BiConsumer<TtkConceptChronicle, EConceptUtility> getCallback() {
        return new BiConsumer<TtkConceptChronicle, EConceptUtility>() {
            @Override
            public void accept(TtkConceptChronicle concept, EConceptUtility utility) {
                try {
                    if (Property.this.getPropertyType().createAsDynamicRefex()) {
                        TtkUtils.configureConceptAsDynamicRefex(concept,
                                (StringUtils.isNotEmpty(Property.this.getSourcePropertyDefinition())
                                        ? Property.this.getSourcePropertyDefinition()
                                        : "Dynamic Sememe"),
                                Property.this.getDataColumnsForDynamicRefex(), null, null,
                                ((rev) -> utility.setRevisionAttributes(rev, Status.ACTIVE,
                                        concept.getConceptAttributes().getTime())));

                        utility.registerDynamicSememeColumnInfo(Property.this.getUUID(),
                                Property.this.getDataColumnsForDynamicRefex());

                        if (Property.this.getDataColumnsForDynamicRefex() != null
                                && Property.this.getDataColumnsForDynamicRefex().length > 0) {
                            if (Property.this.getDataColumnsForDynamicRefex() != null) {
                                Integer[] temp = new Integer[Property.this.getDataColumnsForDynamicRefex().length];
                                for (int i = 0; i < temp.length; i++) {
                                    temp[i] = i;
                                }

                                //Not really the right place to put this sememe... but it will get moved appropriate when loaded in ochre.
                                concept.getConceptAttributes().getAnnotationsDynamic()
                                        .add(TtkUtils.configureDynamicRefexIndexes(Property.this.getUUID(), temp,
                                                (rev -> utility.setRevisionAttributes(rev, Status.ACTIVE,
                                                        concept.getConceptAttributes().getTime()))));
                            }
                        }
                    }

                    if (Property.this instanceof PropertyAssociation) {
                        PropertyAssociation item = (PropertyAssociation) Property.this;
                        DynamicSememeColumnInfo[] columns = TtkUtils.configureConceptAsAssociation(concept,
                                item.getSourcePropertyDefinition(), item.getAssociationInverseName(),
                                item.getAssociationComponentTypeRestriction(),
                                item.getAssociationComponentTypeSubRestriction(),
                                (rev -> utility.setRevisionAttributes(rev, Status.ACTIVE,
                                        concept.getConceptAttributes().getTime())));
                        utility.registerDynamicSememeColumnInfo(Property.this.getUUID(), columns);
                    }
                } catch (NoSuchAlgorithmException | UnsupportedEncodingException | PropertyVetoException e) {
                    throw new RuntimeException("Unexpected");
                }

                for (ConceptCreationNotificationListener ccn : listeners_) {
                    ccn.conceptCreated(Property.this, concept);
                }
            }
        };
    }
}