001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2011-2011 Pentaho and others
008// All Rights Reserved.
009*/
010package mondrian.util;
011
012import java.util.List;
013import java.util.Map;
014
015/**
016 * Variation of Luc's SpatialValueTree.
017 *
018 * <p>Notes:
019 *
020 * <p>1. For clarity, I got rid of the of template args. We might add back
021 * say '&lt;Q extends SpatialRegionRequest,
022 * R extends SpatialRegion,
023 * D extends SpatialDimension&gt;' if it saves a lot of casting in client code.
024 *
025 * <p>2. It might be useful to introduce an interface SpatialDimensionality
026 * that represents a set of dimensions. It will certainly be useful for the
027 * implementation; however, it is not clear that it is necessary in the API.
028 *
029 * <p>3. In mondrian a 'region value' might be a struct that contains
030 *    a weak reference to a segment in cache, and a reference to a segment
031 *    in an external cache. Either of the references can be null.
032 *
033 * <p>4. In a SpatialRegionRequest, it might be useful to coalesce a list
034 *    of values into a range (e.g. {2009, 2010, 2011} would be [2009, 2011]).
035 *
036 * <p>Mondrian requests currently have raw values, but converting those raw
037 *    values into ranges might allow the tree to be more efficient.
038 *
039 * <p>Ranges could be recognized if we know all allowable values. For example,
040 *    if we know the set of states, we can say that {"CA", "CO", "CT"
041 *    "DE"} is equivalent to ["CA", "DE"].
042 *    A range could be recognized if the data type is discreet. For example,
043 *    {2009, 2010, 2011} is equivalent to [2009, 2011] because year is an int.
044 *
045 * <p>5. For performance, and atomicity of operations,
046 * some of the methods might
047 * contain a callback (e.g. {@link #rollup(java.util.Map)} could take a
048 * functor that is applied when a rollup is found. But we can validate the
049 * design introducing functors just yet.
050 *
051 * @author jhyde
052  */
053public interface SpatialValueTree2
054{
055    /**
056     * Returns a list of all the dimensions present in this tree.
057     *
058     * @return A list of dimensions.
059     */
060    List<SpatialDimension> getDimensions();
061
062    /**
063     * Stores a region in this tree.
064     *
065     * <p>REVIEW: What is the behavior if there is another region with an equal
066     * {@link SpatialRegionRequest}?
067     *
068     * @param region Region
069     */
070    void add(SpatialRegion region);
071
072    /**
073     * Removes a region from the tree.
074     *
075     * @param region The region key of the values to clear.
076     */
077    void clear(SpatialRegion region);
078
079    /**
080     * Looks up all the values registered in nodes intersecting
081     * with the provided region key.
082     *
083     * <p>REVIEW: Does it have to PRECISELY fulfill the request, or can it
084     * be a superset? Does the return value's {@link SpatialRegion#getRequest()}
085     * method return the {@code regionRequest} parameter? (That would be
086     * expensive to implement.) Do we even need this method, or is
087     *
088     * @param regionRequest The region key inside of which to search for
089     * value nodes.
090     *
091     * @return Region fulfilling the request, or null
092     */
093    SpatialRegion get(SpatialRegionRequest regionRequest);
094
095    /**
096     * Returns a region containing a given cell.
097     *
098     * <p>If there are multiple regions that contain the cell, returns just
099     * one of them. If there are no regions, returns null.
100     *
101     * @param coordinates Coordinates of cell - a value for each constraining
102     *    dimension
103     * @return a region that contains the cell, or null if there is no such
104     */
105    SpatialRegion getRegionContaining(
106        Map<SpatialDimension, Object> coordinates);
107
108    /**
109     * Returns a collection of regions that can be combined to compute a given
110     * cell.
111     *
112     * <p>The regions are not necessarily disjoint. Nor are they necessarily
113     * of the same dimensionality.
114     *
115     * <p>If multiple rollups are possible, gives the set of regions with the
116     * smallest number of cells. (It is cheaper to roll up from regions that are
117     * already highly aggregated.)
118     *
119     * <p>If no rollup is possible, returns null.
120     *
121     * @param dimensions Coordinates of cell
122     * @return List of spatial regions to roll up; or null
123     */
124    List<SpatialRegion> rollup(
125        Map<SpatialDimension, Object> dimensions);
126
127    public interface SpatialDimension
128    {
129        /**
130         * Ordinal of dimension. Dimension ordinals are unique and contiguous
131         * within a tree.
132         *
133         * @return ordinal of dimension
134         */
135        int ordinal();
136
137        /**
138         * Declares that a particular dimension has a finite set of values. With
139         * this information, an implementation may be able to perform rollups
140         * that it would not otherwise.
141         *
142         * <p>For example, if the user asks for cell (year=2010, measure=sales)
143         * and the tree has regions (year=2010, gender=M, measure=sales) and
144         * (year=2010, gender=F, measure=sales) then the tree can compute the
145         * cell only if it knows that the only values of gender are {M, F}.
146         *
147         * <p>Returns null if the set of values is unbounded, not known, or
148         * too large to be any use in optimizing.
149         *
150         * <p>The values are distinct and sorted (per
151         * {@link Comparable}, and all not null. If you wish to represent a
152         * null value, use a dummy object.
153         *
154         * <p>The client must not modify the array.
155         *
156         * @return set of values that this dimension may have
157         */
158        Object[] getValues();
159    }
160
161    /**
162     * A request for a region. The request has a number of dimensions, a
163     * subset of the dimensions of the tree, and for each dimension it either
164     * requests a list of values or requests all values.
165     */
166    public interface SpatialRegionRequest
167    {
168        /**
169         * Provides a list of the dimensions included in this
170         * region.
171         *
172         * @return Dimensions of this region
173         */
174        List<SpatialDimension> getDimensions();
175
176        /**
177         * Provides an array of objects describing this region's
178         * bounds within the specified dimension's axis.
179         *
180         * <p>The values are unique and are sorted. The client must not modify
181         * the array.
182         *
183         * <p>A null array means wildcard. The caller wanted all possible
184         * values of this dimension.
185         *
186         * @param dimension Dimension
187         * @return An array of the bounds touched by this region.
188         */
189        Object[] getValues(SpatialDimension dimension);
190
191        /**
192         * Returns whether a request might contain a particular cell.
193         *
194         * @param coordinates Value for each dimension of cell's coordinates.
195         *
196         * @return Whether cell is within the bounds of this request
197         */
198        boolean mightContainCell(Map<SpatialDimension, Object> coordinates);
199    }
200
201    public interface SpatialRegion
202    {
203        /**
204         * Returns the specification of this region.
205         *
206         * @return Region request
207         */
208        SpatialRegionRequest getRequest();
209
210        /**
211         * Returns the value of a cell.
212         *
213         * <p>Assumes that this region body is valid for the request. (That is,
214         * {@link SpatialRegionRequest#mightContainCell(java.util.Map)} would
215         * return true.) For example, suppose that the region request was
216         *
217         * <blockquote>(gender=M, year=any, measure=sales)</blockquote>
218         *
219         * this region body is
220         *
221         * <blockquote>(gender=M, year={2009, 2010}, measure=sales)</blockquote>
222         *
223         * and the cell coordinates are
224         *
225         * <blockquote>(gender=F, year=2009, measure=sales)</blockquote>
226         *
227         * <p>Because the coordinate 'gender=F' falls outside the region
228         * request, behavior is unspecified. The implementation might
229         * return null, throw an error, or return a value for the cell
230         * (gender=M, year=2009, measure=sales); any of these behaviors would
231         * be valid.
232         *
233         * @param coordinates Value for each dimension
234         *
235         * @return cell value
236         */
237        Object getCellValue(Map<SpatialDimension, Object> coordinates);
238
239        /**
240         * Version of {@link #getCellValue(java.util.Map)} optimized for
241         * {@code int} values.
242         *
243         * <p>If value is null, writes 'true' into wasNull[0] and returns 0.
244         * Otherwise, does not modify wasNull.
245         *
246         * @param coordinates Value of each dimension
247         * @param wasNull 1-element array to be informed if value was null
248         * @return Value, or 0 if value is null
249         */
250        int getCellValueInt(
251            Map<SpatialDimension, Object> coordinates,
252            boolean[] wasNull);
253
254        /**
255         * Version of {@link #getCellValue(java.util.Map)} optimized for
256         * {@code double} values.
257         *
258         * <p>If value is null, writes 'true' into wasNull[0] and returns 0.
259         * Otherwise, does not modify wasNull.
260         *
261         * @param coordinates Value of each dimension
262         * @param wasNull 1-element array to be informed if value was null
263         * @return Value, or 0 if value is null
264         */
265        double getCellValueDouble(
266            Map<SpatialDimension, Object> coordinates,
267            boolean[] wasNull);
268    }
269}
270
271// End SpatialValueTree2.java