001/* 002 * Copyright 2009-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2009-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.examples; 037 038 039 040import java.io.Serializable; 041import java.util.Arrays; 042import java.util.TreeSet; 043 044import com.unboundid.ldap.sdk.Filter; 045import com.unboundid.util.NotMutable; 046import com.unboundid.util.NotNull; 047import com.unboundid.util.Nullable; 048import com.unboundid.util.StaticUtils; 049import com.unboundid.util.ThreadSafety; 050import com.unboundid.util.ThreadSafetyLevel; 051 052 053 054/** 055 * This class provides a data structure for representing search filters in a 056 * generic way. 057 * <BR> 058 * <BLOCKQUOTE> 059 * <B>NOTE:</B> This class, and other classes within the 060 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 061 * supported for use against Ping Identity, UnboundID, and 062 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 063 * for proprietary functionality or for external specifications that are not 064 * considered stable or mature enough to be guaranteed to work in an 065 * interoperable way with other types of LDAP servers. 066 * </BLOCKQUOTE> 067 * <BR> 068 * This includes: 069 * <UL> 070 * <LI>Using a consistent order for AND and OR components.</LI> 071 * <LI>Converting all attribute names to lowercase.</LI> 072 * <LI>Replacing the assertion value with a "?" character for equality, 073 * greater-or-equal, less-or-equal, approximate match, and extensible 074 * match filters.</LI> 075 * <LI>Replacing all subInitial, subAny, and subFinal elements with "?" 076 * characters in substring filters.</LI> 077 * </UL> 078 */ 079@NotMutable() 080@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 081public final class GenericFilter 082 implements Serializable 083{ 084 /** 085 * The serial version UID for this serializable class. 086 */ 087 private static final long serialVersionUID = -7875317078624475546L; 088 089 090 091 // The hash code for this generic filter. 092 private final int hashCode; 093 094 // The string representation for this filter. 095 @NotNull private final String filterString; 096 097 098 099 /** 100 * Creates a new generic filter from the provided search filter. 101 * 102 * @param f The filter to use to create a generic filter. 103 */ 104 public GenericFilter(@NotNull final Filter f) 105 { 106 final StringBuilder b = new StringBuilder(); 107 b.append('('); 108 109 switch (f.getFilterType()) 110 { 111 case Filter.FILTER_TYPE_AND: 112 case Filter.FILTER_TYPE_OR: 113 appendComponents(f, b); 114 break; 115 116 case Filter.FILTER_TYPE_NOT: 117 b.append('!'); 118 b.append(new GenericFilter(f.getNOTComponent()).toString()); 119 break; 120 121 case Filter.FILTER_TYPE_EQUALITY: 122 b.append(StaticUtils.toLowerCase(f.getAttributeName())); 123 b.append("=?"); 124 break; 125 126 case Filter.FILTER_TYPE_SUBSTRING: 127 b.append(StaticUtils.toLowerCase(f.getAttributeName())); 128 b.append('='); 129 if (f.getRawSubInitialValue() != null) 130 { 131 b.append('?'); 132 } 133 for (int i=0; i < f.getRawSubAnyValues().length; i++) 134 { 135 b.append("*?"); 136 } 137 b.append('*'); 138 if (f.getRawSubFinalValue() != null) 139 { 140 b.append('?'); 141 } 142 break; 143 144 case Filter.FILTER_TYPE_GREATER_OR_EQUAL: 145 b.append(StaticUtils.toLowerCase(f.getAttributeName())); 146 b.append(">=?"); 147 break; 148 149 case Filter.FILTER_TYPE_LESS_OR_EQUAL: 150 b.append(StaticUtils.toLowerCase(f.getAttributeName())); 151 b.append("<=?"); 152 break; 153 154 case Filter.FILTER_TYPE_PRESENCE: 155 b.append(StaticUtils.toLowerCase(f.getAttributeName())); 156 b.append("=*"); 157 break; 158 159 case Filter.FILTER_TYPE_APPROXIMATE_MATCH: 160 b.append(StaticUtils.toLowerCase(f.getAttributeName())); 161 b.append("~=?"); 162 break; 163 164 case Filter.FILTER_TYPE_EXTENSIBLE_MATCH: 165 final String attrName = StaticUtils.toLowerCase(f.getAttributeName()); 166 final String mrID = StaticUtils.toLowerCase(f.getMatchingRuleID()); 167 if (attrName != null) 168 { 169 b.append(attrName); 170 } 171 if (f.getDNAttributes()) 172 { 173 b.append(":dn"); 174 } 175 if (mrID != null) 176 { 177 b.append(':'); 178 b.append(mrID); 179 } 180 b.append(":=?"); 181 break; 182 } 183 184 b.append(')'); 185 186 filterString = b.toString(); 187 hashCode = filterString.hashCode(); 188 } 189 190 191 192 /** 193 * Appends a string representation of the provided AND or OR filter to the 194 * given buffer. 195 * 196 * @param f The filter for which to provide the string representation. 197 * @param b The buffer to which to append the string representation. 198 */ 199 private static void appendComponents(@NotNull final Filter f, 200 @NotNull final StringBuilder b) 201 { 202 if (f.getFilterType() == Filter.FILTER_TYPE_AND) 203 { 204 b.append('&'); 205 } 206 else 207 { 208 b.append('|'); 209 } 210 211 final TreeSet<Filter> compSet = 212 new TreeSet<>(FilterComparator.getInstance()); 213 compSet.addAll(Arrays.asList(f.getComponents())); 214 for (final Filter fc : compSet) 215 { 216 b.append(new GenericFilter(fc).toString()); 217 } 218 } 219 220 221 222 /** 223 * Retrieves a hash code for this generic filter. 224 * 225 * @return A hash code for this generic filter. 226 */ 227 @Override() 228 public int hashCode() 229 { 230 return hashCode; 231 } 232 233 234 235 /** 236 * Indicates whether the provided object is equal to this generic filter. 237 * 238 * @param o The object for which to make the determination. 239 * 240 * @return {@code true} the provided object is equal to this generic filter, 241 * or {@code false} if not. 242 */ 243 @Override() 244 public boolean equals(@Nullable final Object o) 245 { 246 if (o == null) 247 { 248 return false; 249 } 250 251 if (o == this) 252 { 253 return true; 254 } 255 256 return ((o instanceof GenericFilter) && 257 filterString.equals(((GenericFilter) o).filterString)); 258 } 259 260 261 262 /** 263 * Retrieves a string representation of this generic filter. 264 * 265 * @return A string representation of this generic filter. 266 */ 267 @Override() 268 @NotNull() 269 public String toString() 270 { 271 return filterString; 272 } 273}