/* * 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. * */ /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package sh.isaac.provider.taxonomy; //~--- JDK imports ------------------------------------------------------------ import java.util.EnumSet; import java.util.stream.IntStream; import java.util.stream.IntStream.Builder; import java.util.stream.LongStream; import java.util.stream.Stream; //~--- non-JDK imports -------------------------------------------------------- import org.apache.mahout.math.function.LongProcedure; import org.apache.mahout.math.set.OpenLongHashSet; import sh.isaac.api.Get; import sh.isaac.api.collections.ConceptSequenceSet; import sh.isaac.api.collections.StampSequenceSet; import sh.isaac.api.coordinate.TaxonomyCoordinate; import sh.isaac.api.snapshot.calculator.RelativePositionCalculator; //~--- classes ---------------------------------------------------------------- /** * This class maps stamps to the {@code TaxonomyFlags} associated with that * stampSequence. * * @author kec */ public class TypeStampTaxonomyRecords { /** int (the map key) is a stampSequence TaxonomyFlags (the map value) are the flags associated with the stampSequence;. */ private final OpenLongHashSet typeStampFlagsSet = new OpenLongHashSet(7); //~--- constructors -------------------------------------------------------- /** * Instantiates a new type stamp taxonomy records. */ public TypeStampTaxonomyRecords() {} /** * Instantiates a new type stamp taxonomy records. * * @param sourceArray the source array * @param sourcePosition the source position */ public TypeStampTaxonomyRecords(int[] sourceArray, int sourcePosition) { final int length = sourceArray[sourcePosition] >>> 24; final int recordEnd = sourcePosition + length; for (sourcePosition = sourcePosition + 1; sourcePosition < recordEnd; sourcePosition += 2) { long record = sourceArray[sourcePosition]; record = record << 32; record += sourceArray[sourcePosition + 1]; this.typeStampFlagsSet.add(record); } } /** * Instantiates a new type stamp taxonomy records. * * @param typeSequence the type sequence * @param stampSequence the stamp sequence * @param flag the flag */ public TypeStampTaxonomyRecords(int typeSequence, int stampSequence, TaxonomyFlags flag) { this.typeStampFlagsSet.add(convertToLong(typeSequence, stampSequence, flag.bits)); } //~--- methods ------------------------------------------------------------- /** * Adds the stamp record. * * @param typeSequence the type sequence * @param stampSequence the stamp sequence * @param taxonomyFlags the taxonomy flags */ public void addStampRecord(int typeSequence, int stampSequence, int taxonomyFlags) { final long record = convertToLong(typeSequence, stampSequence, taxonomyFlags); this.typeStampFlagsSet.add(record); } /** * Adds the to int array. * * @param conceptSequence the concept sequence * @param destinationArray the destination array * @param destinationPosition the destination position */ public void addToIntArray(int conceptSequence, int[] destinationArray, int destinationPosition) { final int length = length(); final int index = destinationPosition + 1; destinationArray[destinationPosition] = conceptSequence + (length << 24); final AddToArrayProcedure addToArrayIntObjectProcedure = new AddToArrayProcedure(index, destinationArray); this.typeStampFlagsSet.forEachKey(addToArrayIntObjectProcedure); } /** * Contains concept sequence via type. * * @param typeSequenceSet the type sequence set * @param flags the flags * @param computer the computer * @return true, if successful */ public boolean containsConceptSequenceViaType(ConceptSequenceSet typeSequenceSet, int flags, RelativePositionCalculator computer) { final StampSequenceSet latestStamps = computer.getLatestStampSequencesAsSet(getStampsOfTypeWithFlags(typeSequenceSet, flags)); return !latestStamps.isEmpty(); } /** * Contains concept sequence via type. * * @param typeSequenceSet the type sequence set * @param tc the tc * @param computer the computer * @return true, if successful */ public boolean containsConceptSequenceViaType(ConceptSequenceSet typeSequenceSet, TaxonomyCoordinate tc, RelativePositionCalculator computer) { final int flags = TaxonomyFlags.getFlagsFromTaxonomyCoordinate(tc); return containsConceptSequenceViaType(typeSequenceSet, flags, computer); } /** * Contains concept sequence via type. * * @param typeSequence the type sequence * @param flags the flags * @param computer the computer * @return true, if successful */ public boolean containsConceptSequenceViaType(int typeSequence, int flags, RelativePositionCalculator computer) { final StampSequenceSet latestStamps = computer.getLatestStampSequencesAsSet(getStampsOfTypeWithFlags(typeSequence, flags)); return !latestStamps.isEmpty(); } /** * Contains concept sequence via type. * * @param typeSequence the type sequence * @param tc the tc * @param computer the computer * @return true, if successful */ public boolean containsConceptSequenceViaType(int typeSequence, TaxonomyCoordinate tc, RelativePositionCalculator computer) { final int flags = TaxonomyFlags.getFlagsFromTaxonomyCoordinate(tc); return containsConceptSequenceViaType(typeSequence, flags, computer); } /** * Contains stamp of type with flags. * * @param typeSequenceSet An empty set is a wildcard and will match all types. * @param flags the flags * @return true if found. */ public boolean containsStampOfTypeWithFlags(ConceptSequenceSet typeSequenceSet, int flags) { final boolean found = !this.typeStampFlagsSet.forEachKey((long record) -> { if (typeSequenceSet.isEmpty()) { // wildcard if (((record >>> 32) & TaxonomyRecordPrimitive.FLAGS_BIT_MASK) == flags) { return false; // finish search. } } else if (typeSequenceSet.contains(((int) record & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK))) { if (((record >>> 32) & TaxonomyRecordPrimitive.FLAGS_BIT_MASK) == flags) { return false; // finish search. } } return true; // continue search... }); return found; } /** * Contains stamp of type with flags. * * @param typeSequence Integer.MAX_VALUE is a wildcard and will match all types. * @param flags the flags * @return true if found. */ public boolean containsStampOfTypeWithFlags(int typeSequence, int flags) { final boolean found = !this.typeStampFlagsSet.forEachKey((long record) -> { if (typeSequence == Integer.MAX_VALUE) { // wildcard if (flags == 0) { // taxonomy flag wildcard--inferred, stated, non-defining, ... return false; // finish search } else if (((record >>> 32) & TaxonomyRecordPrimitive.FLAGS_BIT_MASK) == flags) { return false; // finish search. } } else if ((record & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK) == typeSequence) { if (flags == 0) { // taxonomy flag wildcard--inferred, stated, non-defining, ... return false; // finish search } else if (((record >>> 32) & TaxonomyRecordPrimitive.FLAGS_BIT_MASK) == flags) { return false; // finish search. } } return true; // continue search... }); return found; } /** * Convert to long. * * @param typeSequence the type sequence * @param stampSequence the stamp sequence * @param taxonomyFlags the taxonomy flags * @return the long */ public static long convertToLong(int typeSequence, int stampSequence, int taxonomyFlags) { long record = stampSequence; if (taxonomyFlags > 512) { record += taxonomyFlags; } else { record += (taxonomyFlags << 24); } record = record << 32; record += typeSequence; return record; } /** * Length. * * @return the number of integers this stampSequence record will occupy when * packed. */ public int length() { // 1 is for the concept sequence with the top 8 bits set to the length // of sequence plus the associated stampSequence records. return 1 + (this.typeStampFlagsSet.size() * 2); } /** * Merge. * * @param newRecords the new records */ public void merge(TypeStampTaxonomyRecords newRecords) { newRecords.typeStampFlagsSet.forEachKey((long recordAsLong) -> { this.typeStampFlagsSet.add(recordAsLong); return true; }); } /** * Stream. * * @return the stream */ public Stream<TypeStampTaxonomyRecord> stream() { final Stream.Builder<TypeStampTaxonomyRecord> builder = Stream.builder(); this.typeStampFlagsSet.forEachKey((long record) -> { builder.accept(new TypeStampTaxonomyRecord(record)); return true; }); return builder.build(); } /** * To string. * * @return the string */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); this.typeStampFlagsSet.forEachKey((long record) -> { final TypeStampTaxonomyRecord str = new TypeStampTaxonomyRecord(record); sb.append(str.toString()); return true; }); return sb.toString(); } //~--- get methods --------------------------------------------------------- /** * Checks if present. * * @param typeSequenceSet the type sequence set * @param flags the flags * @return true, if present */ public boolean isPresent(ConceptSequenceSet typeSequenceSet, int flags) { return containsStampOfTypeWithFlags(typeSequenceSet, flags); } /** * Gets the stamps of type with flags. * * @param typeSequenceSet the type sequence set * @param flags the flags * @return the stamps of type with flags */ public IntStream getStampsOfTypeWithFlags(ConceptSequenceSet typeSequenceSet, int flags) { final Builder intStreamBuilder = IntStream.builder(); this.typeStampFlagsSet.forEachKey((long record) -> { final int stampAndFlag = (int) (record >>> 32); if (typeSequenceSet.isEmpty()) { // wildcard if ((stampAndFlag & TaxonomyRecordPrimitive.FLAGS_BIT_MASK) == flags) { intStreamBuilder.accept(stampAndFlag & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK); } } else if (typeSequenceSet.contains((int) record & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK)) { if ((stampAndFlag & TaxonomyRecordPrimitive.FLAGS_BIT_MASK) == flags) { intStreamBuilder.accept(stampAndFlag & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK); } } return true; }); return intStreamBuilder.build(); } /** * Gets the stamps of type with flags. * * @param typeSequence the type sequence * @param flags the flags * @return the stamps of type with flags */ public IntStream getStampsOfTypeWithFlags(int typeSequence, int flags) { final Builder intStreamBuilder = IntStream.builder(); this.typeStampFlagsSet.forEachKey((long record) -> { final int stampAndFlag = (int) (record >>> 32); if (typeSequence == Integer.MAX_VALUE) { // wildcard if ((stampAndFlag & TaxonomyRecordPrimitive.FLAGS_BIT_MASK) == flags) { intStreamBuilder.accept(stampAndFlag & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK); } } else if ((record & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK) == typeSequence) { if ((stampAndFlag & TaxonomyRecordPrimitive.FLAGS_BIT_MASK) == flags) { intStreamBuilder.accept(stampAndFlag & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK); } } return true; }); return intStreamBuilder.build(); } /** * Gets the type stamp flag stream. * * @return the type stamp flag stream */ public LongStream getTypeStampFlagStream() { return LongStream.of(this.typeStampFlagsSet.keys() .elements()); } //~--- inner classes ------------------------------------------------------- /** * The Class AddToArrayProcedure. */ private static class AddToArrayProcedure implements LongProcedure { /** The index. */ int index; /** The destination array. */ int destinationArray[]; //~--- constructors ----------------------------------------------------- /** * Instantiates a new adds the to array procedure. * * @param index the index * @param destinationArray the destination array */ public AddToArrayProcedure(int index, int[] destinationArray) { this.index = index; this.destinationArray = destinationArray; } //~--- methods ---------------------------------------------------------- /** * Adds the combined typeSequence + stampSequence + flags to the index location in the * destination array defined in the procedure constructor. * * @param record the record * @return true to continue. */ @Override public boolean apply(long record) { final int stampAndFlags = (int) (record >>> 32); this.destinationArray[this.index++] = stampAndFlags; this.destinationArray[this.index++] = (int) record; return true; } } /** * The Class TypeStampTaxonomyRecord. */ public static class TypeStampTaxonomyRecord { /** The type sequence. */ int typeSequence; /** The stamp sequence. */ int stampSequence; /** The taxonomy flags. */ int taxonomyFlags; //~--- constructors ----------------------------------------------------- /** * Instantiates a new type stamp taxonomy record. * * @param record the record */ public TypeStampTaxonomyRecord(long record) { this.typeSequence = (int) record & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK; record = record >>> 32; this.stampSequence = (int) record & TaxonomyRecordPrimitive.SEQUENCE_BIT_MASK; this.taxonomyFlags = (int) record & TaxonomyRecordPrimitive.FLAGS_BIT_MASK; } /** * Instantiates a new type stamp taxonomy record. * * @param typeSequence the type sequence * @param stampSequence the stamp sequence * @param taxonomyFlags the taxonomy flags */ public TypeStampTaxonomyRecord(int typeSequence, int stampSequence, int taxonomyFlags) { this.typeSequence = typeSequence; this.stampSequence = stampSequence; this.taxonomyFlags = taxonomyFlags; } //~--- methods ---------------------------------------------------------- /** * Equals. * * @param obj the obj * @return true, if successful */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final TypeStampTaxonomyRecord other = (TypeStampTaxonomyRecord) obj; if (this.stampSequence != other.stampSequence) { return false; } if (this.typeSequence != other.typeSequence) { return false; } return this.taxonomyFlags == other.taxonomyFlags; } /** * Hash code. * * @return the int */ @Override public int hashCode() { int hash = 7; hash = 97 * hash + this.stampSequence; return hash; } /** * To string. * * @return the string */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("«"); sb.append(Get.conceptService() .getConcept(this.typeSequence) .toUserString()); sb.append(" <"); sb.append(this.typeSequence); sb.append(">"); sb.append(" ss:"); sb.append(this.stampSequence); sb.append(" "); sb.append(Get.stampService() .describeStampSequence(this.stampSequence)); sb.append(" "); sb.append(getTaxonomyFlagsAsEnum()); sb.append("»"); return sb.toString(); } //~--- get methods ------------------------------------------------------ /** * Gets the as long. * * @return the as long */ public long getAsLong() { return convertToLong(this.typeSequence, this.stampSequence, this.taxonomyFlags); } /** * Gets the stamp sequence. * * @return the stamp sequence */ public int getStampSequence() { return this.stampSequence; } /** * Gets the taxonomy flags. * * @return the taxonomy flags */ public int getTaxonomyFlags() { return this.taxonomyFlags; } /** * Gets the taxonomy flags as enum. * * @return the taxonomy flags as enum */ public EnumSet<TaxonomyFlags> getTaxonomyFlagsAsEnum() { return TaxonomyFlags.getTaxonomyFlags(this.taxonomyFlags); } /** * Gets the type sequence. * * @return the type sequence */ public int getTypeSequence() { return this.typeSequence; } } }