001/* 002 * Copyright 2009-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2018 Ping Identity Corporation 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 */ 021package com.unboundid.ldap.sdk.unboundidds.examples; 022 023 024 025import java.io.Serializable; 026import java.util.Arrays; 027import java.util.TreeSet; 028 029import com.unboundid.ldap.sdk.Filter; 030import com.unboundid.util.NotMutable; 031import com.unboundid.util.ThreadSafety; 032import com.unboundid.util.ThreadSafetyLevel; 033 034import static com.unboundid.util.StaticUtils.*; 035 036 037 038/** 039 * This class provides a data structure for representing search filters in a 040 * generic way. 041 * <BR> 042 * <BLOCKQUOTE> 043 * <B>NOTE:</B> This class, and other classes within the 044 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 045 * supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661 046 * server products. These classes provide support for proprietary 047 * functionality or for external specifications that are not considered stable 048 * or mature enough to be guaranteed to work in an interoperable way with 049 * other types of LDAP servers. 050 * </BLOCKQUOTE> 051 * <BR> 052 * This includes: 053 * <UL> 054 * <LI>Using a consistent order for AND and OR components.</LI> 055 * <LI>Converting all attribute names to lowercase.</LI> 056 * <LI>Replacing the assertion value with a "?" character for equality, 057 * greater-or-equal, less-or-equal, approximate match, and extensible 058 * match filters.</LI> 059 * <LI>Replacing all subInitial, subAny, and subFinal elements with "?" 060 * characters in substring filters.</LI> 061 * </UL> 062 */ 063@NotMutable() 064@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 065public final class GenericFilter 066 implements Serializable 067{ 068 /** 069 * The serial version UID for this serializable class. 070 */ 071 private static final long serialVersionUID = -7875317078624475546L; 072 073 074 075 // The hash code for this generic filter. 076 private final int hashCode; 077 078 // The string representation for this filter. 079 private final String filterString; 080 081 082 083 /** 084 * Creates a new generic filter from the provided search filter. 085 * 086 * @param f The filter to use to create a generic filte.r 087 */ 088 public GenericFilter(final Filter f) 089 { 090 final StringBuilder b = new StringBuilder(); 091 b.append('('); 092 093 switch (f.getFilterType()) 094 { 095 case Filter.FILTER_TYPE_AND: 096 case Filter.FILTER_TYPE_OR: 097 appendComponents(f, b); 098 break; 099 100 case Filter.FILTER_TYPE_NOT: 101 b.append('!'); 102 b.append(new GenericFilter(f.getNOTComponent()).toString()); 103 break; 104 105 case Filter.FILTER_TYPE_EQUALITY: 106 b.append(toLowerCase(f.getAttributeName())); 107 b.append("=?"); 108 break; 109 110 case Filter.FILTER_TYPE_SUBSTRING: 111 b.append(toLowerCase(f.getAttributeName())); 112 b.append('='); 113 if (f.getRawSubInitialValue() != null) 114 { 115 b.append('?'); 116 } 117 for (int i=0; i < f.getRawSubAnyValues().length; i++) 118 { 119 b.append("*?"); 120 } 121 b.append('*'); 122 if (f.getRawSubFinalValue() != null) 123 { 124 b.append('?'); 125 } 126 break; 127 128 case Filter.FILTER_TYPE_GREATER_OR_EQUAL: 129 b.append(toLowerCase(f.getAttributeName())); 130 b.append(">=?"); 131 break; 132 133 case Filter.FILTER_TYPE_LESS_OR_EQUAL: 134 b.append(toLowerCase(f.getAttributeName())); 135 b.append("<=?"); 136 break; 137 138 case Filter.FILTER_TYPE_PRESENCE: 139 b.append(toLowerCase(f.getAttributeName())); 140 b.append("=*"); 141 break; 142 143 case Filter.FILTER_TYPE_APPROXIMATE_MATCH: 144 b.append(toLowerCase(f.getAttributeName())); 145 b.append("~=?"); 146 break; 147 148 case Filter.FILTER_TYPE_EXTENSIBLE_MATCH: 149 final String attrName = toLowerCase(f.getAttributeName()); 150 final String mrID = toLowerCase(f.getMatchingRuleID()); 151 if (attrName != null) 152 { 153 b.append(attrName); 154 } 155 if (f.getDNAttributes()) 156 { 157 b.append(":dn"); 158 } 159 if (mrID != null) 160 { 161 b.append(':'); 162 b.append(mrID); 163 } 164 b.append(":=?"); 165 break; 166 } 167 168 b.append(')'); 169 170 filterString = b.toString(); 171 hashCode = filterString.hashCode(); 172 } 173 174 175 176 /** 177 * Appends a string representation of the provided AND or OR filter to the 178 * given buffer. 179 * 180 * @param f The filter for which to provide the string representation. 181 * @param b The buffer to which to append the string representation. 182 */ 183 private static void appendComponents(final Filter f, final StringBuilder b) 184 { 185 if (f.getFilterType() == Filter.FILTER_TYPE_AND) 186 { 187 b.append('&'); 188 } 189 else 190 { 191 b.append('|'); 192 } 193 194 final TreeSet<Filter> compSet = 195 new TreeSet<Filter>(FilterComparator.getInstance()); 196 compSet.addAll(Arrays.asList(f.getComponents())); 197 for (final Filter fc : compSet) 198 { 199 b.append(new GenericFilter(fc).toString()); 200 } 201 } 202 203 204 205 /** 206 * Retrieves a hash code for this generic filter. 207 * 208 * @return A hash code for this generic filter. 209 */ 210 @Override() 211 public int hashCode() 212 { 213 return hashCode; 214 } 215 216 217 218 /** 219 * Indicates whether the provided object is equal to this generic filter. 220 * 221 * @param o The object for which to make the determination. 222 * 223 * @return {@code true} the provided object is equal to this generic filter, 224 * or {@code false} if not. 225 */ 226 @Override() 227 public boolean equals(final Object o) 228 { 229 if (o == null) 230 { 231 return false; 232 } 233 234 if (o == this) 235 { 236 return true; 237 } 238 239 return ((o instanceof GenericFilter) && 240 filterString.equals(((GenericFilter) o).filterString)); 241 } 242 243 244 245 /** 246 * Retrieves a string representation of this generic filter. 247 * 248 * @return A string representation of this generic filter. 249 */ 250 @Override() 251 public String toString() 252 { 253 return filterString; 254 } 255}