001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.server.xdbm.search.impl; 021 022 023 import java.util.Comparator; 024 import java.util.Iterator; 025 026 import org.apache.directory.server.i18n.I18n; 027 import org.apache.directory.server.xdbm.Index; 028 import org.apache.directory.server.xdbm.IndexEntry; 029 import org.apache.directory.server.xdbm.Store; 030 import org.apache.directory.server.xdbm.search.Evaluator; 031 import org.apache.directory.shared.ldap.entry.EntryAttribute; 032 import org.apache.directory.shared.ldap.entry.ServerEntry; 033 import org.apache.directory.shared.ldap.entry.Value; 034 import org.apache.directory.shared.ldap.filter.GreaterEqNode; 035 import org.apache.directory.shared.ldap.schema.AttributeType; 036 import org.apache.directory.shared.ldap.schema.MatchingRule; 037 import org.apache.directory.shared.ldap.schema.Normalizer; 038 import org.apache.directory.shared.ldap.schema.SchemaManager; 039 040 041 /** 042 * An Evaluator which determines if candidates are matched by GreaterEqNode 043 * assertions. 044 * 045 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 046 * @version $Rev$ 047 */ 048 public class GreaterEqEvaluator<T, ID> implements Evaluator<GreaterEqNode<T>, ServerEntry, ID> 049 { 050 private final GreaterEqNode<T> node; 051 private final Store<ServerEntry, ID> db; 052 private final SchemaManager schemaManager; 053 private final AttributeType type; 054 private final Normalizer normalizer; 055 private final Comparator comparator; 056 private final Index<Object, ServerEntry, ID> idx; 057 058 059 @SuppressWarnings("unchecked") 060 public GreaterEqEvaluator( GreaterEqNode<T> node, Store<ServerEntry, ID> db, SchemaManager schemaManager ) 061 throws Exception 062 { 063 this.db = db; 064 this.node = node; 065 this.schemaManager = schemaManager; 066 this.type = schemaManager.lookupAttributeTypeRegistry( node.getAttribute() ); 067 068 if ( db.hasIndexOn( node.getAttribute() ) ) 069 { 070 idx = ( Index<Object, ServerEntry, ID> ) db.getIndex( node.getAttribute() ); 071 } 072 else 073 { 074 idx = null; 075 } 076 077 /* 078 * We prefer matching using the Normalizer and Comparator pair from 079 * the ordering matchingRule if one is available. It may very well 080 * not be. If so then we resort to using the Normalizer and 081 * Comparator from the equality matchingRule as a last resort. 082 */ 083 MatchingRule mr = type.getOrdering(); 084 085 if ( mr == null ) 086 { 087 mr = type.getEquality(); 088 } 089 090 if ( mr == null ) 091 { 092 throw new IllegalStateException( I18n.err( I18n.ERR_715, node ) ); 093 } 094 095 normalizer = mr.getNormalizer(); 096 comparator = mr.getLdapComparator(); 097 } 098 099 100 public GreaterEqNode getExpression() 101 { 102 return node; 103 } 104 105 106 public AttributeType getAttributeType() 107 { 108 return type; 109 } 110 111 112 public Normalizer getNormalizer() 113 { 114 return normalizer; 115 } 116 117 118 public Comparator getComparator() 119 { 120 return comparator; 121 } 122 123 124 public boolean evaluate( IndexEntry<?, ServerEntry, ID> indexEntry ) throws Exception 125 { 126 if ( idx != null ) 127 { 128 return idx.reverseGreaterOrEq( indexEntry.getId(), node.getValue().get() ); 129 } 130 131 ServerEntry entry = indexEntry.getObject(); 132 133 // resuscitate the entry if it has not been and set entry in IndexEntry 134 if ( null == entry ) 135 { 136 entry = db.lookup( indexEntry.getId() ); 137 indexEntry.setObject( entry ); 138 } 139 140 /* 141 * The code below could have been replaced by a call to 142 * evaluate( ServerEntry ) but it was not because we wanted to make 143 * sure the call to evaluate with the attribute was made using a 144 * non-null IndexEntry parameter. This is to make sure the call to 145 * evaluate with the attribute will set the value on the IndexEntry. 146 */ 147 148 // get the attribute 149 EntryAttribute attr = entry.get( type ); 150 151 // if the attribute exists and has a greater than or equal value return true 152 //noinspection unchecked 153 if ( attr != null && evaluate( ( IndexEntry<Object, ServerEntry, ID> ) indexEntry, attr ) ) 154 { 155 return true; 156 } 157 158 // If we do not have the attribute, loop through the sub classes of 159 // the attributeType. Perhaps the entry has an attribute value of a 160 // subtype (descendant) that will produce a match 161 if ( schemaManager.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) ) 162 { 163 // TODO check to see if descendant handling is necessary for the 164 // index so we can match properly even when for example a name 165 // attribute is used instead of more specific commonName 166 Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( 167 node.getAttribute() ); 168 169 while ( descendants.hasNext() ) 170 { 171 AttributeType descendant = descendants.next(); 172 173 attr = entry.get( descendant ); 174 175 //noinspection unchecked 176 if ( attr != null && evaluate( ( IndexEntry<Object, ServerEntry, ID> ) indexEntry, attr ) ) 177 { 178 return true; 179 } 180 } 181 } 182 183 // we fell through so a match was not found - assertion was false. 184 return false; 185 } 186 187 188 public boolean evaluateId( ID id ) throws Exception 189 { 190 if ( idx != null ) 191 { 192 return idx.reverseGreaterOrEq( id, node.getValue().get() ); 193 } 194 195 return evaluateEntry( db.lookup( id ) ); 196 } 197 198 199 public boolean evaluateEntry( ServerEntry entry ) throws Exception 200 { 201 // get the attribute 202 EntryAttribute attr = entry.get( type ); 203 204 // if the attribute exists and has a greater than or equal value return true 205 if ( attr != null && evaluate( null, attr ) ) 206 { 207 return true; 208 } 209 210 // If we do not have the attribute, loop through the sub classes of 211 // the attributeType. Perhaps the entry has an attribute value of a 212 // subtype (descendant) that will produce a match 213 if ( schemaManager.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) ) 214 { 215 // TODO check to see if descendant handling is necessary for the 216 // index so we can match properly even when for example a name 217 // attribute is used instead of more specific commonName 218 Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( 219 node.getAttribute() ); 220 221 while ( descendants.hasNext() ) 222 { 223 AttributeType descendant = descendants.next(); 224 225 attr = entry.get( descendant ); 226 227 if ( attr != null && evaluate( null, attr ) ) 228 { 229 return true; 230 } 231 } 232 } 233 234 // we fell through so a match was not found - assertion was false. 235 return false; 236 } 237 238 239 // TODO - determine if comaparator and index entry should have the Value 240 // wrapper or the raw normalized value 241 private boolean evaluate( IndexEntry<Object, ServerEntry, ID> indexEntry, EntryAttribute attribute ) 242 throws Exception 243 { 244 /* 245 * Cycle through the attribute values testing normalized version 246 * obtained from using the ordering or equality matching rule's 247 * normalizer. The test uses the comparator obtained from the 248 * appropriate matching rule to perform the check. 249 */ 250 for ( Value value : attribute ) 251 { 252 value.normalize( normalizer ); 253 254 //noinspection unchecked 255 if ( comparator.compare( value.getNormalizedValue(), node.getValue().getNormalizedValue() ) >= 0 ) 256 { 257 if ( indexEntry != null ) 258 { 259 indexEntry.setValue( value.getNormalizedValue() ); 260 } 261 return true; 262 } 263 } 264 265 return false; 266 } 267 }