| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.uima.cas.impl; |
| |
| import org.apache.uima.cas.CAS; |
| import org.apache.uima.cas.FSIndex; |
| import org.apache.uima.cas.FSIterator; |
| import org.apache.uima.cas.Feature; |
| import org.apache.uima.cas.FeatureStructure; |
| import org.apache.uima.cas.Type; |
| import org.apache.uima.cas.admin.FSIndexComparator; |
| import org.apache.uima.cas.admin.LinearTypeOrder; |
| import org.apache.uima.internal.util.ComparableIntPointerIterator; |
| import org.apache.uima.internal.util.IntComparator; |
| import org.apache.uima.internal.util.IntPointerIterator; |
| |
| /** |
| * Class comment for FSLeafIndexImpl.java goes here. |
| * |
| * |
| */ |
| public abstract class FSLeafIndexImpl<T extends FeatureStructure> implements IntComparator, FSIndex<T>, FSIndexImpl { |
| |
| private final int indexType; |
| |
| // A reference to the low-level CAS. |
| protected CASImpl lowLevelCAS; |
| |
| private static final int STRING_CODE = 0; |
| |
| private static final int FLOAT_CODE = 1; |
| |
| private static final int INT_CODE = 2; |
| |
| private static final int TYPE_ORDER_CODE = 3; |
| |
| private static final int BOOLEAN_CODE = 4; |
| |
| private static final int BYTE_CODE = 5; |
| |
| private static final int SHORT_CODE = 6; |
| |
| private static final int LONG_CODE = 7; |
| |
| private static final int DOUBLE_CODE = 8; |
| |
| private FSIndexComparatorImpl comparator; |
| |
| private boolean isInitialized = false; |
| |
| // For each key, the int code of the type of that key. |
| private int[] keyType; |
| |
| // For each feature key, the feature offset. |
| private int[] keyOffset; |
| |
| // An array of the type orders used. This array is dense, most values will |
| // be null. |
| private LinearTypeOrder[] typeOrder; |
| |
| // For each key, the comparison to use. |
| private int[] keyComp; |
| |
| // The number of keys. |
| private int numKeys; |
| |
| private Type type; // The type of this |
| |
| // never called |
| // declared private to block external calls |
| @SuppressWarnings("unused") |
| private FSLeafIndexImpl() { |
| super(); |
| this.indexType = 0; // must do because it's final |
| } |
| |
| /** |
| * Constructor for FSLeafIndexImpl. |
| */ |
| protected FSLeafIndexImpl(CASImpl cas, Type type, int indexType) { |
| super(); |
| this.indexType = indexType; |
| this.lowLevelCAS = cas; |
| this.type = type; |
| } |
| |
| abstract boolean insert(int fs); |
| |
| abstract void remove(int fs); |
| |
| // public abstract IntListIterator iterator(); |
| |
| // public abstract ComparableIntIterator iterator(IntComparator comp); |
| |
| public abstract ComparableIntPointerIterator pointerIterator(IntComparator comp, |
| int[] detectIllegalIndexUpdates, int typeCode); |
| |
| public FSIndexComparator getComparator() { |
| return this.comparator; |
| } |
| |
| IntComparator getIntComparator() { |
| return this; |
| } |
| |
| public int getIndexingStrategy() { |
| return this.indexType; |
| } |
| |
| boolean init(FSIndexComparator comp) { |
| if (this.isInitialized) { |
| return false; |
| } |
| FSIndexComparatorImpl comp1 = (FSIndexComparatorImpl) comp; |
| this.comparator = comp1.copy(); |
| if (!this.comparator.isValid()) { |
| return false; |
| } |
| final int nKeys = this.comparator.getNumberOfKeys(); |
| // Initialize the comparator info. |
| this.keyType = new int[nKeys]; |
| this.keyOffset = new int[nKeys]; |
| this.keyComp = new int[nKeys]; |
| this.typeOrder = new LinearTypeOrder[nKeys]; |
| Feature keyFeature; |
| for (int i = 0; i < nKeys; i++) { |
| switch (comp.getKeyType(i)) { |
| case FSIndexComparator.FEATURE_KEY: { |
| keyFeature = this.comparator.getKeyFeature(i); |
| this.keyType[i] = getKeyCode(keyFeature); |
| this.keyOffset[i] = getFeatureOffset(keyFeature); |
| this.keyComp[i] = this.comparator.getKeyComparator(i); |
| break; |
| } |
| case FSIndexComparator.TYPE_ORDER_KEY: { |
| this.keyType[i] = TYPE_ORDER_CODE; |
| this.keyComp[i] = this.comparator.getKeyComparator(i); |
| this.typeOrder[i] = this.comparator.getKeyTypeOrder(i); |
| this.keyOffset[i] = 0; |
| break; |
| } |
| default: { |
| // This is an internal error. |
| throw new RuntimeException("Assertion failed."); |
| } |
| } |
| } |
| this.numKeys = nKeys; |
| this.isInitialized = true; |
| return true; |
| } |
| |
| private static final int getKeyCode(Feature feat) { |
| String typeName = feat.getRange().getName(); |
| if (typeName.equals(CAS.TYPE_NAME_STRING)) { |
| return STRING_CODE; |
| } |
| if (typeName.equals(CAS.TYPE_NAME_FLOAT)) { |
| return FLOAT_CODE; |
| } |
| if (typeName.equals(CAS.TYPE_NAME_BOOLEAN)) { |
| return BOOLEAN_CODE; |
| } |
| if (typeName.equals(CAS.TYPE_NAME_BYTE)) { |
| return BYTE_CODE; |
| } |
| if (typeName.equals(CAS.TYPE_NAME_SHORT)) { |
| return SHORT_CODE; |
| } |
| if (typeName.equals(CAS.TYPE_NAME_LONG)) { |
| return LONG_CODE; |
| } |
| if (typeName.equals(CAS.TYPE_NAME_DOUBLE)) { |
| return DOUBLE_CODE; |
| } |
| // This is defaulty and not nice. We rely on the checking in |
| // FSIndexComparatorImpl to make sure that only correct types get |
| // through. |
| return INT_CODE; |
| } |
| |
| private final int getFeatureOffset(Feature feat) { |
| return this.comparator.getLowLevelCAS().getFeatureOffset(((FeatureImpl) feat).getCode()); |
| } |
| |
| public int ll_compare(int ref1, int ref2) { |
| return this.compare(ref1, ref2); |
| } |
| |
| public int compare(int fs1, int fs2) { |
| int[] heap = this.lowLevelCAS.getHeap().heap; |
| int val1, val2; |
| int compVal; |
| float float1, float2; |
| String string1, string2; |
| for (int i = 0; i < this.numKeys; i++) { |
| val1 = heap[fs1 + this.keyOffset[i]]; |
| val2 = heap[fs2 + this.keyOffset[i]]; |
| switch (this.keyType[i]) { |
| case STRING_CODE: { |
| // System.out.println("Comparing string codes " + val1 + " and " |
| // + val2); |
| // System.out.println( |
| // "Strings: " |
| // + lowLevelCAS.getStringForCode(val1) |
| // + ", " |
| // + lowLevelCAS.getStringForCode(val2)); |
| // if (lowLevelCAS.getStringForCode(val1) == null) { |
| // System.out.println("Value for " + val1 + " is <null>."); |
| // } |
| // if (lowLevelCAS.getStringForCode(val2) == null) { |
| // System.out.println("Value for " + val2 + " is <null>."); |
| // } |
| string1 = this.lowLevelCAS.getStringForCode(val1); |
| string2 = this.lowLevelCAS.getStringForCode(val2); |
| if (string1 == null) { |
| if (string2 == null) { |
| compVal = 0; |
| } else { |
| compVal = -1; |
| } |
| } else { |
| if (string2 == null) { |
| compVal = 1; |
| } else { |
| compVal = string1.compareTo(string2); |
| } |
| } |
| |
| if (compVal != 0) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return compVal; |
| } |
| return -compVal; |
| } |
| break; |
| } |
| case FLOAT_CODE: { |
| float1 = CASImpl.int2float(val1); |
| float2 = CASImpl.int2float(val2); |
| if (float1 < float2) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return -1; |
| } |
| return 1; |
| } else if (float1 > float2) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return 1; |
| } |
| return -1; |
| } |
| break; |
| } |
| case TYPE_ORDER_CODE: { |
| if (val1 == val2) { |
| break; |
| } |
| if (this.typeOrder[i].lessThan(val1, val2)) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return -1; |
| } |
| return 1; |
| } |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return 1; |
| } |
| return -1; |
| } |
| case LONG_CODE: { |
| long long1 = this.lowLevelCAS.getLongHeap().getHeapValue(val1); |
| long long2 = this.lowLevelCAS.getLongHeap().getHeapValue(val2); |
| if (long1 < long2) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return -1; |
| } |
| return 1; |
| } else if (long1 > long2) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return 1; |
| } |
| return -1; |
| } |
| break; |
| } |
| case DOUBLE_CODE: { |
| double double1 = Double.longBitsToDouble(this.lowLevelCAS.getLongHeap().getHeapValue(val1)); |
| double double2 = Double.longBitsToDouble(this.lowLevelCAS.getLongHeap().getHeapValue(val2)); |
| if (double1 < double2) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return -1; |
| } |
| return 1; |
| } else if (double1 > double2) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return 1; |
| } |
| return -1; |
| } |
| break; |
| } |
| |
| default: { // Compare the int values directly. |
| // boolean compare done here as well. |
| // byte compare done here as well. |
| // short compare done here as well. |
| if (val1 < val2) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return -1; |
| } |
| return 1; |
| } else if (val1 > val2) { |
| if (this.keyComp[i] == FSIndexComparator.STANDARD_COMPARE) { |
| return 1; |
| } |
| return -1; |
| |
| } |
| break; |
| } |
| } |
| } |
| // FSs are identical as far as this comparator goes. |
| return 0; |
| } |
| |
| // Eclipse says this method is never called by uimaj-core methods 9-2009 |
| public final boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| return this.comparator.equals(o); |
| // if (o instanceof FSIndexComparator) { |
| // return this.comparator.equals(o); |
| // } else if (o instanceof FSVectorIndex) { |
| // return this.comparator.equals(((FSLeafIndexImpl)o).comparator); |
| // } else { |
| // return false; |
| // } |
| } |
| |
| public int hashCode() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * @see org.apache.uima.cas.FSIndex#compare(FeatureStructure, FeatureStructure) |
| */ |
| public int compare(FeatureStructure fs1, FeatureStructure fs2) { |
| return compare(((FeatureStructureImpl) fs1).getAddress(), ((FeatureStructureImpl) fs2) |
| .getAddress()); |
| } |
| |
| /** |
| * @see org.apache.uima.cas.FSIndex#getType() |
| */ |
| public Type getType() { |
| return this.type; |
| } |
| |
| protected abstract IntPointerIterator refIterator(); |
| |
| public IntPointerIterator getIntIterator() { |
| return refIterator(); |
| } |
| |
| protected abstract IntPointerIterator refIterator(int fsCode); |
| |
| // these next two are never called (maybe) |
| // because the object this method is called on is |
| // never this object, but instead the FSIndexRepositoryImpl.IndexImpl object |
| // |
| // It would be good to refactor this so that this confusion is eliminated, |
| // perhaps by not having this class implement FSIndex |
| |
| /** |
| * @see org.apache.uima.cas.FSIndex#iterator() |
| */ |
| public FSIterator<T> iterator() { |
| return new FSIteratorWrapper<T>(refIterator(), this.lowLevelCAS); |
| } |
| |
| /** |
| * @see org.apache.uima.cas.FSIndex#iterator(FeatureStructure) |
| */ |
| public FSIterator<T> iterator(FeatureStructure fs) { |
| return new FSIteratorWrapper<T>(refIterator(((FeatureStructureImpl) fs).getAddress()), |
| this.lowLevelCAS); |
| } |
| |
| /** |
| * Method deleteFS. |
| * |
| * @param fs |
| */ |
| public abstract void deleteFS(FeatureStructure fs); |
| |
| public LowLevelIterator ll_iterator(boolean ambiguous) { |
| if (ambiguous) { |
| return this.ll_iterator(); |
| } |
| |
| return null; |
| } |
| |
| public LowLevelIterator ll_rootIterator() { |
| return this.ll_iterator(); |
| } |
| |
| } |