| /* |
| * 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<Node> |
| { |
| 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; |
| } |
| |
| ///// <summary> |
| ///// Note: doesn't contain a trailing leaf byte. |
| ///// </summary> |
| ///// <returns></returns> |
| //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<Node> 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<Node>(new[] { GetSubCell(point) }).ToReadOnlyCollection(); |
| #else |
| return new List<Node>(new[]{GetSubCell(point)}).AsReadOnly(); |
| #endif |
| |
| } |
| |
| var cells = GetSubCells(); |
| if (shapeFilter == null) |
| { |
| return cells; |
| } |
| var copy = new List<Node>(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<Node> 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); |
| } |
| } |
| } |