001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2014 Manuel Gaupp 016 * Portions Copyright 2014-2016 ForgeRock AS. 017 */ 018package org.opends.server.controls; 019 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.List; 023 024import org.forgerock.i18n.LocalizableMessage; 025import org.forgerock.i18n.slf4j.LocalizedLogger; 026import org.forgerock.opendj.io.ASN1Reader; 027import org.forgerock.opendj.io.ASN1Writer; 028import org.forgerock.opendj.ldap.Assertion; 029import org.forgerock.opendj.ldap.ByteString; 030import org.forgerock.opendj.ldap.DecodeException; 031import org.forgerock.opendj.ldap.schema.AttributeType; 032import org.forgerock.opendj.ldap.schema.MatchingRule; 033import org.forgerock.opendj.ldap.schema.UnknownSchemaElementException; 034import org.forgerock.util.Reject; 035import org.opends.server.core.DirectoryServer; 036import org.opends.server.protocols.ldap.LDAPResultCode; 037import org.opends.server.types.LDAPException; 038import org.opends.server.types.RawFilter; 039 040import static org.opends.messages.ProtocolMessages.*; 041import static org.opends.server.protocols.ldap.LDAPConstants.*; 042import static org.opends.server.util.StaticUtils.*; 043 044/** 045 * This class defines a filter that may be used in conjunction with the matched 046 * values control to indicate which particular values of a multivalued attribute 047 * should be returned. The matched values filter is essentially a subset of an 048 * LDAP search filter, lacking support for AND, OR, and NOT components, and 049 * lacking support for the dnAttributes component of extensible matching 050 * filters. 051 */ 052public class MatchedValuesFilter 053{ 054 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 055 056 /** The BER type associated with the equalityMatch filter type. */ 057 public static final byte EQUALITY_MATCH_TYPE = (byte) 0xA3; 058 /** The BER type associated with the substrings filter type. */ 059 public static final byte SUBSTRINGS_TYPE = (byte) 0xA4; 060 /** The BER type associated with the greaterOrEqual filter type. */ 061 public static final byte GREATER_OR_EQUAL_TYPE = (byte) 0xA5; 062 /** The BER type associated with the lessOrEqual filter type. */ 063 public static final byte LESS_OR_EQUAL_TYPE = (byte) 0xA6; 064 /** The BER type associated with the present filter type. */ 065 public static final byte PRESENT_TYPE = (byte) 0x87; 066 /** The BER type associated with the approxMatch filter type. */ 067 public static final byte APPROXIMATE_MATCH_TYPE = (byte) 0xA8; 068 /** The BER type associated with the extensibleMatch filter type. */ 069 public static final byte EXTENSIBLE_MATCH_TYPE = (byte) 0xA9; 070 071 /** The matching rule ID for this matched values filter. */ 072 private final String matchingRuleID; 073 /** Indicates whether the elements of this matched values filter have been fully decoded. */ 074 private boolean decoded; 075 /** The match type for this matched values filter. */ 076 private final byte matchType; 077 078 /** The raw, unprocessed attribute type for this matched values filter. */ 079 private final String rawAttributeType; 080 /** The processed attribute type for this matched values filter. */ 081 private AttributeType attributeType; 082 083 /** The matching rule for this matched values filter. */ 084 private MatchingRule matchingRule; 085 /** The equality matching rule for this matched values filter. */ 086 private MatchingRule equalityMatchingRule; 087 /** The ordering matching rule for this matched values filter. */ 088 private MatchingRule orderingMatchingRule; 089 /** The substring matching rule for this matched values filter. */ 090 private MatchingRule substringMatchingRule; 091 /** The approximate matching rule for this matched values filter. */ 092 private MatchingRule approximateMatchingRule; 093 094 /** The raw, unprocessed assertion value for this matched values filter. */ 095 private final ByteString rawAssertionValue; 096 /** The processed assertion value for this matched values filter. */ 097 private ByteString assertionValue; 098 /** The assertion created from substring matching rule using values of this filter. */ 099 private Assertion substringAssertion; 100 /** The subInitial value for this matched values filter. */ 101 private final ByteString subInitial; 102 /** The set of subAny values for this matched values filter. */ 103 private final List<ByteString> subAny; 104 /** The subFinal value for this matched values filter. */ 105 private final ByteString subFinal; 106 107 /** 108 * Creates a new matched values filter with the provided information. 109 * 110 * @param matchType The match type for this matched values filter. 111 * @param rawAttributeType The raw, unprocessed attribute type. 112 * @param rawAssertionValue The raw, unprocessed assertion value. 113 * @param subInitial The subInitial element. 114 * @param subAny The set of subAny elements. 115 * @param subFinal The subFinal element. 116 * @param matchingRuleID The matching rule ID. 117 */ 118 private MatchedValuesFilter(byte matchType, String rawAttributeType, 119 ByteString rawAssertionValue, 120 ByteString subInitial, List<ByteString> subAny, 121 ByteString subFinal, String matchingRuleID) 122 { 123 this.matchType = matchType; 124 this.rawAttributeType = rawAttributeType; 125 this.rawAssertionValue = rawAssertionValue; 126 this.subInitial = subInitial; 127 this.subAny = subAny; 128 this.subFinal = subFinal; 129 this.matchingRuleID = matchingRuleID; 130 } 131 132 133 134 /** 135 * Creates a new equalityMatch filter with the provided information. 136 * 137 * @param rawAttributeType The raw, unprocessed attribute type. 138 * @param rawAssertionValue The raw, unprocessed assertion value. 139 * 140 * @return The created equalityMatch filter. 141 */ 142 public static MatchedValuesFilter createEqualityFilter( 143 String rawAttributeType, 144 ByteString rawAssertionValue) 145 { 146 Reject.ifNull(rawAttributeType,rawAssertionValue); 147 148 return new MatchedValuesFilter(EQUALITY_MATCH_TYPE, rawAttributeType, 149 rawAssertionValue, null, null, null, null); 150 } 151 152 153 154 /** 155 * Creates a new equalityMatch filter with the provided information. 156 * 157 * @param attributeType The attribute type. 158 * @param assertionValue The assertion value. 159 * 160 * @return The created equalityMatch filter. 161 */ 162 public static MatchedValuesFilter createEqualityFilter( 163 AttributeType attributeType, 164 ByteString assertionValue) 165 { 166 Reject.ifNull(attributeType, assertionValue); 167 String rawAttributeType = attributeType.getNameOrOID(); 168 169 MatchedValuesFilter filter = 170 new MatchedValuesFilter(EQUALITY_MATCH_TYPE, rawAttributeType, 171 assertionValue, null, null, null, null); 172 filter.attributeType = attributeType; 173 filter.assertionValue = assertionValue; 174 175 return filter; 176 } 177 178 179 180 /** 181 * Creates a new substrings filter with the provided information. 182 * 183 * @param rawAttributeType The raw, unprocessed attribute type. 184 * @param subInitial The subInitial element. 185 * @param subAny The set of subAny elements. 186 * @param subFinal The subFinal element. 187 * 188 * @return The created substrings filter. 189 */ 190 public static MatchedValuesFilter createSubstringsFilter( 191 String rawAttributeType, 192 ByteString subInitial, 193 List<ByteString> subAny, 194 ByteString subFinal) 195 { 196 Reject.ifNull(rawAttributeType); 197 return new MatchedValuesFilter(SUBSTRINGS_TYPE, rawAttributeType, null, 198 subInitial, subAny, subFinal, null); 199 } 200 201 202 203 /** 204 * Creates a new substrings filter with the provided information. 205 * 206 * @param attributeType The raw, unprocessed attribute type. 207 * @param subInitial The subInitial element. 208 * @param subAny The set of subAny elements. 209 * @param subFinal The subFinal element. 210 * 211 * @return The created substrings filter. 212 */ 213 public static MatchedValuesFilter createSubstringsFilter( 214 AttributeType attributeType, 215 ByteString subInitial, 216 List<ByteString> subAny, 217 ByteString subFinal) 218 { 219 Reject.ifNull(attributeType); 220 String rawAttributeType = attributeType.getNameOrOID(); 221 222 MatchedValuesFilter filter = 223 new MatchedValuesFilter(SUBSTRINGS_TYPE, rawAttributeType, null, 224 subInitial, subAny, subFinal, null); 225 filter.attributeType = attributeType; 226 227 return filter; 228 } 229 230 231 232 /** 233 * Creates a new greaterOrEqual filter with the provided information. 234 * 235 * @param rawAttributeType The raw, unprocessed attribute type. 236 * @param rawAssertionValue The raw, unprocessed assertion value. 237 * 238 * @return The created greaterOrEqual filter. 239 */ 240 public static MatchedValuesFilter createGreaterOrEqualFilter( 241 String rawAttributeType, 242 ByteString rawAssertionValue) 243 { 244 Reject.ifNull(rawAttributeType, rawAssertionValue); 245 246 return new MatchedValuesFilter(GREATER_OR_EQUAL_TYPE, rawAttributeType, 247 rawAssertionValue, null, null, null, null); 248 } 249 250 251 252 /** 253 * Creates a new greaterOrEqual filter with the provided information. 254 * 255 * @param attributeType The attribute type. 256 * @param assertionValue The assertion value. 257 * 258 * @return The created greaterOrEqual filter. 259 */ 260 public static MatchedValuesFilter createGreaterOrEqualFilter( 261 AttributeType attributeType, 262 ByteString assertionValue) 263 { 264 Reject.ifNull(attributeType, assertionValue); 265 266 String rawAttributeType = attributeType.getNameOrOID(); 267 268 MatchedValuesFilter filter = 269 new MatchedValuesFilter(GREATER_OR_EQUAL_TYPE, rawAttributeType, 270 assertionValue, null, null, null, null); 271 filter.attributeType = attributeType; 272 filter.assertionValue = assertionValue; 273 274 return filter; 275 } 276 277 278 279 /** 280 * Creates a new lessOrEqual filter with the provided information. 281 * 282 * @param rawAttributeType The raw, unprocessed attribute type. 283 * @param rawAssertionValue The raw, unprocessed assertion value. 284 * 285 * @return The created lessOrEqual filter. 286 */ 287 public static MatchedValuesFilter createLessOrEqualFilter( 288 String rawAttributeType, 289 ByteString rawAssertionValue) 290 { 291 Reject.ifNull(rawAttributeType, rawAssertionValue); 292 return new MatchedValuesFilter(LESS_OR_EQUAL_TYPE, rawAttributeType, 293 rawAssertionValue, null, null, null, null); 294 } 295 296 297 298 /** 299 * Creates a new lessOrEqual filter with the provided information. 300 * 301 * @param attributeType The attribute type. 302 * @param assertionValue The assertion value. 303 * 304 * @return The created lessOrEqual filter. 305 */ 306 public static MatchedValuesFilter createLessOrEqualFilter( 307 AttributeType attributeType, 308 ByteString assertionValue) 309 { 310 Reject.ifNull(attributeType, assertionValue); 311 312 String rawAttributeType = attributeType.getNameOrOID(); 313 314 MatchedValuesFilter filter = 315 new MatchedValuesFilter(LESS_OR_EQUAL_TYPE, rawAttributeType, 316 assertionValue, null, null, null, null); 317 filter.attributeType = attributeType; 318 filter.assertionValue = assertionValue; 319 320 return filter; 321 } 322 323 324 325 /** 326 * Creates a new present filter with the provided information. 327 * 328 * @param rawAttributeType The raw, unprocessed attribute type. 329 * 330 * @return The created present filter. 331 */ 332 public static MatchedValuesFilter createPresentFilter(String rawAttributeType) 333 { 334 Reject.ifNull(rawAttributeType) ; 335 return new MatchedValuesFilter(PRESENT_TYPE, rawAttributeType, null, null, 336 null, null, null); 337 } 338 339 340 341 /** 342 * Creates a new present filter with the provided information. 343 * 344 * @param attributeType The attribute type. 345 * 346 * @return The created present filter. 347 */ 348 public static MatchedValuesFilter createPresentFilter( 349 AttributeType attributeType) 350 { 351 Reject.ifNull(attributeType); 352 String rawAttributeType = attributeType.getNameOrOID(); 353 354 MatchedValuesFilter filter = 355 new MatchedValuesFilter(PRESENT_TYPE, rawAttributeType, null, null, 356 null, null, null); 357 filter.attributeType = attributeType; 358 359 return filter; 360 } 361 362 363 364 /** 365 * Creates a new approxMatch filter with the provided information. 366 * 367 * @param rawAttributeType The raw, unprocessed attribute type. 368 * @param rawAssertionValue The raw, unprocessed assertion value. 369 * 370 * @return The created approxMatch filter. 371 */ 372 public static MatchedValuesFilter createApproximateFilter( 373 String rawAttributeType, 374 ByteString rawAssertionValue) 375 { 376 Reject.ifNull(rawAttributeType,rawAssertionValue); 377 378 return new MatchedValuesFilter(APPROXIMATE_MATCH_TYPE, rawAttributeType, 379 rawAssertionValue, null, null, null, null); 380 } 381 382 383 384 /** 385 * Creates a new approxMatch filter with the provided information. 386 * 387 * @param attributeType The attribute type. 388 * @param assertionValue The assertion value. 389 * 390 * @return The created approxMatch filter. 391 */ 392 public static MatchedValuesFilter createApproximateFilter( 393 AttributeType attributeType, 394 ByteString assertionValue) 395 { 396 Reject.ifNull(attributeType,assertionValue); 397 String rawAttributeType = attributeType.getNameOrOID(); 398 399 MatchedValuesFilter filter = 400 new MatchedValuesFilter(APPROXIMATE_MATCH_TYPE, rawAttributeType, 401 assertionValue, null, null, null, null); 402 filter.attributeType = attributeType; 403 filter.assertionValue = assertionValue; 404 405 return filter; 406 } 407 408 409 410 /** 411 * Creates a new extensibleMatch filter with the provided information. 412 * 413 * @param rawAttributeType The raw, unprocessed attribute type. 414 * @param matchingRuleID The matching rule ID. 415 * @param rawAssertionValue The raw, unprocessed assertion value. 416 * 417 * @return The created extensibleMatch filter. 418 */ 419 public static MatchedValuesFilter createExtensibleMatchFilter( 420 String rawAttributeType, 421 String matchingRuleID, 422 ByteString rawAssertionValue) 423 { 424 Reject.ifNull(rawAttributeType, matchingRuleID, rawAssertionValue); 425 return new MatchedValuesFilter(EXTENSIBLE_MATCH_TYPE, rawAttributeType, 426 rawAssertionValue, null, null, null, 427 matchingRuleID); 428 } 429 430 431 432 /** 433 * Creates a new extensibleMatch filter with the provided information. 434 * 435 * @param attributeType The attribute type. 436 * @param matchingRule The matching rule. 437 * @param assertionValue The assertion value. 438 * 439 * @return The created extensibleMatch filter. 440 */ 441 public static MatchedValuesFilter createExtensibleMatchFilter( 442 AttributeType attributeType, 443 MatchingRule matchingRule, 444 ByteString assertionValue) 445 { 446 Reject.ifNull(attributeType, matchingRule, assertionValue); 447 String rawAttributeType = attributeType.getNameOrOID(); 448 String matchingRuleID = matchingRule.getOID(); 449 450 MatchedValuesFilter filter = 451 new MatchedValuesFilter(EXTENSIBLE_MATCH_TYPE, rawAttributeType, 452 assertionValue, null, null, null, 453 matchingRuleID); 454 filter.attributeType = attributeType; 455 filter.assertionValue = assertionValue; 456 filter.matchingRule = matchingRule; 457 458 return filter; 459 } 460 461 462 463 /** 464 * Creates a new matched values filter from the provided LDAP filter. 465 * 466 * @param filter The LDAP filter to use for this matched values filter. 467 * 468 * @return The corresponding matched values filter. 469 * 470 * @throws LDAPException If the provided LDAP filter cannot be treated as a 471 * matched values filter. 472 */ 473 public static MatchedValuesFilter createFromLDAPFilter(RawFilter filter) 474 throws LDAPException 475 { 476 switch (filter.getFilterType()) 477 { 478 case AND: 479 case OR: 480 case NOT: 481 // These filter types cannot be used in a matched values filter. 482 LocalizableMessage message = ERR_MVFILTER_INVALID_LDAP_FILTER_TYPE.get( 483 filter, filter.getFilterType()); 484 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 485 486 487 case EQUALITY: 488 return new MatchedValuesFilter(EQUALITY_MATCH_TYPE, 489 filter.getAttributeType(), 490 filter.getAssertionValue(), null, null, 491 null, null); 492 493 494 case SUBSTRING: 495 return new MatchedValuesFilter(SUBSTRINGS_TYPE, 496 filter.getAttributeType(), null, 497 filter.getSubInitialElement(), 498 filter.getSubAnyElements(), 499 filter.getSubFinalElement(), null); 500 501 502 case GREATER_OR_EQUAL: 503 return new MatchedValuesFilter(GREATER_OR_EQUAL_TYPE, 504 filter.getAttributeType(), 505 filter.getAssertionValue(), null, null, 506 null, null); 507 508 509 case LESS_OR_EQUAL: 510 return new MatchedValuesFilter(LESS_OR_EQUAL_TYPE, 511 filter.getAttributeType(), 512 filter.getAssertionValue(), null, null, 513 null, null); 514 515 516 case PRESENT: 517 return new MatchedValuesFilter(PRESENT_TYPE, filter.getAttributeType(), 518 null, null, null, null, null); 519 520 521 case APPROXIMATE_MATCH: 522 return new MatchedValuesFilter(APPROXIMATE_MATCH_TYPE, 523 filter.getAttributeType(), 524 filter.getAssertionValue(), null, null, 525 null, null); 526 527 528 case EXTENSIBLE_MATCH: 529 if (filter.getDNAttributes()) 530 { 531 // This cannot be represented in a matched values filter. 532 message = ERR_MVFILTER_INVALID_DN_ATTRIBUTES_FLAG.get(filter); 533 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 534 } 535 else 536 { 537 return new MatchedValuesFilter(EXTENSIBLE_MATCH_TYPE, 538 filter.getAttributeType(), 539 filter.getAssertionValue(), null, null, 540 null, filter.getMatchingRuleID()); 541 } 542 543 544 default: 545 message = ERR_MVFILTER_INVALID_LDAP_FILTER_TYPE.get(filter, filter.getFilterType()); 546 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 547 } 548 } 549 550 /** 551 * Encodes this matched values filter as an ASN.1 element. 552 * 553 * @param writer The ASN1Writer to use to encode this matched values filter. 554 * @throws IOException if an error occurs while encoding. 555 */ 556 public void encode(ASN1Writer writer) throws IOException 557 { 558 switch (matchType) 559 { 560 case EQUALITY_MATCH_TYPE: 561 case GREATER_OR_EQUAL_TYPE: 562 case LESS_OR_EQUAL_TYPE: 563 case APPROXIMATE_MATCH_TYPE: 564 // These will all be encoded in the same way. 565 writer.writeStartSequence(matchType); 566 writer.writeOctetString(rawAttributeType); 567 writer.writeOctetString(rawAssertionValue); 568 writer.writeEndSequence(); 569 return; 570 571 case SUBSTRINGS_TYPE: 572 writer.writeStartSequence(matchType); 573 writer.writeOctetString(rawAttributeType); 574 575 writer.writeStartSequence(); 576 if (subInitial != null) 577 { 578 writer.writeOctetString(TYPE_SUBINITIAL, subInitial); 579 } 580 581 if (subAny != null) 582 { 583 for (ByteString s : subAny) 584 { 585 writer.writeOctetString(TYPE_SUBANY, s); 586 } 587 } 588 589 if (subFinal != null) 590 { 591 writer.writeOctetString(TYPE_SUBFINAL, subFinal); 592 } 593 writer.writeEndSequence(); 594 595 writer.writeEndSequence(); 596 return; 597 598 case PRESENT_TYPE: 599 writer.writeOctetString(matchType, rawAttributeType); 600 return; 601 602 case EXTENSIBLE_MATCH_TYPE: 603 writer.writeStartSequence(matchType); 604 if (matchingRuleID != null) 605 { 606 writer.writeOctetString(TYPE_MATCHING_RULE_ID, matchingRuleID); 607 } 608 609 if (rawAttributeType != null) 610 { 611 writer.writeOctetString(TYPE_MATCHING_RULE_TYPE, rawAttributeType); 612 } 613 writer.writeOctetString(TYPE_MATCHING_RULE_VALUE, rawAssertionValue); 614 writer.writeEndSequence(); 615 return; 616 617 618 default: 619 } 620 } 621 622 /** 623 * Decodes the provided ASN.1 element as a matched values filter item. 624 * 625 * @param reader The ASN.1 reader. 626 * 627 * @return The decoded matched values filter. 628 * 629 * @throws LDAPException If a problem occurs while attempting to decode the 630 * filter item. 631 */ 632 public static MatchedValuesFilter decode(ASN1Reader reader) 633 throws LDAPException 634 { 635 byte type; 636 try 637 { 638 type = reader.peekType(); 639 } 640 catch(Exception e) 641 { 642 // TODO: Need a better message. 643 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, 644 ERR_MVFILTER_INVALID_ELEMENT_TYPE.get(e)); 645 } 646 647 switch (type) 648 { 649 case EQUALITY_MATCH_TYPE: 650 case GREATER_OR_EQUAL_TYPE: 651 case LESS_OR_EQUAL_TYPE: 652 case APPROXIMATE_MATCH_TYPE: 653 // These will all be decoded in the same manner. The element must be a 654 // sequence consisting of the attribute type and assertion value. 655 try 656 { 657 reader.readStartSequence(); 658 String rawAttributeType = reader.readOctetStringAsString(); 659 ByteString rawAssertionValue = reader.readOctetString(); 660 reader.readEndSequence(); 661 return new MatchedValuesFilter(type, rawAttributeType, 662 rawAssertionValue, null, null, null, null); 663 } 664 catch (Exception e) 665 { 666 logger.traceException(e); 667 668 LocalizableMessage message = 669 ERR_MVFILTER_CANNOT_DECODE_AVA.get(getExceptionMessage(e)); 670 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e); 671 } 672 673 674 case SUBSTRINGS_TYPE: 675 // This must be a sequence of two elements, where the second is a 676 // sequence of substring types. 677 try 678 { 679 reader.readStartSequence(); 680 String rawAttributeType = reader.readOctetStringAsString(); 681 682 reader.readStartSequence(); 683 if(!reader.hasNextElement()) 684 { 685 LocalizableMessage message = ERR_MVFILTER_NO_SUBSTRING_ELEMENTS.get(); 686 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 687 } 688 689 ByteString subInitial = null; 690 ArrayList<ByteString> subAny = null; 691 ByteString subFinal = null; 692 693 if(reader.hasNextElement() && 694 reader.peekType() == TYPE_SUBINITIAL) 695 { 696 subInitial = reader.readOctetString(); 697 } 698 while(reader.hasNextElement() && 699 reader.peekType() == TYPE_SUBANY) 700 { 701 if(subAny == null) 702 { 703 subAny = new ArrayList<>(); 704 } 705 subAny.add(reader.readOctetString()); 706 } 707 if(reader.hasNextElement() && 708 reader.peekType() == TYPE_SUBFINAL) 709 { 710 subFinal = reader.readOctetString(); 711 } 712 reader.readEndSequence(); 713 714 reader.readEndSequence(); 715 716 return new MatchedValuesFilter(type, rawAttributeType, 717 null, subInitial, subAny, subFinal, null); 718 } 719 catch (LDAPException le) 720 { 721 throw le; 722 } 723 catch (Exception e) 724 { 725 logger.traceException(e); 726 727 LocalizableMessage message = 728 ERR_MVFILTER_CANNOT_DECODE_SUBSTRINGS.get(getExceptionMessage(e)); 729 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e); 730 } 731 732 733 case PRESENT_TYPE: 734 // The element must be an ASN.1 octet string holding the attribute type. 735 try 736 { 737 String rawAttributeType = reader.readOctetStringAsString(); 738 739 return new MatchedValuesFilter(type, rawAttributeType, 740 null, null, null, null, null); 741 } 742 catch (Exception e) 743 { 744 logger.traceException(e); 745 746 LocalizableMessage message = ERR_MVFILTER_CANNOT_DECODE_PRESENT_TYPE.get( 747 getExceptionMessage(e)); 748 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e); 749 } 750 751 752 case EXTENSIBLE_MATCH_TYPE: 753 // This must be a two or three element sequence with an assertion value 754 // as the last element and an attribute type and/or matching rule ID as 755 // the first element(s). 756 try 757 { 758 reader.readStartSequence(); 759 760 String rawAttributeType = null; 761 String matchingRuleID = null; 762 763 if(reader.peekType() == TYPE_MATCHING_RULE_ID) 764 { 765 matchingRuleID = reader.readOctetStringAsString(); 766 } 767 if(matchingRuleID == null || 768 reader.peekType() == TYPE_MATCHING_RULE_TYPE) 769 { 770 rawAttributeType = reader.readOctetStringAsString(); 771 } 772 ByteString rawAssertionValue = reader.readOctetString(); 773 reader.readEndSequence(); 774 775 return new MatchedValuesFilter(type, rawAttributeType, 776 rawAssertionValue, null, null, null, 777 matchingRuleID); 778 } 779 catch (Exception e) 780 { 781 logger.traceException(e); 782 783 LocalizableMessage message = ERR_MVFILTER_CANNOT_DECODE_EXTENSIBLE_MATCH.get( 784 getExceptionMessage(e)); 785 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e); 786 } 787 788 789 default: 790 LocalizableMessage message = 791 ERR_MVFILTER_INVALID_ELEMENT_TYPE.get(byteToHex(type)); 792 throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message); 793 } 794 } 795 796 797 798 /** 799 * Retrieves the match type for this matched values filter. 800 * 801 * @return The match type for this matched values filter. 802 */ 803 public byte getMatchType() 804 { 805 return matchType; 806 } 807 808 809 810 /** 811 * Retrieves the raw, unprocessed attribute type for this matched values 812 * filter. 813 * 814 * @return The raw, unprocessed attribute type for this matched values 815 * filter, or <CODE>null</CODE> if there is none. 816 */ 817 public String getRawAttributeType() 818 { 819 return rawAttributeType; 820 } 821 822 823 /** 824 * Retrieves the attribute type for this matched values filter. 825 * 826 * @return The attribute type for this matched values filter, or 827 * <CODE>null</CODE> if there is none. 828 */ 829 public AttributeType getAttributeType() 830 { 831 if (attributeType == null && rawAttributeType != null) 832 { 833 attributeType = DirectoryServer.getSchema().getAttributeType(rawAttributeType); 834 } 835 return attributeType; 836 } 837 838 839 /** 840 * Retrieves the raw, unprocessed assertion value for this matched values 841 * filter. 842 * 843 * @return The raw, unprocessed assertion value for this matched values 844 * filter, or <CODE>null</CODE> if there is none. 845 */ 846 public ByteString getRawAssertionValue() 847 { 848 return rawAssertionValue; 849 } 850 851 852 853 /** 854 * Retrieves the assertion value for this matched values filter. 855 * 856 * @return The assertion value for this matched values filter, or 857 * <CODE>null</CODE> if there is none. 858 */ 859 public ByteString getAssertionValue() 860 { 861 if (assertionValue == null && rawAssertionValue != null) 862 { 863 assertionValue = rawAssertionValue; 864 } 865 return assertionValue; 866 } 867 868 869 870 /** 871 * Retrieves the subInitial element for this matched values filter. 872 * 873 * @return The subInitial element for this matched values filter, or 874 * <CODE>null</CODE> if there is none. 875 */ 876 public ByteString getSubInitialElement() 877 { 878 return subInitial; 879 } 880 881 private Assertion getSubstringAssertion() { 882 if (substringAssertion == null) 883 { 884 try 885 { 886 MatchingRule rule = getSubstringMatchingRule(); 887 if (rule != null) 888 { 889 substringAssertion = rule.getSubstringAssertion(subInitial, subAny, subFinal); 890 } 891 } 892 catch (DecodeException e) 893 { 894 logger.traceException(e); 895 } 896 } 897 return substringAssertion; 898 } 899 900 901 902 /** 903 * Retrieves the set of subAny elements for this matched values filter. 904 * 905 * @return The set of subAny elements for this matched values filter. If 906 * there are none, then the return value may be either 907 * <CODE>null</CODE> or an empty list. 908 */ 909 public List<ByteString> getSubAnyElements() 910 { 911 return subAny; 912 } 913 914 /** 915 * Retrieves the subFinal element for this matched values filter. 916 * 917 * @return The subFinal element for this matched values filter, or 918 * <CODE>null</CODE> if there is none. 919 */ 920 public ByteString getSubFinalElement() 921 { 922 return subFinal; 923 } 924 925 /** 926 * Retrieves the matching rule ID for this matched values filter. 927 * 928 * @return The matching rule ID for this matched values filter, or 929 * <CODE>null</CODE> if there is none. 930 */ 931 public String getMatchingRuleID() 932 { 933 return matchingRuleID; 934 } 935 936 937 938 /** 939 * Retrieves the matching rule for this matched values filter. 940 * 941 * @return The matching rule for this matched values filter, or 942 * <CODE>null</CODE> if there is none. 943 */ 944 public MatchingRule getMatchingRule() 945 { 946 if (matchingRule == null && matchingRuleID != null) 947 { 948 try 949 { 950 matchingRule = DirectoryServer.getSchema().getMatchingRule(matchingRuleID); 951 } 952 catch (UnknownSchemaElementException e) 953 { 954 } 955 } 956 return matchingRule; 957 } 958 959 960 961 /** 962 * Retrieves the approximate matching rule that should be used for this 963 * matched values filter. 964 * 965 * @return The approximate matching rule that should be used for this matched 966 * values filter, or <CODE>null</CODE> if there is none. 967 */ 968 public MatchingRule getApproximateMatchingRule() 969 { 970 if (approximateMatchingRule == null) 971 { 972 AttributeType attrType = getAttributeType(); 973 if (attrType != null) 974 { 975 approximateMatchingRule = attrType.getApproximateMatchingRule(); 976 } 977 } 978 979 return approximateMatchingRule; 980 } 981 982 983 984 /** 985 * Retrieves the equality matching rule that should be used for this matched 986 * values filter. 987 * 988 * @return The equality matching rule that should be used for this matched 989 * values filter, or <CODE>null</CODE> if there is none. 990 */ 991 public MatchingRule getEqualityMatchingRule() 992 { 993 if (equalityMatchingRule == null) 994 { 995 AttributeType attrType = getAttributeType(); 996 if (attrType != null) 997 { 998 equalityMatchingRule = attrType.getEqualityMatchingRule(); 999 } 1000 } 1001 1002 return equalityMatchingRule; 1003 } 1004 1005 1006 1007 /** 1008 * Retrieves the ordering matching rule that should be used for this matched 1009 * values filter. 1010 * 1011 * @return The ordering matching rule that should be used for this matched 1012 * values filter, or <CODE>null</CODE> if there is none. 1013 */ 1014 public MatchingRule getOrderingMatchingRule() 1015 { 1016 if (orderingMatchingRule == null) 1017 { 1018 AttributeType attrType = getAttributeType(); 1019 if (attrType != null) 1020 { 1021 orderingMatchingRule = attrType.getOrderingMatchingRule(); 1022 } 1023 } 1024 1025 return orderingMatchingRule; 1026 } 1027 1028 1029 1030 /** 1031 * Retrieves the substring matching rule that should be used for this matched 1032 * values filter. 1033 * 1034 * @return The substring matching rule that should be used for this matched 1035 * values filter, or <CODE>null</CODE> if there is none. 1036 */ 1037 public MatchingRule getSubstringMatchingRule() 1038 { 1039 if (substringMatchingRule == null) 1040 { 1041 AttributeType attrType = getAttributeType(); 1042 if (attrType != null) 1043 { 1044 substringMatchingRule = attrType.getSubstringMatchingRule(); 1045 } 1046 } 1047 1048 return substringMatchingRule; 1049 } 1050 1051 1052 1053 /** 1054 * Decodes all components of the matched values filter so that they can be 1055 * referenced as member variables. 1056 */ 1057 private void fullyDecode() 1058 { 1059 if (! decoded) 1060 { 1061 getAttributeType(); 1062 getAssertionValue(); 1063 getSubstringAssertion(); 1064 getMatchingRule(); 1065 getApproximateMatchingRule(); 1066 getEqualityMatchingRule(); 1067 getOrderingMatchingRule(); 1068 getSubstringMatchingRule(); 1069 decoded = true; 1070 } 1071 } 1072 1073 1074 1075 /** 1076 * Indicates whether the specified attribute value matches the criteria 1077 * defined in this matched values filter. 1078 * 1079 * @param type The attribute type with which the provided value is 1080 * associated. 1081 * @param value The attribute value for which to make the determination. 1082 * 1083 * @return <CODE>true</CODE> if the specified attribute value matches the 1084 * criteria defined in this matched values filter, or 1085 * <CODE>false</CODE> if not. 1086 */ 1087 public boolean valueMatches(AttributeType type, ByteString value) 1088 { 1089 fullyDecode(); 1090 1091 switch (matchType) 1092 { 1093 case EQUALITY_MATCH_TYPE: 1094 if (attributeType != null 1095 && attributeType.equals(type) 1096 && rawAssertionValue != null 1097 && value != null 1098 && equalityMatchingRule != null) 1099 { 1100 return matches(equalityMatchingRule, value, rawAssertionValue); 1101 } 1102 return false; 1103 1104 1105 case SUBSTRINGS_TYPE: 1106 if (attributeType != null 1107 && attributeType.equals(type) 1108 && substringAssertion != null) 1109 { 1110 try 1111 { 1112 return substringAssertion.matches(substringMatchingRule.normalizeAttributeValue(value)).toBoolean(); 1113 } 1114 catch (Exception e) 1115 { 1116 logger.traceException(e); 1117 } 1118 } 1119 return false; 1120 1121 1122 case GREATER_OR_EQUAL_TYPE: 1123 if (attributeType != null 1124 && attributeType.equals(type) 1125 && assertionValue != null 1126 && value != null 1127 && orderingMatchingRule != null) 1128 { 1129 try 1130 { 1131 ByteString normValue = orderingMatchingRule.normalizeAttributeValue(value); 1132 Assertion assertion = orderingMatchingRule.getGreaterOrEqualAssertion(assertionValue); 1133 return assertion.matches(normValue).toBoolean(); 1134 } 1135 catch (DecodeException e) 1136 { 1137 logger.traceException(e); 1138 } 1139 } 1140 return false; 1141 1142 1143 case LESS_OR_EQUAL_TYPE: 1144 if (attributeType != null 1145 && attributeType.equals(type) 1146 && assertionValue != null 1147 && value != null 1148 && orderingMatchingRule != null) 1149 { 1150 try 1151 { 1152 ByteString normValue = orderingMatchingRule.normalizeAttributeValue(value); 1153 Assertion assertion = orderingMatchingRule.getLessOrEqualAssertion(assertionValue); 1154 return assertion.matches(normValue).toBoolean(); 1155 } 1156 catch (DecodeException e) 1157 { 1158 logger.traceException(e); 1159 } 1160 } 1161 return false; 1162 1163 1164 case PRESENT_TYPE: 1165 return attributeType != null && attributeType.equals(type); 1166 1167 1168 case APPROXIMATE_MATCH_TYPE: 1169 if (attributeType != null 1170 && attributeType.equals(type) 1171 && assertionValue != null 1172 && value != null 1173 && approximateMatchingRule != null) 1174 { 1175 return matches(approximateMatchingRule, value, assertionValue); 1176 } 1177 return false; 1178 1179 1180 case EXTENSIBLE_MATCH_TYPE: 1181 if (attributeType == null) 1182 { 1183 return matches(matchingRule, value, assertionValue); 1184 } 1185 else if (!attributeType.equals(type)) 1186 { 1187 return false; 1188 } 1189 return matches(equalityMatchingRule, value, rawAssertionValue); 1190 1191 1192 default: 1193 return false; 1194 } 1195 } 1196 1197 private boolean matches(MatchingRule matchingRule, ByteString value, ByteString assertionValue) 1198 { 1199 if (matchingRule == null || value == null || assertionValue == null) 1200 { 1201 return false; 1202 } 1203 1204 try 1205 { 1206 ByteString normValue = matchingRule.normalizeAttributeValue(value); 1207 Assertion assertion = matchingRule.getAssertion(assertionValue); 1208 return assertion.matches(normValue).toBoolean(); 1209 } 1210 catch (DecodeException e) 1211 { 1212 logger.traceException(e); 1213 return false; 1214 } 1215 } 1216 1217 /** 1218 * Retrieves a string representation of this matched values filter, as an RFC 1219 * 2254-compliant filter string. 1220 * 1221 * @return A string representation of this matched values filter. 1222 */ 1223 @Override 1224 public String toString() 1225 { 1226 StringBuilder buffer = new StringBuilder(); 1227 toString(buffer); 1228 return buffer.toString(); 1229 } 1230 1231 1232 1233 /** 1234 * Appends a string representation of this matched values filter, as an RFC 1235 * 2254-compliant filter string, to the provided buffer. 1236 * 1237 * @param buffer The buffer to which the filter string should be appended. 1238 */ 1239 public void toString(StringBuilder buffer) 1240 { 1241 switch (matchType) 1242 { 1243 case EQUALITY_MATCH_TYPE: 1244 appendAttributeTypeAndAssertion(buffer, "="); 1245 break; 1246 1247 1248 case SUBSTRINGS_TYPE: 1249 buffer.append("("); 1250 buffer.append(rawAttributeType); 1251 buffer.append("="); 1252 if (subInitial != null) 1253 { 1254 RawFilter.valueToFilterString(buffer, subInitial); 1255 } 1256 1257 if (subAny != null) 1258 { 1259 for (ByteString s : subAny) 1260 { 1261 buffer.append("*"); 1262 RawFilter.valueToFilterString(buffer, s); 1263 } 1264 } 1265 1266 buffer.append("*"); 1267 if (subFinal != null) 1268 { 1269 RawFilter.valueToFilterString(buffer, subFinal); 1270 } 1271 buffer.append(")"); 1272 break; 1273 1274 1275 case GREATER_OR_EQUAL_TYPE: 1276 appendAttributeTypeAndAssertion(buffer, ">="); 1277 break; 1278 1279 1280 case LESS_OR_EQUAL_TYPE: 1281 appendAttributeTypeAndAssertion(buffer, "<="); 1282 break; 1283 1284 1285 case PRESENT_TYPE: 1286 buffer.append("("); 1287 buffer.append(rawAttributeType); 1288 buffer.append("=*)"); 1289 break; 1290 1291 1292 case APPROXIMATE_MATCH_TYPE: 1293 appendAttributeTypeAndAssertion(buffer, "~="); 1294 break; 1295 1296 1297 case EXTENSIBLE_MATCH_TYPE: 1298 buffer.append("("); 1299 1300 if (rawAttributeType != null) 1301 { 1302 buffer.append(rawAttributeType); 1303 } 1304 1305 if (matchingRuleID != null) 1306 { 1307 buffer.append(":"); 1308 buffer.append(matchingRuleID); 1309 } 1310 1311 buffer.append(":="); 1312 RawFilter.valueToFilterString(buffer, rawAssertionValue); 1313 buffer.append(")"); 1314 break; 1315 } 1316 } 1317 1318 private void appendAttributeTypeAndAssertion(StringBuilder buffer, String operator) 1319 { 1320 buffer.append("("); 1321 buffer.append(rawAttributeType); 1322 buffer.append(operator); 1323 RawFilter.valueToFilterString(buffer, rawAssertionValue); 1324 buffer.append(")"); 1325 } 1326} 1327