/* * Copyright (c) 2010 SimpleServer authors (see CONTRIBUTORS) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package simpleserver.config.xml; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; public class SegmentTree<E> { private Node root; private List<HyperSegment> segments = new LinkedList<HyperSegment>(); private NodeCache cache; private boolean built = false; private int dimensions; public SegmentTree(int dimensions) { this.dimensions = dimensions; } public void add(int[] start, int[] end, E object) { segments.add(new HyperSegment(start, end, object)); if (built) { build(); } } public void remove(E object) { for (HyperSegment segment : segments) { if (segment.object == object) { segments.remove(segment); if (built) { build(); } return; } } } public void build() { cache = new NodeCache(); root = build(segments, 0); cache = null; built = true; } public List<E> get(int... point) { return root.find(point); } public Set<E> overlaps(int[] start, int[] end) { return overlaps(new HyperSegment(start, end, null)); } private Set<E> overlaps(HyperSegment segment) { return root.overlaps(segment); } private Node build(List<HyperSegment> segments, int dimension) { if (segments.size() == 1 && cache.contains(segments.get(0), dimension)) { return cache.get(segments.get(0), dimension); } // find all end points TreeMap<Integer, Boolean> points = new TreeMap<Integer, Boolean>(); for (HyperSegment hyperSegment : segments) { Segment segment = hyperSegment.segments[dimension]; points.put(segment.start, false); points.put(segment.end, segment.start == segment.end); } if (points.isEmpty()) { return new Node(0, 0); } // create leaves List<Node> leaves = new LinkedList<Node>(); while (points.size() > 1) { int point = points.firstKey(); if (points.remove(point)) { leaves.add(new Node(point, point)); } leaves.add(new Node(point, points.firstKey())); } Entry<Integer, Boolean> lastPoint = points.firstEntry(); if (lastPoint.getValue()) { leaves.add(new Node(lastPoint.getKey(), lastPoint.getKey())); } // build tree Node root = createTree(leaves, 0, leaves.size() - 1); // insert segments for (HyperSegment segment : segments) { root.insertSegment(segment.segments[dimension]); } // build higher dimensions if (++dimension < dimensions) { root.buildDimension(dimension); } if (segments.size() == 1) { cache.put(root, segments.get(0), dimension - 1); } return root; } private Node createTree(List<Node> leaves, int start, int end) { if (start == end) { return leaves.get(start); } Node node = new Node(leaves.get(start).start, leaves.get(end).end); int middle = (end - start) / 2 + start; node.left = createTree(leaves, start, middle); node.right = createTree(leaves, middle + 1, end); return node; } private class Node { int start; int end; Node left; Node right; Node nextDimension; List<HyperSegment> segments = new ArrayList<HyperSegment>(); Node(int start, int end) { this.start = start; this.end = end; } void buildDimension(int dimension) { if (!segments.isEmpty()) { nextDimension = build(segments, dimension); } if (left != null) { left.buildDimension(dimension); } if (right != null) { right.buildDimension(dimension); } } List<E> find(int[] point) { List<E> list = new LinkedList<E>(); find(point, list, 0); return list; } void find(int[] point, List<E> list, int dimension) { if (point[dimension] > end || point[dimension] < start) { return; } else { if (dimension == dimensions - 1) { for (HyperSegment segment : segments) { list.add(segment.object); } } else if (nextDimension != null) { nextDimension.find(point, list, dimension + 1); } if (left != null) { left.find(point, list, dimension); } if (right != null) { right.find(point, list, dimension); } } } public Set<E> overlaps(HyperSegment segment) { Set<E> set = new HashSet<E>(); overlaps(segment, set, 0); return set; } private void overlaps(HyperSegment segment, Set<E> set, int dimension) { if (segment.segments[dimension].start > end || segment.segments[dimension].end < start) { return; } else { if (dimension == dimensions - 1) { for (HyperSegment s : segments) { set.add(s.object); } } else if (nextDimension != null) { nextDimension.overlaps(segment, set, dimension + 1); } if (left != null) { left.overlaps(segment, set, dimension); } if (right != null) { right.overlaps(segment, set, dimension); } } } public void insertSegment(Segment segment) { if (segment.start <= start && segment.end >= end) { segments.add(segment.parent); } else { if (left != null) { left.insertSegment(segment); } if (right != null) { right.insertSegment(segment); } } } } private class HyperSegment { E object; Segment[] segments; @SuppressWarnings("unchecked") HyperSegment(int[] start, int[] end, E object) { this.object = object; segments = (Segment[]) Array.newInstance(Segment.class, dimensions); for (int d = 0; d < dimensions; d++) { if (start[d] <= end[d]) { segments[d] = new Segment(start[d], end[d], this); } else { segments[d] = new Segment(end[d], start[d], this); } } } } private class Segment { HyperSegment parent; int start; int end; Segment(int start, int end, HyperSegment parent) { this.start = start; this.end = end; this.parent = parent; } } private class NodeCache { private HashMap<Segment, Node> cache = new HashMap<Segment, Node>(); boolean contains(Segment segment) { return cache.containsKey(segment); } boolean contains(HyperSegment segment, int dimension) { return contains(segment.segments[dimension]); } void put(Node node, Segment segment) { cache.put(segment, node); } public void put(Node node, HyperSegment segment, int dimension) { put(node, segment.segments[dimension]); } Node get(Segment segment) { return cache.get(segment); } Node get(HyperSegment segment, int dimension) { return get(segment.segments[dimension]); } } }