001    /*
002     * Copyright 2011-2012 UnboundID Corp.
003     *
004     * This program is free software; you can redistribute it and/or modify
005     * it under the terms of the GNU General Public License (GPLv2 only)
006     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
007     * as published by the Free Software Foundation.
008     *
009     * This program is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012     * GNU General Public License for more details.
013     *
014     * You should have received a copy of the GNU General Public License
015     * along with this program; if not, see <http://www.gnu.org/licenses>.
016     */
017    
018    package com.unboundid.scim.sdk;
019    
020    import org.json.JSONException;
021    import org.json.JSONObject;
022    
023    import java.util.ArrayList;
024    import java.util.List;
025    
026    
027    
028    /**
029     * This class represents a SCIM query filter.
030     */
031    public class SCIMFilter
032    {
033      /**
034       * The filter type.
035       */
036      private final SCIMFilterType filterType;
037    
038      /**
039       * The attribute or sub-attribute to filter by, or {@code null} if not
040       * applicable.
041       */
042      private final AttributePath filterAttribute;
043    
044      /**
045       * The filter attribute value, or {@code null} if not applicable.
046       */
047      private final String filterValue;
048    
049      /**
050       * Specifies whether the filter value is quoted in the string representation
051       * of the filter. String and DateTime values are quoted. Integer and Boolean
052       * values are not quoted.
053       */
054      private final boolean quoteFilterValue;
055    
056      /**
057       * The filter components for 'or' and 'and' filter types, or {@code null}
058       * if not applicable.
059       */
060      private final List<SCIMFilter> filterComponents;
061    
062    
063    
064      /**
065       * Create a new SCIM filter from the provided information.
066       *
067       * @param filterType        The filter type.
068       * @param filterAttribute   The attribute or sub-attribute to filter by, or
069       *                          {@code null} if not applicable.
070       * @param filterValue       The filter attribute value, or {@code null} if not
071       *                          applicable.
072       * @param quoteFilterValue  Specifies whether the filter value is quoted in
073       *                          the string representation of the filter.
074       * @param filterComponents  The filter components for 'or' and 'and' filter
075       *                          types, or {@code null} if not applicable.
076       */
077      public SCIMFilter(final SCIMFilterType filterType,
078                        final AttributePath filterAttribute,
079                        final String filterValue,
080                        final boolean quoteFilterValue,
081                        final List<SCIMFilter> filterComponents)
082      {
083        this.filterType        = filterType;
084        this.filterAttribute   = filterAttribute;
085        this.filterValue       = filterValue;
086        this.quoteFilterValue  = quoteFilterValue;
087        this.filterComponents  = filterComponents;
088      }
089    
090    
091    
092      /**
093       * Parse a filter from its string representation.
094       *
095       * @param filterString  The string representation of the filter expression.
096       *
097       * @return  The parsed filter.
098       *
099       * @throws  SCIMException  If the filter string could not be parsed.
100       */
101      public static SCIMFilter parse(final String filterString)
102          throws SCIMException
103      {
104        final FilterParser parser = new FilterParser(filterString);
105    
106        return parser.parse();
107      }
108    
109    
110    
111      /**
112       * Create a new and filter.
113       *
114       * @param filterComponents  The filter components.
115       *
116       * @return  A new and filter.
117       */
118      public static SCIMFilter createAndFilter(
119          final List<SCIMFilter> filterComponents)
120      {
121        return new SCIMFilter(SCIMFilterType.AND, null, null, false,
122                              new ArrayList<SCIMFilter>(filterComponents));
123      }
124    
125    
126    
127      /**
128       * Create a new or filter.
129       *
130       * @param filterComponents  The filter components.
131       *
132       * @return  A new or filter.
133       */
134      public static SCIMFilter createOrFilter(
135          final List<SCIMFilter> filterComponents)
136      {
137        return new SCIMFilter(SCIMFilterType.OR, null, null, false,
138                              new ArrayList<SCIMFilter>(filterComponents));
139      }
140    
141    
142    
143      /**
144       * Create a new equality filter.
145       *
146       * @param filterAttribute  The attribute or sub-attribute to filter by.
147       * @param filterValue      The filter attribute value.
148       *
149       * @return  A new equality filter.
150       */
151      public static SCIMFilter createEqualityFilter(
152          final AttributePath filterAttribute, final String filterValue)
153      {
154        return new SCIMFilter(SCIMFilterType.EQUALITY, filterAttribute,
155                              filterValue, true, null);
156      }
157    
158    
159    
160      /**
161       * Create a new contains filter.
162       *
163       * @param filterAttribute  The attribute or sub-attribute to filter by.
164       * @param filterValue      The filter attribute value.
165       *
166       * @return  A new contains filter.
167       */
168      public static SCIMFilter createContainsFilter(
169          final AttributePath filterAttribute, final String filterValue)
170      {
171        return new SCIMFilter(SCIMFilterType.CONTAINS, filterAttribute,
172                              filterValue, true, null);
173      }
174    
175    
176    
177      /**
178       * Create a new starts with filter.
179       *
180       * @param filterAttribute  The attribute or sub-attribute to filter by.
181       * @param filterValue      The filter attribute value.
182       *
183       * @return  A new starts with filter.
184       */
185      public static SCIMFilter createStartsWithFilter(
186          final AttributePath filterAttribute, final String filterValue)
187      {
188        return new SCIMFilter(SCIMFilterType.STARTS_WITH, filterAttribute,
189                              filterValue, true, null);
190      }
191    
192    
193    
194      /**
195       * Create a new presence filter.
196       *
197       * @param filterAttribute  The attribute or sub-attribute to filter by.
198       *
199       * @return  A new presence filter.
200       */
201      public static SCIMFilter createPresenceFilter(
202          final AttributePath filterAttribute)
203      {
204        return new SCIMFilter(SCIMFilterType.PRESENCE, filterAttribute,
205                              null, true, null);
206      }
207    
208    
209    
210      /**
211       * Create a new greater than filter.
212       *
213       * @param filterAttribute  The attribute or sub-attribute to filter by.
214       * @param filterValue      The filter attribute value.
215       *
216       * @return  A new greater than filter.
217       */
218      public static SCIMFilter createGreaterThanFilter(
219          final AttributePath filterAttribute, final String filterValue)
220      {
221        return new SCIMFilter(SCIMFilterType.GREATER_THAN, filterAttribute,
222                              filterValue, true, null);
223      }
224    
225    
226    
227      /**
228       * Create a new greater or equal filter.
229       *
230       * @param filterAttribute  The attribute or sub-attribute to filter by.
231       * @param filterValue      The filter attribute value.
232       *
233       * @return  A new greater or equal filter.
234       */
235      public static SCIMFilter createGreaterOrEqualFilter(
236          final AttributePath filterAttribute, final String filterValue)
237      {
238        return new SCIMFilter(SCIMFilterType.GREATER_OR_EQUAL, filterAttribute,
239                              filterValue, true, null);
240      }
241    
242    
243    
244      /**
245       * Create a new less than filter.
246       *
247       * @param filterAttribute  The attribute or sub-attribute to filter by.
248       * @param filterValue      The filter attribute value.
249       *
250       * @return  A new less than filter.
251       */
252      public static SCIMFilter createLessThanFilter(
253          final AttributePath filterAttribute, final String filterValue)
254      {
255        return new SCIMFilter(SCIMFilterType.LESS_THAN, filterAttribute,
256                              filterValue, true, null);
257      }
258    
259    
260    
261      /**
262       * Create a new less or equal filter.
263       *
264       * @param filterAttribute  The attribute or sub-attribute to filter by.
265       * @param filterValue      The filter attribute value.
266       *
267       * @return  A new less or equal filter.
268       */
269      public static SCIMFilter createLessOrEqualFilter(
270          final AttributePath filterAttribute, final String filterValue)
271      {
272        return new SCIMFilter(SCIMFilterType.LESS_OR_EQUAL, filterAttribute,
273                              filterValue, true, null);
274      }
275    
276    
277    
278      /**
279       * Retrieve the filter type.
280       *
281       * @return  The filter type.
282       */
283      public SCIMFilterType getFilterType()
284      {
285        return filterType;
286      }
287    
288    
289    
290      /**
291       * Retrieve the attribute or sub-attribute to filter by, or {@code null} if
292       * not applicable for this filter type.
293       *
294       * @return  The attribute or sub-attribute to filter by
295       */
296      public AttributePath getFilterAttribute()
297      {
298        return filterAttribute;
299      }
300    
301    
302    
303      /**
304       * Retrieve the filter attribute value.
305       *
306       * @return  The filter attribute value, or {@code null} if not applicable
307       *          for this filter type.
308       */
309      public String getFilterValue()
310      {
311        return filterValue;
312      }
313    
314    
315    
316      /**
317       * Determine whether the filter attribute value is quoted in the string
318       * representation of the filter.
319       *
320       * @return {@code true} if the filter attribute value is quoted in the string
321       *         representation of the filter.
322       */
323      public boolean isQuoteFilterValue()
324      {
325        return quoteFilterValue;
326      }
327    
328    
329    
330      /**
331       * Retrieve the filter components for an 'and' or 'or' filter.
332       *
333       * @return  The filter components for an 'and' or 'or' filter.
334       */
335      public List<SCIMFilter> getFilterComponents()
336      {
337        return filterComponents;
338      }
339    
340    
341    
342      /**
343       * {@inheritDoc}
344       */
345      @Override
346      public String toString()
347      {
348        final StringBuilder builder = new StringBuilder();
349        toString(builder);
350        return builder.toString();
351      }
352    
353    
354    
355      /**
356       * Append the string representation of the filter to the provided buffer.
357       *
358       * @param builder  The buffer to which the string representation of the
359       *                 filter is to be appended.
360       */
361      public void toString(final StringBuilder builder)
362      {
363        switch (filterType)
364        {
365          case AND:
366          case OR:
367            builder.append('(');
368    
369            for (int i = 0; i < filterComponents.size(); i++)
370            {
371              if (i != 0)
372              {
373                builder.append(' ');
374                builder.append(filterType);
375                builder.append(' ');
376              }
377    
378              builder.append(filterComponents.get(i));
379            }
380    
381            builder.append(')');
382            break;
383    
384          case EQUALITY:
385          case CONTAINS:
386          case STARTS_WITH:
387          case GREATER_THAN:
388          case GREATER_OR_EQUAL:
389          case LESS_THAN:
390          case LESS_OR_EQUAL:
391            builder.append(filterAttribute);
392            builder.append(' ');
393            builder.append(filterType);
394            builder.append(' ');
395    
396            if (quoteFilterValue)
397            {
398              try
399              {
400                builder.append(JSONObject.valueToString(filterValue));
401              }
402              catch (JSONException e)
403              {
404                Debug.debugException(e);
405              }
406            }
407            else
408            {
409              builder.append(filterValue);
410            }
411            break;
412    
413          case PRESENCE:
414            builder.append(filterAttribute);
415            builder.append(' ');
416            builder.append(filterType);
417            break;
418        }
419      }
420    }