/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2009 Sun Microsystems, Inc. * Portions Copyright 2013 ForgeRock AS */ package org.opends.server.types; import org.opends.messages.Message; import java.util.ArrayList; import java.io.IOException; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.protocols.asn1.*; import org.opends.server.protocols.ldap.LDAPFilter; import static org.opends.messages.ProtocolMessages.*; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.protocols.ldap.LDAPConstants.*; import static org.opends.server.protocols.ldap.LDAPResultCode.*; import static org.opends.server.util.StaticUtils.*; /** * This class defines the data structures and methods to use when * interacting with a raw search filter, which defines a set of * criteria for locating entries in a search request. */ @org.opends.server.types.PublicAPI( stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, mayInstantiate=true, mayExtend=false, mayInvoke=true) public abstract class RawFilter { /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); /** * Creates a new LDAP filter from the provided filter string. * * @param filterString The filter string to use to create this raw * filter. * * @return The raw filter decoded from the provided filter string. * * @throws LDAPException If the provied filter string could not be * decoded as a raw filter. */ public static RawFilter create(String filterString) throws LDAPException { return LDAPFilter.decode(filterString); } /** * Creates a new LDAP filter from the provided search filter. * * @param filter The search filter to use to create this raw * filter. * * @return The constructed raw filter. */ public static RawFilter create(SearchFilter filter) { return new LDAPFilter(filter); } /** * Creates a new AND search filter with the provided filter * components. * * @param filterComponents The filter components for this AND * filter. * * @return The AND search filter with the provided filter * components. */ public static LDAPFilter createANDFilter(ArrayList<RawFilter> filterComponents) { return new LDAPFilter(FilterType.AND, filterComponents, null, null, null, null, null, null, null, false); } /** * Creates a new OR search filter with the provided filter * components. * * @param filterComponents The filter components for this OR * filter. * * @return The OR search filter with the provided filter * components. */ public static LDAPFilter createORFilter(ArrayList<RawFilter> filterComponents) { return new LDAPFilter(FilterType.OR, filterComponents, null, null, null, null, null, null, null, false); } /** * Creates a new NOT search filter with the provided filter * component. * * @param notComponent The filter component for this NOT filter. * * @return The NOT search filter with the provided filter * component. */ public static LDAPFilter createNOTFilter(RawFilter notComponent) { return new LDAPFilter(FilterType.NOT, null, notComponent, null, null, null, null, null, null, false); } /** * Creates a new equality search filter with the provided * information. * * @param attributeType The attribute type for this equality * filter. * @param assertionValue The assertion value for this equality * filter. * * @return The constructed equality search filter. */ public static LDAPFilter createEqualityFilter(String attributeType, ByteString assertionValue) { return new LDAPFilter(FilterType.EQUALITY, null, null, attributeType, assertionValue, null, null, null, null, false); } /** * Creates a new substring search filter with the provided * information. * * @param attributeType The attribute type for this substring * filter. * @param subInitialElement The subInitial element for this * substring filter. * @param subAnyElements The subAny elements for this substring * filter. * @param subFinalElement The subFinal element for this * substring filter. * * @return The constructed substring search filter. */ public static LDAPFilter createSubstringFilter(String attributeType, ByteString subInitialElement, ArrayList<ByteString> subAnyElements, ByteString subFinalElement) { return new LDAPFilter(FilterType.SUBSTRING, null, null, attributeType, null, subInitialElement, subAnyElements, subFinalElement, null, false); } /** * Creates a new greater or equal search filter with the provided * information. * * @param attributeType The attribute type for this greater or * equal filter. * @param assertionValue The assertion value for this greater or * equal filter. * * @return The constructed greater or equal search filter. */ public static LDAPFilter createGreaterOrEqualFilter( String attributeType, ByteString assertionValue) { return new LDAPFilter(FilterType.GREATER_OR_EQUAL, null, null, attributeType, assertionValue, null, null, null, null, false); } /** * Creates a new less or equal search filter with the provided * information. * * @param attributeType The attribute type for this less or equal * filter. * @param assertionValue The assertion value for this less or * equal filter. * * @return The constructed less or equal search filter. */ public static LDAPFilter createLessOrEqualFilter( String attributeType, ByteString assertionValue) { return new LDAPFilter(FilterType.LESS_OR_EQUAL, null, null, attributeType, assertionValue, null, null, null, null, false); } /** * Creates a new presence search filter with the provided attribute * type. * * @param attributeType The attribute type for this presence * filter. * * @return The constructed presence search filter. */ public static LDAPFilter createPresenceFilter(String attributeType) { return new LDAPFilter(FilterType.PRESENT, null, null, attributeType, null, null, null, null, null, false); } /** * Creates a new approximate search filter with the provided * information. * * @param attributeType The attribute type for this approximate * filter. * @param assertionValue The assertion value for this approximate * filter. * * @return The constructed approximate search filter. */ public static LDAPFilter createApproximateFilter( String attributeType, ByteString assertionValue) { return new LDAPFilter(FilterType.APPROXIMATE_MATCH, null, null, attributeType, assertionValue, null, null, null, null, false); } /** * Creates a new extensible matching search filter with the provided * information. * * @param matchingRuleID The matching rule ID for this extensible * filter. * @param attributeType The attribute type for this extensible * filter. * @param assertionValue The assertion value for this extensible * filter. * @param dnAttributes The dnAttributes flag for this extensible * filter. * * @return The constructed extensible matching search filter. */ public static LDAPFilter createExtensibleFilter( String matchingRuleID, String attributeType, ByteString assertionValue, boolean dnAttributes) { return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null, attributeType, assertionValue, null, null, null, matchingRuleID, dnAttributes); } /** * Retrieves the filter type for this search filter. * * @return The filter type for this search filter. */ public abstract FilterType getFilterType(); /** * Retrieves the set of subordinate filter components for AND or OR * searches. The contents of the returned list may be altered by * the caller. * * @return The set of subordinate filter components for AND and OR * searches, or {@code null} if this is not an AND or OR * search. */ public abstract ArrayList<RawFilter> getFilterComponents(); /** * Retrieves the subordinate filter component for NOT searches. * * @return The subordinate filter component for NOT searches, or * {@code null} if this is not a NOT search. */ public abstract RawFilter getNOTComponent(); /** * Retrieves the attribute type for this search filter. This will * not be applicable for AND, OR, or NOT filters. * * @return The attribute type for this search filter, or * {@code null} if there is none. */ public abstract String getAttributeType(); /** * Retrieves the assertion value for this search filter. This will * only be applicable for equality, greater or equal, less or equal, * approximate, or extensible matching filters. * * @return The assertion value for this search filter, or * {@code null} if there is none. */ public abstract ByteString getAssertionValue(); /** * Retrieves the subInitial component for this substring filter. * This is only applicable for substring search filters, but even * substring filters might not have a value for this component. * * @return The subInitial component for this substring filter, or * {@code null} if there is none. */ public abstract ByteString getSubInitialElement(); /** * Retrieves the set of subAny elements for this substring filter. * This is only applicable for substring search filters, and even * then may be {@code null} or empty for some substring filters. * * @return The set of subAny elements for this substring filter, or * {@code null} if there are none. */ public abstract ArrayList<ByteString> getSubAnyElements(); /** * Retrieves the subFinal element for this substring filter. This * is not applicable for any other filter type, and may not be * provided even for some substring filters. * * @return The subFinal element for this substring filter, or * {@code null} if there is none. */ public abstract ByteString getSubFinalElement(); /** * Retrieves the matching rule ID for this extensible match filter. * This is not applicable for any other type of filter and may not * be included in some extensible matching filters. * * @return The matching rule ID for this extensible match filter, * or {@code null} if there is none. */ public abstract String getMatchingRuleID(); /** * Retrieves the value of the DN attributes flag for this extensible * match filter, which indicates whether to perform matching on the * components of the DN. This does not apply for any other type of * filter. * * @return The value of the DN attributes flag for this * extensibleMatch filter. */ public abstract boolean getDNAttributes(); /** * Writes this protocol op to an ASN.1 output stream. * * @param stream The ASN.1 output stream to write to. * @throws IOException If a problem occurs while writing to the * stream. */ public void write(ASN1Writer stream) throws IOException { FilterType filterType = getFilterType(); switch (filterType) { case AND: case OR: stream.writeStartSequence(filterType.getBERType()); for(RawFilter f : getFilterComponents()) { f.write(stream); } stream.writeEndSequence(); return; case NOT: stream.writeStartSequence(filterType.getBERType()); getNOTComponent().write(stream); stream.writeEndSequence(); return; case EQUALITY: case GREATER_OR_EQUAL: case LESS_OR_EQUAL: case APPROXIMATE_MATCH: stream.writeStartSequence(filterType.getBERType()); stream.writeOctetString(getAttributeType()); stream.writeOctetString(getAssertionValue()); stream.writeEndSequence(); return; case SUBSTRING: stream.writeStartSequence(filterType.getBERType()); stream.writeOctetString(getAttributeType()); stream.writeStartSequence(); ByteString subInitialElement = getSubInitialElement(); if (subInitialElement != null) { stream.writeOctetString(TYPE_SUBINITIAL, subInitialElement); } ArrayList<ByteString> subAnyElements = getSubAnyElements(); if ((subAnyElements != null) && (! subAnyElements.isEmpty())) { for (ByteString s : subAnyElements) { stream.writeOctetString(TYPE_SUBANY, s); } } ByteString subFinalElement = getSubFinalElement(); if (subFinalElement != null) { stream.writeOctetString(TYPE_SUBFINAL, subFinalElement); } stream.writeEndSequence(); stream.writeEndSequence(); return; case PRESENT: stream.writeOctetString(filterType.getBERType(), getAttributeType()); return; case EXTENSIBLE_MATCH: stream.writeStartSequence(filterType.getBERType()); String matchingRuleID = getMatchingRuleID(); if (matchingRuleID != null) { stream.writeOctetString(TYPE_MATCHING_RULE_ID, matchingRuleID); } String attributeType = getAttributeType(); if (attributeType != null) { stream.writeOctetString(TYPE_MATCHING_RULE_TYPE, attributeType); } stream.writeOctetString(TYPE_MATCHING_RULE_VALUE, getAssertionValue()); if (getDNAttributes()) { stream.writeBoolean(TYPE_MATCHING_RULE_DN_ATTRIBUTES, true); } stream.writeEndSequence(); return; default: if (debugEnabled()) { TRACER.debugError("Invalid search filter type: %s", filterType); } } } /** * Decodes the elements from the provided ASN.1 reader as a * raw search filter. * * @param reader The ASN.1 reader. * * @return The decoded search filter. * * @throws LDAPException If the provided ASN.1 element cannot be * decoded as a raw search filter. */ public static LDAPFilter decode(ASN1Reader reader) throws LDAPException { byte type; try { type = reader.peekType(); } catch(Exception e) { Message message = ERR_LDAP_FILTER_DECODE_NULL.get(); throw new LDAPException(PROTOCOL_ERROR, message); } switch (type) { case TYPE_FILTER_AND: case TYPE_FILTER_OR: return decodeCompoundFilter(reader); case TYPE_FILTER_NOT: return decodeNotFilter(reader); case TYPE_FILTER_EQUALITY: case TYPE_FILTER_GREATER_OR_EQUAL: case TYPE_FILTER_LESS_OR_EQUAL: case TYPE_FILTER_APPROXIMATE: return decodeAVAFilter(reader); case TYPE_FILTER_SUBSTRING: return decodeSubstringFilter(reader); case TYPE_FILTER_PRESENCE: return decodePresenceFilter(reader); case TYPE_FILTER_EXTENSIBLE_MATCH: return decodeExtensibleMatchFilter(reader); default: Message message = ERR_LDAP_FILTER_DECODE_INVALID_TYPE.get(type); throw new LDAPException(PROTOCOL_ERROR, message); } } /** * Decodes the elements from the provided ASN.1 reader as a compound * filter (i.e. one that holds a set of subordinate filter * components, like AND or OR filters). * * @param reader The ASN.1 reader. * * @return The decoded LDAP search filter. * * @throws LDAPException If a problem occurs while trying to * decode the provided ASN.1 element as a * raw search filter. */ private static LDAPFilter decodeCompoundFilter(ASN1Reader reader) throws LDAPException { byte type; try { type = reader.peekType(); } catch(Exception e) { Message message = ERR_LDAP_FILTER_DECODE_NULL.get(); throw new LDAPException(PROTOCOL_ERROR, message); } FilterType filterType; switch (type) { case TYPE_FILTER_AND: filterType = FilterType.AND; break; case TYPE_FILTER_OR: filterType = FilterType.OR; break; default: // This should never happen. if (debugEnabled()) { TRACER.debugError("Invalid filter type %x for a " + "compound filter", type); } filterType = null; } ArrayList<RawFilter> filterComponents = new ArrayList<RawFilter>(); try { reader.readStartSequence(); // Should have at least 1 filter // could also be an absolute true/false filter while (reader.hasNextElement()) { filterComponents.add(LDAPFilter.decode(reader)); } reader.readEndSequence(); } catch (LDAPException le) { throw le; } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_COMPOUND_COMPONENTS. get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } return new LDAPFilter(filterType, filterComponents, null, null, null, null, null, null, null, false); } /** * Decodes the elements from the provided ASN.1 reader as a NOT * filter. * * @param reader The ASN.1 reader. * * @return The decoded LDAP search filter. * * @throws LDAPException If a problem occurs while trying to * decode the provided ASN.1 element as a * raw search filter. */ private static LDAPFilter decodeNotFilter(ASN1Reader reader) throws LDAPException { RawFilter notComponent; try { reader.readStartSequence(); notComponent = decode(reader); reader.readEndSequence(); } catch (LDAPException le) { throw le; } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_NOT_COMPONENT.get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } return new LDAPFilter(FilterType.NOT, null, notComponent, null, null, null, null, null, null, false); } /** * Decodes the elements from the provided ASN.1 read as as a filter * containing an attribute type and an assertion value. This * includes equality, greater or equal, less or equal, and * approximate search filters. * * @param reader The ASN.1 reader. * * @return The decoded LDAP search filter. * * @throws LDAPException If a problem occurs while trying to * decode the provided ASN.1 element as a * raw search filter. */ private static LDAPFilter decodeAVAFilter(ASN1Reader reader) throws LDAPException { byte type; try { type = reader.peekType(); } catch(Exception e) { Message message = ERR_LDAP_FILTER_DECODE_NULL.get(); throw new LDAPException(PROTOCOL_ERROR, message); } FilterType filterType; switch (type) { case TYPE_FILTER_EQUALITY: filterType = FilterType.EQUALITY; break; case TYPE_FILTER_GREATER_OR_EQUAL: filterType = FilterType.GREATER_OR_EQUAL; break; case TYPE_FILTER_LESS_OR_EQUAL: filterType = FilterType.LESS_OR_EQUAL; break; case TYPE_FILTER_APPROXIMATE: filterType = FilterType.APPROXIMATE_MATCH; break; default: // This should never happen. if (debugEnabled()) { TRACER.debugError("Invalid filter type %x for a " + "type-and-value filter", type); } filterType = null; } try { reader.readStartSequence(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_TV_SEQUENCE.get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } String attributeType; try { attributeType = reader.readOctetStringAsString(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_TV_TYPE.get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } ByteString assertionValue; try { assertionValue = reader.readOctetString(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_TV_VALUE.get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } try { reader.readEndSequence(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_TV_SEQUENCE.get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } return new LDAPFilter(filterType, null, null, attributeType, assertionValue, null, null, null, null, false); } /** * Decodes the elements from the provided ASN.1 reader as a * substring filter. * * @param reader The ASN.1 reader. * * @return The decoded LDAP search filter. * * @throws LDAPException If a problem occurs while trying to * decode the provided ASN.1 element as a * raw search filter. */ private static LDAPFilter decodeSubstringFilter(ASN1Reader reader) throws LDAPException { try { reader.readStartSequence(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_SEQUENCE.get( String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } String attributeType; try { attributeType = reader.readOctetStringAsString(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_TYPE.get( String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } try { reader.readStartSequence(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_ELEMENTS.get( String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } try { // Make sure we have at least 1 substring reader.peekType(); } catch (Exception e) { Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_NO_SUBELEMENTS.get(); throw new LDAPException(PROTOCOL_ERROR, message); } ByteString subInitialElement = null; ByteString subFinalElement = null; ArrayList<ByteString> subAnyElements = null; try { if(reader.hasNextElement() && reader.peekType() == TYPE_SUBINITIAL) { subInitialElement = reader.readOctetString(); } while(reader.hasNextElement() && reader.peekType() == TYPE_SUBANY) { if(subAnyElements == null) { subAnyElements = new ArrayList<ByteString>(); } subAnyElements.add(reader.readOctetString()); } if(reader.hasNextElement() && reader.peekType() == TYPE_SUBFINAL) { subFinalElement = reader.readOctetString(); } } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_VALUES.get( String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } try { reader.readEndSequence(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_ELEMENTS.get( String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } try { reader.readEndSequence(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_SUBSTRING_SEQUENCE.get( String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } return new LDAPFilter(FilterType.SUBSTRING, null, null, attributeType, null, subInitialElement, subAnyElements, subFinalElement, null, false); } /** * Decodes the elements from the provided ASN.1 reader as a * presence filter. * * @param reader The ASN.1 reader. * * @return The decoded LDAP search filter. * * @throws LDAPException If a problem occurs while trying to * decode the provided ASN.1 element as a * raw search filter. */ private static LDAPFilter decodePresenceFilter(ASN1Reader reader) throws LDAPException { String attributeType; try { attributeType = reader.readOctetStringAsString(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_PRESENCE_TYPE.get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } return new LDAPFilter(FilterType.PRESENT, null, null, attributeType, null, null, null, null, null, false); } /** * Decodes the elements from the provided ASN.1 reader as an * extensible match filter. * * @param reader The ASN.1 reader. * * @return The decoded LDAP search filter. * * @throws LDAPException If a problem occurs while trying to * decode the provided ASN.1 element as a * raw search filter. */ private static LDAPFilter decodeExtensibleMatchFilter( ASN1Reader reader) throws LDAPException { try { reader.readStartSequence(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_EXTENSIBLE_SEQUENCE. get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } ByteString assertionValue; boolean dnAttributes = false; String attributeType = null; String matchingRuleID = null; try { if(reader.peekType() == TYPE_MATCHING_RULE_ID) { matchingRuleID = reader.readOctetStringAsString(); } if(matchingRuleID == null || reader.peekType() == TYPE_MATCHING_RULE_TYPE) { attributeType = reader.readOctetStringAsString(); } assertionValue = reader.readOctetString(); if(reader.hasNextElement() && reader.peekType() == TYPE_MATCHING_RULE_DN_ATTRIBUTES) { dnAttributes = reader.readBoolean(); } } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_EXTENSIBLE_ELEMENTS. get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } try { reader.readEndSequence(); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_LDAP_FILTER_DECODE_EXTENSIBLE_SEQUENCE. get(String.valueOf(e)); throw new LDAPException(PROTOCOL_ERROR, message, e); } return new LDAPFilter(FilterType.EXTENSIBLE_MATCH, null, null, attributeType, assertionValue, null, null, null, matchingRuleID, dnAttributes); } /** * Converts this raw filter to a search filter that may be used by * the Directory Server's core processing. * * @return The generated search filter. * * @throws DirectoryException If a problem occurs while attempting * to construct the search filter. */ public abstract SearchFilter toSearchFilter() throws DirectoryException; /** * Retrieves a string representation of this search filter. * * @return A string representation of this search filter. */ @Override public String toString() { StringBuilder buffer = new StringBuilder(); toString(buffer); return buffer.toString(); } /** * Appends a string representation of this search filter to the * provided buffer. * * @param buffer The buffer to which the information should be * appended. */ public abstract void toString(StringBuilder buffer); /** * Appends a properly-cleaned version of the provided value to the * given buffer so that it can be safely used in string * representations of this search filter. The formatting changes * that may be performed will be in compliance with the * specification in RFC 2254. * * @param buffer The buffer to which the "safe" version of the * value will be appended. * @param value The value to be appended to the buffer. */ public static void valueToFilterString(StringBuilder buffer, ByteString value) { if (value == null) { return; } // Get the binary representation of the value and iterate through // it to see if there are any unsafe characters. If there are, // then escape them and replace them with a two-digit hex // equivalent. buffer.ensureCapacity(buffer.length() + value.length()); for (int i = 0; i < value.length(); i++) { byte b = value.byteAt(i); if (((b & 0x7F) != b) || // Not 7-bit clean (b <= 0x1F) || // Below the printable character range (b == 0x28) || // Open parenthesis (b == 0x29) || // Close parenthesis (b == 0x2A) || // Asterisk (b == 0x5C) || // Backslash (b == 0x7F)) // Delete character { buffer.append("\\"); buffer.append(byteToHex(b)); } else { buffer.append((char) b); } } } }