View Javadoc
1   package com.github.davidmoten.rtree.fbs;
2   
3   import static com.github.davidmoten.rtree.fbs.FlatBuffersHelper.createBox;
4   import static com.github.davidmoten.rtree.fbs.FlatBuffersHelper.parseObject;
5   import static com.github.davidmoten.rtree.fbs.FlatBuffersHelper.toGeometry;
6   
7   import java.util.ArrayList;
8   import java.util.List;
9   
10  import com.github.davidmoten.guavamini.Preconditions;
11  import com.github.davidmoten.rtree.Context;
12  import com.github.davidmoten.rtree.Entries;
13  import com.github.davidmoten.rtree.Entry;
14  import com.github.davidmoten.rtree.Node;
15  import com.github.davidmoten.rtree.NonLeaf;
16  import com.github.davidmoten.rtree.fbs.generated.BoundsType_;
17  import com.github.davidmoten.rtree.fbs.generated.Bounds_;
18  import com.github.davidmoten.rtree.fbs.generated.BoxDouble_;
19  import com.github.davidmoten.rtree.fbs.generated.BoxFloat_;
20  import com.github.davidmoten.rtree.fbs.generated.Entry_;
21  import com.github.davidmoten.rtree.fbs.generated.Geometry_;
22  import com.github.davidmoten.rtree.fbs.generated.Node_;
23  import com.github.davidmoten.rtree.geometry.Geometries;
24  import com.github.davidmoten.rtree.geometry.Geometry;
25  import com.github.davidmoten.rtree.geometry.Rectangle;
26  import com.github.davidmoten.rtree.internal.NodeAndEntries;
27  import com.github.davidmoten.rtree.internal.NonLeafHelper;
28  
29  import rx.Subscriber;
30  import rx.functions.Func1;
31  
32  final class NonLeafFlatBuffers<T, S extends Geometry> implements NonLeaf<T, S> {
33  
34      private final Node_ node;
35      private final Context<T, S> context;
36      private final Func1<byte[], ? extends T> deserializer;
37  
38      NonLeafFlatBuffers(Node_ node, Context<T, S> context, Func1<byte[], ? extends T> deserializer) {
39          Preconditions.checkNotNull(node);
40          // remove precondition because reduces performance
41          // Preconditions.checkArgument(node.childrenLength() > 0);
42          this.node = node;
43          this.context = context;
44          this.deserializer = deserializer;
45      }
46  
47      @Override
48      public List<Node<T, S>> add(Entry<? extends T, ? extends S> entry) {
49          return NonLeafHelper.add(entry, this);
50      }
51  
52      @Override
53      public NodeAndEntries<T, S> delete(Entry<? extends T, ? extends S> entry, boolean all) {
54          return NonLeafHelper.delete(entry, all, this);
55      }
56  
57      @Override
58      public void searchWithoutBackpressure(Func1<? super Geometry, Boolean> criterion,
59              Subscriber<? super Entry<T, S>> subscriber) {
60          // pass through entry and geometry and box instances to be reused for
61          // flatbuffers extraction this reduces allocation/gc costs (but of
62          // course introduces some mutable ugliness into the codebase)
63          searchWithoutBackpressure(node, criterion, subscriber, deserializer, new Entry_(),
64                  new Geometry_(), new Bounds_());
65      }
66  
67      @SuppressWarnings("unchecked")
68      private static <T, S extends Geometry> void searchWithoutBackpressure(Node_ node,
69              Func1<? super Geometry, Boolean> criterion, Subscriber<? super Entry<T, S>> subscriber,
70              Func1<byte[], ? extends T> deserializer, Entry_ entry, Geometry_ geometry,
71              Bounds_ bounds) {
72          {
73              // write bounds from node to bounds variable
74              node.mbb(bounds);
75              final Rectangle rect;
76              if (bounds.type() == BoundsType_.BoundsDouble) {
77                  BoxDouble_ b = bounds.boxDouble();
78                  rect = Geometries.rectangle(b.minX(), b.minY(), b.maxX(), b.maxY());
79              } else {
80                  BoxFloat_ b = bounds.boxFloat();
81                  rect = Geometries.rectangle(b.minX(), b.minY(), b.maxX(), b.maxY());
82              }
83              if (!criterion.call(rect)) {
84                  return;
85              }
86          }
87          int numChildren = node.childrenLength();
88          // reduce allocations by reusing objects
89          Node_ child = new Node_();
90          if (numChildren > 0) {
91              for (int i = 0; i < numChildren; i++) {
92                  if (subscriber.isUnsubscribed())
93                      return;
94                  node.children(child, i);
95                  searchWithoutBackpressure(child, criterion, subscriber, deserializer, entry,
96                          geometry, bounds);
97              }
98          } else {
99              int numEntries = node.entriesLength();
100             // reduce allocations by reusing objects
101             // check all entries
102             for (int i = 0; i < numEntries; i++) {
103                 if (subscriber.isUnsubscribed())
104                     return;
105                 // set entry
106                 node.entries(entry, i);
107                 // set geometry
108                 entry.geometry(geometry);
109                 final Geometry g = toGeometry(geometry);
110                 if (criterion.call(g)) {
111                     T t = parseObject(deserializer, entry);
112                     Entry<T, S> ent = Entries.entry(t, (S) g);
113                     subscriber.onNext(ent);
114                 }
115             }
116         }
117 
118     }
119 
120     private List<Node<T, S>> createChildren() {
121 
122         // reduce allocations by resusing objects
123         int numChildren = node.childrenLength();
124         List<Node<T, S>> children = new ArrayList<Node<T, S>>(numChildren);
125         for (int i = 0; i < numChildren; i++) {
126             Node_ child = node.children(i);
127             if (child.childrenLength() > 0) {
128                 children.add(new NonLeafFlatBuffers<T, S>(child, context, deserializer));
129             } else {
130                 children.add(new LeafFlatBuffers<T, S>(child, context, deserializer));
131             }
132         }
133         return children;
134     }
135 
136     @Override
137     public int count() {
138         return node.childrenLength();
139     }
140 
141     @Override
142     public Context<T, S> context() {
143         return context;
144     }
145 
146     @Override
147     public Geometry geometry() {
148         return FlatBuffersHelper.createBox(node.mbb());
149     }
150 
151     @Override
152     public Node<T, S> child(int i) {
153         Node_ child = node.children(i);
154         if (child.childrenLength() > 0)
155             return new NonLeafFlatBuffers<T, S>(child, context, deserializer);
156         else
157             return new LeafFlatBuffers<T, S>(child, context, deserializer);
158     }
159 
160     @Override
161     public List<Node<T, S>> children() {
162         return createChildren();
163     }
164 
165     @Override
166     public String toString() {
167         return "Node [" + (node.childrenLength() > 0 ? "NonLeaf" : "Leaf") + ","
168                 + createBox(node.mbb()).toString() + "]";
169     }
170 
171 }