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 2006-2008 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.controls;
018import org.forgerock.i18n.LocalizableMessage;
019
020
021
022import java.util.ArrayList;
023import java.io.IOException;
024import java.util.List;
025
026import org.forgerock.opendj.io.*;
027import org.forgerock.opendj.ldap.schema.AttributeType;
028import org.opends.server.types.*;
029import org.forgerock.opendj.ldap.ResultCode;
030import org.forgerock.opendj.ldap.ByteString;
031import org.forgerock.i18n.slf4j.LocalizedLogger;
032import static org.opends.messages.ProtocolMessages.*;
033import static org.opends.server.util.ServerConstants.*;
034import static org.opends.server.util.StaticUtils.*;
035
036
037
038/**
039 * This class implements the matched values control as defined in RFC 3876.  It
040 * may be included in a search request to indicate that only attribute values
041 * matching one or more filters contained in the matched values control should
042 * be returned to the client.
043 */
044public class MatchedValuesControl
045       extends Control
046{
047  /** ControlDecoder implementation to decode this control from a ByteString. */
048  private static final class Decoder
049      implements ControlDecoder<MatchedValuesControl>
050  {
051    @Override
052    public MatchedValuesControl decode(boolean isCritical, ByteString value)
053        throws DirectoryException
054    {
055      ArrayList<MatchedValuesFilter> filters;
056      if (value == null)
057      {
058        LocalizableMessage message = ERR_MATCHEDVALUES_NO_CONTROL_VALUE.get();
059        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
060      }
061
062      ASN1Reader reader = ASN1.getReader(value);
063      try
064      {
065        reader.readStartSequence();
066        if (!reader.hasNextElement())
067        {
068          LocalizableMessage message = ERR_MATCHEDVALUES_NO_FILTERS.get();
069          throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
070        }
071
072        filters = new ArrayList<>();
073        while(reader.hasNextElement())
074        {
075          filters.add(MatchedValuesFilter.decode(reader));
076        }
077        reader.readEndSequence();
078      }
079      catch (DirectoryException e)
080      {
081        throw e;
082      }
083      catch (Exception e)
084      {
085        logger.traceException(e);
086
087        LocalizableMessage message = ERR_MATCHEDVALUES_CANNOT_DECODE_VALUE_AS_SEQUENCE.get(
088            getExceptionMessage(e));
089        throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message);
090      }
091
092      return new MatchedValuesControl(isCritical,filters);
093    }
094
095
096    @Override
097    public String getOID()
098    {
099      return OID_MATCHED_VALUES;
100    }
101
102  }
103
104  /** The Control Decoder that can be used to decode this control. */
105  public static final ControlDecoder<MatchedValuesControl> DECODER =
106    new Decoder();
107  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
108
109
110
111
112  /** The set of matched values filters for this control. */
113  private final List<MatchedValuesFilter> filters;
114
115
116
117  /**
118   * Creates a new matched values control using the default OID and the provided
119   * criticality and set of filters.
120   *
121   * @param  isCritical  Indicates whether this control should be considered
122   *                     critical to the operation processing.
123   * @param  filters     The set of filters to use to determine which values to
124   *                     return.
125   */
126  public MatchedValuesControl(boolean isCritical,
127                              List<MatchedValuesFilter> filters)
128  {
129    super(OID_MATCHED_VALUES, isCritical);
130
131
132    this.filters = filters;
133  }
134
135  @Override
136  public void writeValue(ASN1Writer writer) throws IOException {
137    writer.writeStartSequence(ASN1.UNIVERSAL_OCTET_STRING_TYPE);
138
139    writer.writeStartSequence();
140    for (MatchedValuesFilter f : filters)
141    {
142      f.encode(writer);
143    }
144    writer.writeEndSequence();
145
146    writer.writeEndSequence();
147  }
148
149
150  /**
151   * Retrieves the set of filters associated with this matched values control.
152   *
153   * @return  The set of filters associated with this matched values control.
154   */
155  public List<MatchedValuesFilter> getFilters()
156  {
157    return filters;
158  }
159
160
161
162  /**
163   * Indicates whether any of the filters associated with this matched values
164   * control matches the provided attribute type/value.
165   *
166   * @param  type   The attribute type with which the value is associated.
167   * @param  value  The attribute value for which to make the determination.
168   *
169   * @return  <CODE>true</CODE> if at least one of the filters associated with
170   *          this matched values control does match the provided attribute
171   *          value, or <CODE>false</CODE> if none of the filters match.
172   */
173  public boolean valueMatches(AttributeType type, ByteString value)
174  {
175    for (MatchedValuesFilter f : filters)
176    {
177      try
178      {
179        if (f.valueMatches(type, value))
180        {
181          return true;
182        }
183      }
184      catch (Exception e)
185      {
186        logger.traceException(e);
187      }
188    }
189
190    return false;
191  }
192
193  @Override
194  public void toString(StringBuilder buffer)
195  {
196    if (filters.size() == 1)
197    {
198      buffer.append("MatchedValuesControl(filter=\"");
199      filters.get(0).toString(buffer);
200      buffer.append("\")");
201    }
202    else
203    {
204      buffer.append("MatchedValuesControl(filters=\"(");
205
206      for (MatchedValuesFilter f : filters)
207      {
208        f.toString(buffer);
209      }
210
211      buffer.append(")\")");
212    }
213  }
214}
215