/* * 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. */ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Runtime.CompilerServices; using Spatial4n.Core.Shapes; namespace Lucene.Net.Spatial.Prefix.Tree { public abstract class Node : IComparable { public static byte LEAF_BYTE = (byte)'+';//NOTE: must sort before letters & numbers // /* //Holds a byte[] and/or String representation of the cell. Both are lazy constructed from the other. //Neither contains the trailing leaf byte. // */ //private byte[] bytes; //private int b_off; //private int b_len; private String token;//this is the only part of equality protected SpatialRelation shapeRel;//set in getSubCells(filter), and via setLeaf(). protected readonly SpatialPrefixTree spatialPrefixTree; protected Node(SpatialPrefixTree spatialPrefixTree, String token) { this.spatialPrefixTree = spatialPrefixTree; this.token = token; if (token.Length > 0 && token[token.Length - 1] == (char)LEAF_BYTE) { this.token = token.Substring(0, token.Length - 1); SetLeaf(); } if (GetLevel() == 0) GetShape();//ensure any lazy instantiation completes to make this threadsafe } public virtual void Reset(string newToken) { Debug.Assert(GetLevel() != 0); this.token = newToken; shapeRel = SpatialRelation.NULL_VALUE; b_fixLeaf(); } private void b_fixLeaf() { if (GetLevel() == spatialPrefixTree.GetMaxLevels()) { SetLeaf(); } } public SpatialRelation GetShapeRel() { return shapeRel; } public bool IsLeaf() { return shapeRel == SpatialRelation.WITHIN; } public void SetLeaf() { Debug.Assert(GetLevel() != 0); shapeRel = SpatialRelation.WITHIN; } /* * Note: doesn't contain a trailing leaf byte. */ public String GetTokenString() { if (token == null) throw new InvalidOperationException("Somehow we got a null token"); return token; } ///// ///// Note: doesn't contain a trailing leaf byte. ///// ///// //public byte[] GetTokenBytes() //{ // if (bytes != null) // { // if (b_off != 0 || b_len != bytes.Length) // { // throw new IllegalStateException("Not supported if byte[] needs to be recreated."); // } // } // else // { // bytes = token.GetBytes(SpatialPrefixTree.UTF8); // b_off = 0; // b_len = bytes.Length; // } // return bytes; //} public int GetLevel() { return token.Length; //return token != null ? token.Length : b_len; } //TODO add getParent() and update some algorithms to use this? //public Cell getParent(); /* * Like {@link #getSubCells()} but with the results filtered by a shape. If that shape is a {@link com.spatial4j.core.shape.Point} then it * must call {@link #getSubCell(com.spatial4j.core.shape.Point)}; * Precondition: Never called when getLevel() == maxLevel. * * @param shapeFilter an optional filter for the returned cells. * @return A set of cells (no dups), sorted. Not Modifiable. */ public IList GetSubCells(Shape shapeFilter) { //Note: Higher-performing subclasses might override to consider the shape filter to generate fewer cells. var point = shapeFilter as Point; if (point != null) { #if !NET35 return new ReadOnlyCollectionBuilder(new[] {GetSubCell(point)}).ToReadOnlyCollection(); #else return new List(new[]{GetSubCell(point)}).AsReadOnly(); #endif } var cells = GetSubCells(); if (shapeFilter == null) { return cells; } var copy = new List(cells.Count);//copy since cells contractually isn't modifiable foreach (var cell in cells) { SpatialRelation rel = cell.GetShape().Relate(shapeFilter); if (rel == SpatialRelation.DISJOINT) continue; cell.shapeRel = rel; copy.Add(cell); } cells = copy; return cells; } /* * Performant implementations are expected to implement this efficiently by considering the current * cell's boundary. * Precondition: Never called when getLevel() == maxLevel. * Precondition: this.getShape().relate(p) != DISJOINT. */ public abstract Node GetSubCell(Point p); //TODO Cell getSubCell(byte b) /* * Gets the cells at the next grid cell level that cover this cell. * Precondition: Never called when getLevel() == maxLevel. * * @return A set of cells (no dups), sorted. Not Modifiable. */ public abstract IList GetSubCells(); /* * {@link #getSubCells()}.size() -- usually a constant. Should be >=2 */ public abstract int GetSubCellsSize(); public abstract Shape GetShape(); public virtual Point GetCenter() { return GetShape().GetCenter(); } public int CompareTo(Node o) { return System.String.CompareOrdinal(GetTokenString(), o.GetTokenString()); } public override bool Equals(object obj) { return !(obj == null || !(obj is Node)) && GetTokenString().Equals(((Node) obj).GetTokenString()); } public override int GetHashCode() { return GetTokenString().GetHashCode(); } public override string ToString() { return GetTokenString() + (IsLeaf() ? new string(new[] {(char) LEAF_BYTE}) : string.Empty); } } }