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}