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