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.Iterator; 024 025 import org.apache.directory.server.i18n.I18n; 026 import org.apache.directory.server.xdbm.Index; 027 import org.apache.directory.server.xdbm.IndexEntry; 028 import org.apache.directory.server.xdbm.Store; 029 import org.apache.directory.server.xdbm.search.Evaluator; 030 import org.apache.directory.shared.ldap.entry.EntryAttribute; 031 import org.apache.directory.shared.ldap.entry.ServerEntry; 032 import org.apache.directory.shared.ldap.entry.Value; 033 import org.apache.directory.shared.ldap.filter.LessEqNode; 034 import org.apache.directory.shared.ldap.schema.AttributeType; 035 import org.apache.directory.shared.ldap.schema.LdapComparator; 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 LessEqNode 043 * assertions. 044 * 045 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 046 * @version $Rev$ 047 */ 048 public class LessEqEvaluator<T, ID> implements Evaluator<LessEqNode<T>, ServerEntry, ID> 049 { 050 private final LessEqNode<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 LdapComparator<? super Object> ldapComparator; 056 private final Index<T, ServerEntry, ID> idx; 057 058 059 @SuppressWarnings("unchecked") 060 public LessEqEvaluator( LessEqNode<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<T, 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_717, node ) ); 093 } 094 095 normalizer = mr.getNormalizer(); 096 ldapComparator = mr.getLdapComparator(); 097 } 098 099 100 public LessEqNode<T> 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 LdapComparator<? super Object> getLdapComparator() 119 { 120 return ldapComparator; 121 } 122 123 124 public boolean evaluateId( ID id ) throws Exception 125 { 126 if ( idx != null ) 127 { 128 return idx.reverseLessOrEq( id, node.getValue().get() ); 129 } 130 131 return evaluateEntry( db.lookup( id ) ); 132 } 133 134 135 public boolean evaluate( IndexEntry<?, ServerEntry, ID> indexEntry ) throws Exception 136 { 137 if ( idx != null ) 138 { 139 return idx.reverseLessOrEq( indexEntry.getId(), node.getValue().get() ); 140 } 141 142 ServerEntry entry = indexEntry.getObject(); 143 144 // resuscitate the entry if it has not been and set entry in IndexEntry 145 if ( null == entry ) 146 { 147 entry = db.lookup( indexEntry.getId() ); 148 indexEntry.setObject( entry ); 149 } 150 151 if ( null == entry ) 152 { 153 return false; 154 } 155 156 // get the attribute 157 EntryAttribute attr = entry.get( type ); 158 159 // if the attribute does not exist just return false 160 //noinspection unchecked 161 if ( attr != null && evaluate( ( IndexEntry<Object, ServerEntry, ID> ) indexEntry, attr ) ) 162 { 163 return true; 164 } 165 166 // If we do not have the attribute, loop through the sub classes of 167 // the attributeType. Perhaps the entry has an attribute value of a 168 // subtype (descendant) that will produce a match 169 if ( schemaManager.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) ) 170 { 171 // TODO check to see if descendant handling is necessary for the 172 // index so we can match properly even when for example a name 173 // attribute is used instead of more specific commonName 174 Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( 175 node.getAttribute() ); 176 177 while ( descendants.hasNext() ) 178 { 179 AttributeType descendant = descendants.next(); 180 181 attr = entry.get( descendant ); 182 183 //noinspection unchecked 184 if ( attr != null && evaluate( ( IndexEntry<Object, ServerEntry, ID> ) indexEntry, attr ) ) 185 { 186 return true; 187 } 188 } 189 } 190 191 // we fell through so a match was not found - assertion was false. 192 return false; 193 } 194 195 196 public boolean evaluateEntry( ServerEntry entry ) throws Exception 197 { 198 // get the attribute 199 EntryAttribute attr = entry.get( type ); 200 201 // if the attribute does not exist just return false 202 if ( attr != null && evaluate( null, attr ) ) 203 { 204 return true; 205 } 206 207 // If we do not have the attribute, loop through the sub classes of 208 // the attributeType. Perhaps the entry has an attribute value of a 209 // subtype (descendant) that will produce a match 210 if ( schemaManager.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) ) 211 { 212 // TODO check to see if descendant handling is necessary for the 213 // index so we can match properly even when for example a name 214 // attribute is used instead of more specific commonName 215 Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( 216 node.getAttribute() ); 217 218 while ( descendants.hasNext() ) 219 { 220 AttributeType descendant = descendants.next(); 221 222 attr = entry.get( descendant ); 223 224 if ( attr != null && evaluate( null, attr ) ) 225 { 226 return true; 227 } 228 } 229 } 230 231 // we fell through so a match was not found - assertion was false. 232 return false; 233 } 234 235 236 // TODO - determine if comaparator and index entry should have the Value 237 // wrapper or the raw normalized value 238 private boolean evaluate( IndexEntry<Object, ServerEntry, ID> indexEntry, EntryAttribute attribute ) 239 throws Exception 240 { 241 /* 242 * Cycle through the attribute values testing normalized version 243 * obtained from using the ordering or equality matching rule's 244 * normalizer. The test uses the comparator obtained from the 245 * appropriate matching rule to perform the check. 246 */ 247 for ( Value<?> value : attribute ) 248 { 249 value.normalize( normalizer ); 250 251 //noinspection unchecked 252 if ( ldapComparator.compare( value.getNormalizedValue(), node.getValue().getNormalizedValue() ) <= 0 ) 253 { 254 if ( indexEntry != null ) 255 { 256 indexEntry.setValue( value.getNormalizedValue() ); 257 } 258 return true; 259 } 260 } 261 262 return false; 263 } 264 }