001    /*
002     * Copyright 2008-2013 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2013 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk.controls;
022    
023    
024    
025    import com.unboundid.asn1.ASN1Element;
026    import com.unboundid.asn1.ASN1OctetString;
027    import com.unboundid.asn1.ASN1Sequence;
028    import com.unboundid.ldap.sdk.Control;
029    import com.unboundid.ldap.sdk.LDAPException;
030    import com.unboundid.ldap.sdk.ResultCode;
031    import com.unboundid.util.NotMutable;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
034    
035    import static com.unboundid.ldap.sdk.controls.ControlMessages.*;
036    import static com.unboundid.util.Debug.*;
037    import static com.unboundid.util.Validator.*;
038    
039    
040    
041    /**
042     * This class provides an implementation of the matched values request control
043     * as defined in <A HREF="http://www.ietf.org/rfc/rfc3876.txt">RFC 3876</A>.  It
044     * should only be used with a search request, in which case it indicates that
045     * only attribute values matching at least one of the provided
046     * {@link MatchedValuesFilter}s should be included in matching entries.  That
047     * is, this control may be used to restrict the set of values included in the
048     * entries that are returned.  This is particularly useful for multivalued
049     * attributes with a large number of values when only a small number of values
050     * are of interest to the client.
051     * <BR><BR>
052     * There are no corresponding response controls included in the search result
053     * entry, search result reference, or search result done messages returned for
054     * the associated search request.
055     * <BR><BR>
056     * <H2>Example</H2>
057     * The following example demonstrates the use of the matched values request
058     * control.  It will cause only values of the "{@code myIntValues}" attribute
059     * to be returned in which those values are greater than or equal to five:
060     * <PRE>
061     *   SearchRequest searchRequest =
062     *        new SearchRequest("uid=john.doe,ou=People,dc=example,dc=com",
063     *                          SearchScope.BASE, "(objectClass=*)", "myIntValues");
064     *   searchRequest.addControl(new MatchedValuesRequestControl(
065     *        MatchedValuesFilter.createGreaterOrEqualFilter("myIntValues", "5"));
066     *   SearchResult result = connection.search(searchRequest);
067     * </PRE>
068     */
069    @NotMutable()
070    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
071    public final class MatchedValuesRequestControl
072           extends Control
073    {
074      /**
075       * The OID (1.2.826.0.1.3344810.2.3) for the matched values request control.
076       */
077      public static final String MATCHED_VALUES_REQUEST_OID =
078           "1.2.826.0.1.3344810.2.3";
079    
080    
081    
082      /**
083       * The serial version UID for this serializable class.
084       */
085      private static final long serialVersionUID = 6799850686547208774L;
086    
087    
088    
089      // The set of matched values filters for this control.
090      private final MatchedValuesFilter[] filters;
091    
092    
093    
094      /**
095       * Creates a new matched values request control with the provided set of
096       * filters.  It will not be be marked as critical.
097       *
098       * @param  filters  The set of filters to use for this control.  At least one
099       *                  filter must be provided.
100       */
101      public MatchedValuesRequestControl(final MatchedValuesFilter... filters)
102      {
103        this(false, filters);
104      }
105    
106    
107    
108      /**
109       * Creates a new matched values request control with the provided criticality
110       * and set of filters.
111       *
112       * @param  isCritical  Indicates whether this control should be marked
113       *                     critical.
114       * @param  filters     The set of filters to use for this control.  At least
115       *                     one filter must be provided.
116       */
117      public MatchedValuesRequestControl(final boolean isCritical,
118                                         final MatchedValuesFilter... filters)
119      {
120        super(MATCHED_VALUES_REQUEST_OID, isCritical,  encodeValue(filters));
121    
122        this.filters = filters;
123      }
124    
125    
126    
127      /**
128       * Creates a new matched values request control which is decoded from the
129       * provided generic control.
130       *
131       * @param  control  The generic control to be decoded as a matched values
132       *                  request control.
133       *
134       * @throws  LDAPException  If the provided control cannot be decoded as a
135       *                         matched values request control.
136       */
137      public MatchedValuesRequestControl(final Control control)
138             throws LDAPException
139      {
140        super(control);
141    
142        final ASN1OctetString value = control.getValue();
143        if (value == null)
144        {
145          throw new LDAPException(ResultCode.DECODING_ERROR,
146                                  ERR_MV_REQUEST_NO_VALUE.get());
147        }
148    
149        try
150        {
151          final ASN1Element valueElement = ASN1Element.decode(value.getValue());
152          final ASN1Element[] filterElements =
153               ASN1Sequence.decodeAsSequence(valueElement).elements();
154          filters = new MatchedValuesFilter[filterElements.length];
155          for (int i=0; i < filterElements.length; i++)
156          {
157            filters[i] = MatchedValuesFilter.decode(filterElements[i]);
158          }
159        }
160        catch (Exception e)
161        {
162          debugException(e);
163          throw new LDAPException(ResultCode.DECODING_ERROR,
164                                  ERR_MV_REQUEST_CANNOT_DECODE.get(e), e);
165        }
166      }
167    
168    
169    
170      /**
171       * Encodes the provided set of filters into a value appropriate for use with
172       * the matched values control.
173       *
174       * @param  filters  The set of filters to include in the value.  It must not
175       *                  be {@code null} or empty.
176       *
177       * @return  The ASN.1 octet string containing the encoded control value.
178       */
179      private static ASN1OctetString encodeValue(
180                                          final MatchedValuesFilter[] filters)
181      {
182        ensureNotNull(filters);
183        ensureTrue(filters.length > 0,
184                   "MatchedValuesRequestControl.filters must not be empty.");
185    
186        final ASN1Element[] elements = new ASN1Element[filters.length];
187        for (int i=0; i < filters.length; i++)
188        {
189          elements[i] = filters[i].encode();
190        }
191    
192        return new ASN1OctetString(new ASN1Sequence(elements).encode());
193      }
194    
195    
196    
197      /**
198       * Retrieves the set of filters for this matched values request control.
199       *
200       * @return  The set of filters for this matched values request control.
201       */
202      public MatchedValuesFilter[] getFilters()
203      {
204        return filters;
205      }
206    
207    
208    
209      /**
210       * {@inheritDoc}
211       */
212      @Override()
213      public String getControlName()
214      {
215        return INFO_CONTROL_NAME_MATCHED_VALUES_REQUEST.get();
216      }
217    
218    
219    
220      /**
221       * {@inheritDoc}
222       */
223      @Override()
224      public void toString(final StringBuilder buffer)
225      {
226        buffer.append("MatchedValuesRequestControl(filters={");
227    
228        for (int i=0; i < filters.length; i++)
229        {
230          if (i > 0)
231          {
232            buffer.append(", ");
233          }
234    
235          buffer.append('\'');
236          filters[i].toString(buffer);
237          buffer.append('\'');
238        }
239    
240        buffer.append("}, isCritical=");
241        buffer.append(isCritical());
242        buffer.append(')');
243      }
244    }