private static boolean isHashable(IndexableConstraint indexableConstraint) {
   return indexableConstraint.isIndexable(NodeTypeEnums.AlphaNode)
       && indexableConstraint.getField() != null
       && indexableConstraint.getFieldExtractor().getValueType() != ValueType.OBJECT_TYPE
       &&
       // our current implementation does not support hashing of deeply nested properties
       indexableConstraint.getFieldExtractor().getIndex() >= 0;
 }
  public ObjectSinkPropagator removeObjectSink(final ObjectSink sink) {
    this.sinks = null; // dirty it, so it'll rebuild on next get
    if (sink.getType() == NodeTypeEnums.AlphaNode) {
      final AlphaNode alphaNode = (AlphaNode) sink;
      final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();

      if (fieldConstraint instanceof IndexableConstraint) {
        final IndexableConstraint indexableConstraint = (IndexableConstraint) fieldConstraint;
        final FieldValue value = indexableConstraint.getField();

        if (isHashable(indexableConstraint)) {
          final InternalReadAccessor fieldAccessor = indexableConstraint.getFieldExtractor();
          final int index = fieldAccessor.getIndex();
          final FieldIndex fieldIndex = unregisterFieldIndex(index);

          if (fieldIndex.isHashed()) {
            HashKey hashKey = new HashKey(index, value, fieldAccessor);
            this.hashedSinkMap.remove(hashKey);
            if (fieldIndex.getCount() <= this.alphaNodeHashingThreshold - 1) {
              // we have less than three so unhash
              unHashSinks(fieldIndex);
            }
          } else {
            this.hashableSinks.remove(alphaNode);
          }

          if (this.hashableSinks != null && this.hashableSinks.isEmpty()) {
            this.hashableSinks = null;
          }

          return size() == 1 ? new SingleObjectSinkAdapter(getSinks()[0]) : this;
        }
      }
    }

    this.otherSinks.remove((ObjectSinkNode) sink);

    if (this.otherSinks.isEmpty()) {
      this.otherSinks = null;
    }

    return size() == 1 ? new SingleObjectSinkAdapter(getSinks()[0]) : this;
  }
  void unHashSinks(final FieldIndex fieldIndex) {
    final int index = fieldIndex.getIndex();
    // this is the list of sinks that need to be removed from the hashedSinkMap
    final List<HashKey> unhashedSinks = new ArrayList<HashKey>();

    final Iterator iter = this.hashedSinkMap.newIterator();
    ObjectHashMap.ObjectEntry entry = (ObjectHashMap.ObjectEntry) iter.next();

    while (entry != null) {
      final AlphaNode alphaNode = (AlphaNode) entry.getValue();
      final IndexableConstraint indexableConstraint =
          (IndexableConstraint) alphaNode.getConstraint();

      // only alpha nodes that have an Operator.EQUAL are in sinks, so only check if it is
      // the right field index
      if (index == indexableConstraint.getFieldExtractor().getIndex()) {
        final FieldValue value = indexableConstraint.getField();
        if (this.hashableSinks == null) {
          this.hashableSinks = new ObjectSinkNodeList();
        }
        this.hashableSinks.add(alphaNode);

        unhashedSinks.add(new HashKey(index, value, fieldIndex.getFieldExtractor()));
      }

      entry = (ObjectHashMap.ObjectEntry) iter.next();
    }

    for (HashKey hashKey : unhashedSinks) {
      this.hashedSinkMap.remove(hashKey);
    }

    if (this.hashedSinkMap.isEmpty()) {
      this.hashedSinkMap = null;
    }

    fieldIndex.setHashed(false);
  }
  void hashSinks(final FieldIndex fieldIndex) {
    if (this.hashedSinkMap == null) {
      this.hashedSinkMap = new ObjectHashMap();
    }

    final int index = fieldIndex.getIndex();
    final InternalReadAccessor fieldReader = fieldIndex.getFieldExtractor();

    ObjectSinkNode currentSink = this.hashableSinks.getFirst();

    while (currentSink != null) {
      final AlphaNode alphaNode = (AlphaNode) currentSink;
      final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint();
      final IndexableConstraint indexableConstraint = (IndexableConstraint) fieldConstraint;

      // position to the next sink now because alphaNode may be removed if the index is equal. If we
      // were to do this
      // afterwards, currentSink.nextNode would be null
      currentSink = currentSink.getNextObjectSinkNode();

      // only alpha nodes that have an Operator.EQUAL are in hashableSinks, so only check if it is
      // the right field index
      if (index == indexableConstraint.getFieldExtractor().getIndex()) {
        final FieldValue value = indexableConstraint.getField();
        this.hashedSinkMap.put(new HashKey(index, value, fieldReader), alphaNode);

        // remove the alpha from the possible candidates of hashable sinks since it is now hashed
        hashableSinks.remove(alphaNode);
      }
    }

    if (this.hashableSinks.isEmpty()) {
      this.hashableSinks = null;
    }

    fieldIndex.setHashed(true);
  }