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