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}