001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015 ForgeRock AS.
015 */
016package org.forgerock.util.query;
017
018import static org.forgerock.util.query.QueryFilterOperators.*;
019
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Map;
023import java.util.HashMap;
024
025/**
026 * A {@link QueryFilterVisitor} that produces a Map representation of the filter tree.
027 * <p>
028 * The produced map is constructed according to the following representation:
029 *
030 * <dl>
031 *     <dt>an and expression</dt>
032 *     <dd><pre>{ "operator" : "and", "subfilters" : [ the subfilters ] }</pre></dd>
033 *     <dt>an or expression</dt>
034 *     <dd><pre>{ "operator" : "or", "subfilters" : [ the subfilters ] }</pre></dd>
035 *     <dt>a not expression</dt>
036 *     <dd><pre>{ "operator" : "not", "subfilter" : the subfilter }</pre></dd>
037 *     <dt>a presence expression</dt>
038 *     <dd><pre>{ "operator" : "pr", "field" : "/afield"}</pre></dd>
039 *     <dt>an equals expression</dt>
040 *     <dd><pre>{ "operator" : "eq", "field" : "/afield", "value" : "something"}</pre></dd>
041 *     <dt>a contains expression</dt>
042 *     <dd><pre>{ "operator" : "co", "field" : "/afield", "value" : "something"}</pre></dd>
043 *     <dt>a starts-with expression</dt>
044 *     <dd><pre>{ "operator" : "sw", "field" : "/afield", "value" : "some"}</pre></dd>
045 *     <dt>a less-than expression</dt>
046 *     <dd><pre>{ "operator" : "lt", "field" : "/afield", "value" : "something"}</pre></dd>
047 *     <dt>a less-than-or-equal-to expression</dt>
048 *     <dd><pre>{ "operator" : "le", "field" : "/afield", "value" : "something"}</pre></dd>
049 *     <dt>a greater-than expression</dt>
050 *     <dd><pre>{ "operator" : "gt", "field" : "/afield", "value" : "something"}</pre></dd>
051 *     <dt>a greater-than-or-equal-to expression</dt>
052 *     <dd><pre>{ "operator" : "ge", "field" : "/afield", "value" : "something"}</pre></dd>
053 * </dl>
054 * <em><strong>Notes:</strong></em>
055 * <ol>
056 *     <li>
057 *         JSON notation used for convenience to illustrate Map-structure.
058 *         <ul>
059 *             <li>To produce JSON, use
060 *                  {@code new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(map);}
061 *             </li>
062 *             <li>Or wrap with JsonValue: <pre>new org.forgerock.json.JsonValue(map);</pre></li>
063 *         </ul>
064 *     </li>
065 *     <li>
066 *         Field values are shown in {@code org.forgerock.json.JsonPointer} syntax; actual field representation
067 * depends on field type of QueryFilter.
068 *     </li>
069 * </ol>
070 *
071 * @param <F> The type of field description used in parsed {@link QueryFilter} objects.
072 */
073public class MapFilterVisitor<F> implements QueryFilterVisitor<Map<String, Object>, Void, F> {
074    static final String OPERATOR = "operator";
075    static final String FIELD = "field";
076    static final String VALUE = "value";
077    static final String SUBFILTERS = "subfilters";
078    static final String SUBFILTER = "subfilter";
079
080    /**
081     * {@inheritDoc}
082     */
083    @Override
084    public Map<String, Object> visitAndFilter(final Void parameters, List<QueryFilter<F>> subFilters) {
085        final List<Object> filters = new ArrayList<>();
086        for (QueryFilter<F> filter : subFilters) {
087            filters.add(filter.accept(this, parameters));
088        }
089        final Map<String, Object> object = new HashMap<>();
090        object.put(OPERATOR, AND);
091        object.put(SUBFILTERS, filters);
092        return object;
093    }
094
095    /**
096     * {@inheritDoc}
097     */
098    @Override
099    public Map<String, Object> visitBooleanLiteralFilter(Void parameters, boolean value) {
100        final Map<String, Object> object = new HashMap<>();
101        object.put(OPERATOR, value);
102        return object;
103    }
104
105    /**
106     * {@inheritDoc}
107     */
108    @Override
109    public Map<String, Object> visitContainsFilter(Void parameters, F field, Object valueAssertion) {
110        return object(field, CONTAINS, valueAssertion);
111    }
112
113    /**
114     * {@inheritDoc}
115     */
116    @Override
117    public Map<String, Object> visitEqualsFilter(Void parameters, F field, Object valueAssertion) {
118        return object(field, EQUALS, valueAssertion);
119    }
120
121    /**
122     * {@inheritDoc}
123     */
124    @Override
125    public Map<String, Object> visitExtendedMatchFilter(Void parameters, F field, String operator,
126            Object valueAssertion) {
127        return object(field, operator, valueAssertion);
128    }
129
130    /**
131     * {@inheritDoc}
132     */
133    @Override
134    public Map<String, Object> visitGreaterThanFilter(Void parameters, F field, Object valueAssertion) {
135        return object(field, GREATER_THAN, valueAssertion);
136    }
137
138    /**
139     * {@inheritDoc}
140     */
141    @Override
142    public Map<String, Object> visitGreaterThanOrEqualToFilter(Void parameters, F field, Object valueAssertion) {
143        return object(field, GREATER_EQUAL, valueAssertion);
144    }
145
146    /**
147     * {@inheritDoc}
148     */
149    @Override
150    public Map<String, Object> visitLessThanFilter(Void parameters, F field, Object valueAssertion) {
151        return object(field, LESS_THAN, valueAssertion);
152    }
153
154    /**
155     * {@inheritDoc}
156     */
157    @Override
158    public Map<String, Object> visitLessThanOrEqualToFilter(Void parameters, F field, Object valueAssertion) {
159        return object(field, LESS_EQUAL, valueAssertion);
160    }
161
162    /**
163     * {@inheritDoc}
164     */
165    @Override
166    public Map<String, Object> visitNotFilter(Void parameters, QueryFilter<F> subFilter) {
167        final Map<String, Object> object = new HashMap<>();
168        object.put(OPERATOR, NOT);
169        object.put(SUBFILTER, subFilter.accept(this, parameters));
170        return object;
171    }
172
173    /**
174     * {@inheritDoc}
175     */
176    @Override
177    public Map<String, Object> visitOrFilter(final Void parameters, List<QueryFilter<F>> subFilters) {
178        final List<Object> filters = new ArrayList<>();
179        for (QueryFilter<F> filter : subFilters) {
180            filters.add(filter.accept(this, parameters));
181        }
182        final Map<String, Object> object = new HashMap<>();
183        object.put(OPERATOR, OR);
184        object.put(SUBFILTERS, filters);
185        return object;
186    }
187
188    /**
189     * {@inheritDoc}
190     */
191    @Override
192    public Map<String, Object> visitPresentFilter(Void parameters, F field) {
193        final Map<String, Object> object = new HashMap<>();
194        object.put(OPERATOR, PRESENT);
195        object.put(FIELD, field.toString());
196        return object;
197    }
198
199    /**
200     * {@inheritDoc}
201     */
202    @Override
203    public Map<String, Object> visitStartsWithFilter(Void parameters, F field, Object valueAssertion) {
204        return object(field, STARTS_WITH, valueAssertion);
205    }
206
207    private Map<String, Object> object(F field, String operator, Object valueAssertion) {
208        final Map<String, Object> object = new HashMap<>();
209        object.put(OPERATOR, operator);
210        object.put(FIELD, field.toString());
211        if (valueAssertion == null || valueAssertion instanceof Number || valueAssertion instanceof Boolean) {
212            object.put(VALUE, valueAssertion);
213        } else {
214            object.put(VALUE, String.valueOf(valueAssertion));
215        }
216        return object;
217    }
218
219}