/* * 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.lucene.spatial.prefix.tree; import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Shape; /** * Navigates a {@link org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree} from a given cell (typically the world * cell) down to a maximum number of configured levels, filtered by a given shape. Intermediate non-leaf cells are * returned. It supports {@link #remove()} for skipping traversal of subcells of the current cell. * * @lucene.internal */ class TreeCellIterator extends CellIterator { //This class uses a stack approach, which is more efficient than creating linked nodes. And it might more easily // pave the way for re-using Cell & CellIterator at a given level in the future. private final Shape shapeFilter;//possibly null private final CellIterator[] iterStack;//starts at level 1 private int stackIdx;//-1 when done private boolean descend; public TreeCellIterator(Shape shapeFilter, int detailLevel, Cell parentCell) { this.shapeFilter = shapeFilter; assert parentCell.getLevel() == 0; iterStack = new CellIterator[detailLevel]; iterStack[0] = parentCell.getNextLevelCells(shapeFilter); stackIdx = 0;//always points to an iter (non-null) //note: not obvious but needed to visit the first cell before trying to descend descend = false; } @Override public boolean hasNext() { if (nextCell != null) return true; while (true) { if (stackIdx == -1)//the only condition in which we return false return false; //If we can descend... if (descend && !(stackIdx == iterStack.length - 1 || iterStack[stackIdx].thisCell().isLeaf())) { CellIterator nextIter = iterStack[stackIdx].thisCell().getNextLevelCells(shapeFilter); //push stack iterStack[++stackIdx] = nextIter; } //Get sibling... if (iterStack[stackIdx].hasNext()) { nextCell = iterStack[stackIdx].next(); //at detailLevel if (stackIdx == iterStack.length - 1 && !(shapeFilter instanceof Point)) //point check is a kludge nextCell.setLeaf();//because at bottom break; } //Couldn't get next; go up... //pop stack iterStack[stackIdx--] = null; descend = false;//so that we don't re-descend where we just were } assert nextCell != null; descend = true;//reset return true; } @Override public void remove() { assert thisCell() != null && nextCell == null; descend = false; } //TODO implement a smart nextFrom() that looks at the parent's bytes first }