001 /*
002 * The MIT License
003 * Copyright (c) 2012 Microsoft Corporation
004 *
005 * Permission is hereby granted, free of charge, to any person obtaining a copy
006 * of this software and associated documentation files (the "Software"), to deal
007 * in the Software without restriction, including without limitation the rights
008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009 * copies of the Software, and to permit persons to whom the Software is
010 * furnished to do so, subject to the following conditions:
011 *
012 * The above copyright notice and this permission notice shall be included in
013 * all copies or substantial portions of the Software.
014 *
015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021 * THE SOFTWARE.
022 */
023
024 package microsoft.exchange.webservices.data.search.filter;
025
026 import microsoft.exchange.webservices.data.attribute.EditorBrowsable;
027 import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
028 import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
029 import microsoft.exchange.webservices.data.core.XmlAttributeNames;
030 import microsoft.exchange.webservices.data.core.XmlElementNames;
031 import microsoft.exchange.webservices.data.core.enumeration.search.ComparisonMode;
032 import microsoft.exchange.webservices.data.core.enumeration.search.ContainmentMode;
033 import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState;
034 import microsoft.exchange.webservices.data.core.enumeration.search.LogicalOperator;
035 import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
036 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
037 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException;
038 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
039 import microsoft.exchange.webservices.data.misc.OutParam;
040 import microsoft.exchange.webservices.data.property.complex.ComplexProperty;
041 import microsoft.exchange.webservices.data.property.complex.IComplexPropertyChangedDelegate;
042 import microsoft.exchange.webservices.data.property.complex.ISearchStringProvider;
043 import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase;
044 import org.apache.commons.logging.Log;
045 import org.apache.commons.logging.LogFactory;
046
047 import javax.xml.stream.XMLStreamException;
048
049 import java.util.ArrayList;
050 import java.util.Iterator;
051
052 /**
053 * Represents the base search filter class. Use descendant search filter classes
054 * such as SearchFilter.IsEqualTo, SearchFilter.ContainsSubstring and
055 * SearchFilter.SearchFilterCollection to define search filter.
056 */
057 public abstract class SearchFilter extends ComplexProperty {
058
059 private static final Log LOG = LogFactory.getLog(SearchFilter.class);
060
061 /**
062 * Initializes a new instance of the SearchFilter class.
063 */
064 protected SearchFilter() {
065 }
066
067 /**
068 * The search.
069 *
070 * @param reader the reader
071 * @return the search filter
072 * @throws Exception the exception
073 */
074 //static SearchFilter search;
075
076 /**
077 * Loads from XML.
078 *
079 * @param reader the reader
080 * @return SearchFilter
081 * @throws Exception the exception
082 */
083 public static SearchFilter loadFromXml(EwsServiceXmlReader reader)
084 throws Exception {
085 reader.ensureCurrentNodeIsStartElement();
086
087 SearchFilter searchFilter = null;
088
089 if (reader.getLocalName().equalsIgnoreCase(XmlElementNames.Exists)) {
090 searchFilter = new Exists();
091 } else if (reader.getLocalName().equalsIgnoreCase(
092 XmlElementNames.Contains)) {
093 searchFilter = new ContainsSubstring();
094 } else if (reader.getLocalName().equalsIgnoreCase(
095 XmlElementNames.Excludes)) {
096 searchFilter = new ExcludesBitmask();
097 } else if (reader.getLocalName().equalsIgnoreCase(XmlElementNames.Not)) {
098 searchFilter = new Not();
099 } else if (reader.getLocalName().equalsIgnoreCase(XmlElementNames.And)) {
100 searchFilter = new SearchFilterCollection(
101 LogicalOperator.And);
102 } else if (reader.getLocalName().equalsIgnoreCase(XmlElementNames.Or)) {
103 searchFilter = new SearchFilterCollection(
104 LogicalOperator.Or);
105 } else if (reader.getLocalName().equalsIgnoreCase(
106 XmlElementNames.IsEqualTo)) {
107 searchFilter = new IsEqualTo();
108 } else if (reader.getLocalName().equalsIgnoreCase(
109 XmlElementNames.IsNotEqualTo)) {
110 searchFilter = new IsNotEqualTo();
111 } else if (reader.getLocalName().equalsIgnoreCase(
112 XmlElementNames.IsGreaterThan)) {
113 searchFilter = new IsGreaterThan();
114 } else if (reader.getLocalName().equalsIgnoreCase(
115 XmlElementNames.IsGreaterThanOrEqualTo)) {
116 searchFilter = new IsGreaterThanOrEqualTo();
117 } else if (reader.getLocalName().equalsIgnoreCase(
118 XmlElementNames.IsLessThan)) {
119 searchFilter = new IsLessThan();
120 } else if (reader.getLocalName().equalsIgnoreCase(
121 XmlElementNames.IsLessThanOrEqualTo)) {
122 searchFilter = new IsLessThanOrEqualTo();
123 } else {
124 searchFilter = null;
125 }
126
127 if (searchFilter != null) {
128 searchFilter.loadFromXml(reader, reader.getLocalName());
129 }
130
131 return searchFilter;
132 }
133
134 /**
135 * Gets the name of the XML element.
136 *
137 * @return the xml element name
138 */
139 protected abstract String getXmlElementName();
140
141 /**
142 * Writes to XML.
143 *
144 * @param writer the writer
145 * @throws Exception the exception
146 */
147 public void writeToXml(EwsServiceXmlWriter writer) throws Exception {
148 super.writeToXml(writer, this.getXmlElementName());
149 }
150
151 /**
152 * Represents a search filter that checks for the presence of a substring
153 * inside a text property. Applications can use ContainsSubstring to define
154 * conditions such as "Field CONTAINS Value" or
155 * "Field IS PREFIXED WITH Value".
156 */
157 public static final class ContainsSubstring extends PropertyBasedFilter {
158
159 /**
160 * The containment mode.
161 */
162 private ContainmentMode containmentMode = ContainmentMode.Substring;
163
164 /**
165 * The comparison mode.
166 */
167 private ComparisonMode comparisonMode = ComparisonMode.IgnoreCase;
168
169 /**
170 * The value.
171 */
172 private String value;
173
174 /**
175 * Initializes a new instance of the class.
176 */
177 public ContainsSubstring() {
178 super();
179 }
180
181 /**
182 * Initializes a new instance of the class.
183 *
184 * @param propertyDefinition The definition of the property that is being compared.
185 * @param value The value to compare with.
186 */
187 public ContainsSubstring(PropertyDefinitionBase propertyDefinition,
188 String value) {
189 super(propertyDefinition);
190 this.value = value;
191 }
192
193 /**
194 * Initializes a new instance of the class.
195 *
196 * @param propertyDefinition The definition of the property that is being compared.
197 * @param value The value to compare with.
198 * @param containmentMode The containment mode.
199 * @param comparisonMode The comparison mode.
200 */
201 public ContainsSubstring(PropertyDefinitionBase propertyDefinition,
202 String value, ContainmentMode containmentMode,
203 ComparisonMode comparisonMode) {
204 this(propertyDefinition, value);
205 this.containmentMode = containmentMode;
206 this.comparisonMode = comparisonMode;
207 }
208
209 /**
210 * validates instance.
211 *
212 * @throws ServiceValidationException the service validation exception
213 */
214 @Override
215 protected void internalValidate() throws ServiceValidationException {
216 super.internalValidate();
217 if ((this.value == null) || this.value.isEmpty()) {
218 throw new ServiceValidationException("The Value property must be set.");
219 }
220 }
221
222 /**
223 * Gets the name of the XML element.
224 *
225 * @return the xml element name
226 */
227 @Override
228 protected String getXmlElementName() {
229 return XmlElementNames.Contains;
230 }
231
232 /**
233 * Tries to read element from XML.
234 *
235 * @param reader the reader
236 * @return True if element was read.
237 * @throws Exception the exception
238 */
239 @Override
240 public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
241 throws Exception {
242 boolean result = super.tryReadElementFromXml(reader);
243
244 if (!result) {
245 if (reader.getLocalName().equals(XmlElementNames.Constant)) {
246 this.value = reader
247 .readAttributeValue(XmlAttributeNames.Value);
248 result = true;
249 }
250 }
251 return result;
252 }
253
254 /**
255 * Reads the attribute of Xml.
256 *
257 * @param reader the reader
258 * @throws Exception the exception
259 */
260 @Override
261 public void readAttributesFromXml(EwsServiceXmlReader reader)
262 throws Exception {
263
264 super.readAttributesFromXml(reader);
265 this.containmentMode = reader.readAttributeValue(
266 ContainmentMode.class, XmlAttributeNames.ContainmentMode);
267 try {
268 this.comparisonMode = reader.readAttributeValue(
269 ComparisonMode.class,
270 XmlAttributeNames.ContainmentComparison);
271 } catch (IllegalArgumentException ile) {
272 // This will happen if we receive a value that is defined in the
273 // EWS
274 // schema but that is not defined
275 // in the API. We map that
276 // value to IgnoreCaseAndNonSpacingCharacters.
277 this.comparisonMode = ComparisonMode.
278 IgnoreCaseAndNonSpacingCharacters;
279 }
280 }
281
282 /**
283 * Writes the attribute to XML.
284 *
285 * @param writer the writer
286 * @throws ServiceXmlSerializationException the service xml serialization exception
287 */
288 @Override
289 public void writeAttributesToXml(EwsServiceXmlWriter writer)
290 throws ServiceXmlSerializationException {
291 super.writeAttributesToXml(writer);
292
293 writer.writeAttributeValue(XmlAttributeNames.ContainmentMode,
294 this.containmentMode);
295 writer.writeAttributeValue(XmlAttributeNames.ContainmentComparison,
296 this.comparisonMode);
297 }
298
299 /**
300 * Writes the elements to Xml.
301 *
302 * @param writer the writer
303 * @throws XMLStreamException the XML stream exception
304 * @throws ServiceXmlSerializationException the service xml serialization exception
305 */
306 @Override
307 public void writeElementsToXml(EwsServiceXmlWriter writer)
308 throws XMLStreamException, ServiceXmlSerializationException {
309 super.writeElementsToXml(writer);
310
311 writer.writeStartElement(XmlNamespace.Types,
312 XmlElementNames.Constant);
313 writer.writeAttributeValue(XmlAttributeNames.Value, this.value);
314 writer.writeEndElement(); // Constant
315 }
316
317 /**
318 * Gets the containment mode.
319 *
320 * @return ContainmentMode
321 */
322 public ContainmentMode getContainmentMode() {
323 return containmentMode;
324 }
325
326 /**
327 * sets the ContainmentMode.
328 *
329 * @param containmentMode the new containment mode
330 */
331 public void setContainmentMode(ContainmentMode containmentMode) {
332 this.containmentMode = containmentMode;
333 }
334
335 /**
336 * Gets the comparison mode.
337 *
338 * @return ComparisonMode
339 */
340 public ComparisonMode getComparisonMode() {
341 return comparisonMode;
342 }
343
344 /**
345 * sets the comparison mode.
346 *
347 * @param comparisonMode the new comparison mode
348 */
349 public void setComparisonMode(ComparisonMode comparisonMode) {
350 this.comparisonMode = comparisonMode;
351 }
352
353 /**
354 * gets the value to compare the specified property with.
355 *
356 * @return String
357 */
358 public String getValue() {
359 return value;
360 }
361
362 /**
363 * sets the value to compare the specified property with.
364 *
365 * @param value the new value
366 */
367 public void setValue(String value) {
368 this.value = value;
369 }
370 }
371
372
373 /**
374 * Represents a bitmask exclusion search filter. Applications can use
375 * ExcludesBitExcludesBitmaskFilter to define conditions such as
376 * "(OrdinalField and 0x0010) != 0x0010"
377 */
378 public static class ExcludesBitmask extends PropertyBasedFilter {
379
380 /**
381 * The bitmask.
382 */
383 private int bitmask;
384
385 /**
386 * Initializes a new instance of the class.
387 */
388 public ExcludesBitmask() {
389 super();
390 }
391
392 /**
393 * Initializes a new instance of the class.
394 *
395 * @param propertyDefinition the property definition
396 * @param bitmask the bitmask
397 */
398 public ExcludesBitmask(PropertyDefinitionBase propertyDefinition,
399 int bitmask) {
400 super(propertyDefinition);
401 this.bitmask = bitmask;
402 }
403
404 /**
405 * Gets the name of the XML element.
406 *
407 * @return XML element name
408 */
409 @Override
410 public String getXmlElementName() {
411 return XmlElementNames.Excludes;
412 }
413
414 /**
415 * Tries to read element from XML.
416 *
417 * @param reader the reader
418 * @return true if element was read
419 * @throws Exception the exception
420 */
421 @Override
422 public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
423 throws Exception {
424 boolean result = super.tryReadElementFromXml(reader);
425
426 if (!result) {
427 if (reader.getLocalName().equals(XmlElementNames.Bitmask)) {
428 // EWS always returns the Bitmask value in hexadecimal
429 this.bitmask = Integer.parseInt(reader
430 .readAttributeValue(XmlAttributeNames.Value));
431 }
432 }
433
434 return result;
435 }
436
437 /**
438 * Writes the elements to XML.
439 *
440 * @param writer the writer
441 * @throws javax.xml.stream.XMLStreamException , ServiceXmlSerializationException
442 * @throws ServiceXmlSerializationException the service xml serialization exception
443 */
444 @Override
445 public void writeElementsToXml(EwsServiceXmlWriter writer)
446 throws XMLStreamException, ServiceXmlSerializationException {
447 super.writeElementsToXml(writer);
448
449 writer.writeStartElement(XmlNamespace.Types,
450 XmlElementNames.Bitmask);
451 writer.writeAttributeValue(XmlAttributeNames.Value, this.bitmask);
452 writer.writeEndElement(); // Bitmask
453 }
454
455 /**
456 * Gets the bitmask to compare the property with.
457 *
458 * @return bitmask
459 */
460 public int getBitmask() {
461 return bitmask;
462 }
463
464 /**
465 * Sets the bitmask to compare the property with.
466 *
467 * @param bitmask the new bitmask
468 */
469 public void setBitmask(int bitmask) {
470 this.bitmask = bitmask;
471 }
472
473 }
474
475
476 /**
477 * Represents a search filter checking if a field is set. Applications can
478 * use ExistsFilter to define conditions such as "Field IS SET".
479 */
480 public static final class Exists extends PropertyBasedFilter {
481
482 /**
483 * Initializes a new instance of the class.
484 */
485 public Exists() {
486 super();
487 }
488
489 /**
490 * Initializes a new instance of the class.
491 *
492 * @param propertyDefinition the property definition
493 */
494 public Exists(PropertyDefinitionBase propertyDefinition) {
495 super(propertyDefinition);
496 }
497
498 /**
499 * Gets the name of the XML element.
500 *
501 * @return the xml element name
502 */
503 @Override
504 protected String getXmlElementName() {
505 return XmlElementNames.Exists;
506 }
507 }
508
509
510 /**
511 * Represents a search filter that checks if a property is equal to a given
512 * value or other property.
513 */
514 public static class IsEqualTo extends RelationalFilter {
515
516 /**
517 * Initializes a new instance of the class.
518 */
519 public IsEqualTo() {
520 super();
521 }
522
523 /**
524 * Initializes a new instance of the class.
525 *
526 * @param propertyDefinition The definition of the property that is being compared.
527 * @param otherPropertyDefinition The definition of the property to compare with.
528 */
529 public IsEqualTo(PropertyDefinitionBase propertyDefinition,
530 PropertyDefinitionBase otherPropertyDefinition) {
531 super(propertyDefinition, otherPropertyDefinition);
532 }
533
534 /**
535 * Initializes a new instance of the class.
536 *
537 * @param propertyDefinition The definition of the property that is being compared.
538 * @param value The value of the property to compare with.
539 */
540 public IsEqualTo(PropertyDefinitionBase propertyDefinition,
541 Object value) {
542 super(propertyDefinition, value);
543 }
544
545 /**
546 * Gets the name of the XML element.
547 *
548 * @return the xml element name
549 */
550 @Override
551 protected String getXmlElementName() {
552 return XmlElementNames.IsEqualTo;
553 }
554
555 }
556
557
558 /**
559 * Represents a search filter that checks if a property is greater than a
560 * given value or other property.
561 */
562 public static class IsGreaterThan extends RelationalFilter {
563
564 /**
565 * Initializes a new instance of the class.
566 */
567 public IsGreaterThan() {
568 super();
569 }
570
571 /**
572 * Initializes a new instance of the class.
573 *
574 * @param propertyDefinition The definition of the property that is being compared.
575 * @param otherPropertyDefinition The definition of the property to compare with.
576 */
577 public IsGreaterThan(PropertyDefinitionBase propertyDefinition,
578 PropertyDefinitionBase otherPropertyDefinition) {
579 super(propertyDefinition, otherPropertyDefinition);
580 }
581
582 /**
583 * Initializes a new instance of the class.
584 *
585 * @param propertyDefinition The definition of the property that is being compared.
586 * @param value The value of the property to compare with.
587 */
588 public IsGreaterThan(PropertyDefinitionBase propertyDefinition,
589 Object value) {
590 super(propertyDefinition, value);
591 }
592
593 /**
594 * Gets the name of the XML element.
595 *
596 * @return XML element name.
597 */
598 @Override
599 protected String getXmlElementName() {
600 return XmlElementNames.IsGreaterThan;
601 }
602 }
603
604
605 /**
606 * Represents a search filter that checks if a property is greater than or
607 * equal to a given value or other property.
608 */
609 public static class IsGreaterThanOrEqualTo extends RelationalFilter {
610
611 /**
612 * Initializes a new instance of the class.
613 */
614 public IsGreaterThanOrEqualTo() {
615 super();
616 }
617
618 /**
619 * Initializes a new instance of the class.
620 *
621 * @param propertyDefinition The definition of the property that is being compared.
622 * @param otherPropertyDefinition The definition of the property to compare with.
623 */
624 public IsGreaterThanOrEqualTo(
625 PropertyDefinitionBase propertyDefinition,
626 PropertyDefinitionBase otherPropertyDefinition) {
627 super(propertyDefinition, otherPropertyDefinition);
628 }
629
630 /**
631 * Initializes a new instance of the class.
632 *
633 * @param propertyDefinition The definition of the property that is being compared.
634 * @param value The value of the property to compare with.
635 */
636 public IsGreaterThanOrEqualTo(
637 PropertyDefinitionBase propertyDefinition, Object value) {
638 super(propertyDefinition, value);
639 }
640
641 /**
642 * Gets the name of the XML element. XML element name.
643 *
644 * @return the xml element name
645 */
646 @Override
647 protected String getXmlElementName() {
648 return XmlElementNames.IsGreaterThanOrEqualTo;
649 }
650
651 }
652
653
654 /**
655 * Represents a search filter that checks if a property is less than a given
656 * value or other property.
657 */
658 public static class IsLessThan extends RelationalFilter {
659
660 /**
661 * Initializes a new instance of the class.
662 */
663 public IsLessThan() {
664 super();
665 }
666
667 /**
668 * Initializes a new instance of the class.
669 *
670 * @param propertyDefinition The definition of the property that is being compared.
671 * @param otherPropertyDefinition The definition of the property to compare with.
672 */
673 public IsLessThan(PropertyDefinitionBase propertyDefinition,
674 PropertyDefinitionBase otherPropertyDefinition) {
675 super(propertyDefinition, otherPropertyDefinition);
676 }
677
678 /**
679 * Initializes a new instance of the class.
680 *
681 * @param propertyDefinition The definition of the property that is being compared.
682 * @param value The value of the property to compare with.
683 */
684 public IsLessThan(PropertyDefinitionBase propertyDefinition,
685 Object value) {
686 super(propertyDefinition, value);
687 }
688
689 /**
690 * Gets the name of the XML element. XML element name.
691 *
692 * @return the xml element name
693 */
694 @Override
695 protected String getXmlElementName() {
696 return XmlElementNames.IsLessThan;
697 }
698
699 }
700
701
702 /**
703 * Represents a search filter that checks if a property is less than or
704 * equal to a given value or other property.
705 */
706 public static class IsLessThanOrEqualTo extends RelationalFilter {
707
708 /**
709 * Initializes a new instance of the class.
710 */
711 public IsLessThanOrEqualTo() {
712 super();
713 }
714
715 /**
716 * Initializes a new instance of the class.
717 *
718 * @param propertyDefinition The definition of the property that is being compared.
719 * @param otherPropertyDefinition The definition of the property to compare with.
720 */
721 public IsLessThanOrEqualTo(PropertyDefinitionBase propertyDefinition,
722 PropertyDefinitionBase otherPropertyDefinition) {
723 super(propertyDefinition, otherPropertyDefinition);
724 }
725
726 /**
727 * Initializes a new instance of the class.
728 *
729 * @param propertyDefinition The definition of the property that is being compared.
730 * @param value The value of the property to compare with.
731 */
732 public IsLessThanOrEqualTo(PropertyDefinitionBase propertyDefinition,
733 Object value) {
734 super(propertyDefinition, value);
735 }
736
737 /**
738 * Gets the name of the XML element. XML element name.
739 *
740 * @return the xml element name
741 */
742 @Override
743 protected String getXmlElementName() {
744 return XmlElementNames.IsLessThanOrEqualTo;
745 }
746
747 }
748
749
750 /**
751 * Represents a search filter that checks if a property is not equal to a
752 * given value or other property.
753 */
754 public static class IsNotEqualTo extends RelationalFilter {
755
756 /**
757 * Initializes a new instance of the class.
758 */
759 public IsNotEqualTo() {
760 super();
761 }
762
763 /**
764 * Initializes a new instance of the class.
765 *
766 * @param propertyDefinition The definition of the property that is being compared.
767 * @param otherPropertyDefinition The definition of the property to compare with.
768 */
769 public IsNotEqualTo(PropertyDefinitionBase propertyDefinition,
770 PropertyDefinitionBase otherPropertyDefinition) {
771 super(propertyDefinition, otherPropertyDefinition);
772 }
773
774 /**
775 * Initializes a new instance of the class.
776 *
777 * @param propertyDefinition The definition of the property that is being compared.
778 * @param value The value of the property to compare with.
779 */
780 public IsNotEqualTo(PropertyDefinitionBase propertyDefinition,
781 Object value) {
782 super(propertyDefinition, value);
783 }
784
785 /**
786 * Gets the name of the XML element.
787 *
788 * @return XML element name.
789 */
790 @Override
791 protected String getXmlElementName() {
792 return XmlElementNames.IsNotEqualTo;
793 }
794
795 }
796
797
798 /**
799 * Represents a search filter that negates another. Applications can use
800 * NotFilter to define conditions such as "NOT(other filter)".
801 */
802 public static class Not extends SearchFilter implements IComplexPropertyChangedDelegate {
803
804 /**
805 * The search filter.
806 */
807 private SearchFilter searchFilter;
808
809 /**
810 * Initializes a new instance of the class.
811 */
812 public Not() {
813 super();
814 }
815
816 /**
817 * Initializes a new instance of the class.
818 *
819 * @param searchFilter the search filter
820 */
821 public Not(SearchFilter searchFilter) {
822 super();
823 this.searchFilter = searchFilter;
824 }
825
826 /**
827 * Search filter changed.
828 *
829 * @param complexProperty the complex property
830 */
831 private void searchFilterChanged(ComplexProperty complexProperty) {
832 this.changed();
833 }
834
835 /**
836 * validates the instance.
837 *
838 * @throws ServiceValidationException the service validation exception
839 */
840 @Override
841 protected void internalValidate() throws ServiceValidationException {
842 if (this.searchFilter == null) {
843 throw new ServiceValidationException("The SearchFilter property must be set.");
844 }
845 }
846
847 /**
848 * Gets the name of the XML element.
849 *
850 * @return the xml element name
851 */
852 @Override
853 protected String getXmlElementName() {
854 return XmlElementNames.Not;
855 }
856
857 /**
858 * Tries to read element from XML.
859 *
860 * @param reader the reader
861 * @return true if the element was read
862 * @throws Exception the exception
863 */
864 @Override
865 public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
866 throws Exception {
867 this.searchFilter = SearchFilter.loadFromXml(reader);
868 return true;
869 }
870
871 /**
872 * Writes the elements to XML.
873 *
874 * @param writer the writer
875 * @throws Exception the exception
876 */
877 @Override
878 public void writeElementsToXml(EwsServiceXmlWriter writer)
879 throws Exception {
880 this.searchFilter.writeToXml(writer);
881 }
882
883 /**
884 * Gets the search filter to negate. Available search filter
885 * classes include SearchFilter.IsEqualTo,
886 * SearchFilter.ContainsSubstring and
887 * SearchFilter.SearchFilterCollection.
888 *
889 * @return SearchFilter
890 */
891 public SearchFilter getSearchFilter() {
892 return searchFilter;
893 }
894
895 /**
896 * Sets the search filter to negate. Available search filter classes
897 * include SearchFilter.IsEqualTo, SearchFilter.ContainsSubstring and
898 * SearchFilter.SearchFilterCollection.
899 *
900 * @param searchFilter the new search filter
901 */
902 public void setSearchFilter(SearchFilter searchFilter) {
903 if (this.searchFilter != null) {
904 this.searchFilter.removeChangeEvent(this);
905 }
906
907 if (this.canSetFieldValue(this.searchFilter, searchFilter)) {
908 this.searchFilter = searchFilter;
909 this.changed();
910
911 }
912
913 if (this.searchFilter != null) {
914 this.searchFilter.addOnChangeEvent(this);
915 }
916 }
917
918 /*
919 * (non-Javadoc)
920 *
921 * @see
922 * microsoft.exchange.webservices.
923 * ComplexPropertyChangedDelegateInterface#
924 * complexPropertyChanged(microsoft.exchange.webservices.ComplexProperty
925 * )
926 */
927 @Override
928 public void complexPropertyChanged(ComplexProperty complexProperty) {
929 searchFilterChanged(complexProperty);
930
931 }
932 }
933
934
935 /**
936 * Represents a search filter where an item or folder property is involved.
937 */
938 @EditorBrowsable(state = EditorBrowsableState.Never)
939 public static abstract class PropertyBasedFilter extends SearchFilter {
940
941 /**
942 * The property definition.
943 */
944 private PropertyDefinitionBase propertyDefinition;
945
946 /**
947 * Initializes a new instance of the class.
948 */
949 PropertyBasedFilter() {
950 super();
951 }
952
953 /**
954 * Initializes a new instance of the class.
955 *
956 * @param propertyDefinition the property definition
957 */
958 PropertyBasedFilter(PropertyDefinitionBase propertyDefinition) {
959 super();
960 this.propertyDefinition = propertyDefinition;
961 }
962
963 /**
964 * validate instance.
965 *
966 * @throws ServiceValidationException the service validation exception
967 */
968 @Override
969 protected void internalValidate() throws ServiceValidationException {
970 if (this.propertyDefinition == null) {
971 throw new ServiceValidationException("The PropertyDefinition property must be set.");
972 }
973 }
974
975 /**
976 * Tries to read element from XML.
977 *
978 * @param reader the reader
979 * @return true if element was read
980 * @throws Exception the exception
981 */
982 @Override
983 public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
984 throws Exception {
985 OutParam<PropertyDefinitionBase> outParam =
986 new OutParam<PropertyDefinitionBase>();
987 outParam.setParam(this.propertyDefinition);
988
989 return PropertyDefinitionBase.tryLoadFromXml(reader, outParam);
990 }
991
992 /**
993 * Writes the elements to XML.
994 *
995 * @param writer the writer
996 * @throws XMLStreamException the XML stream exception
997 * @throws ServiceXmlSerializationException the service xml serialization exception
998 */
999 @Override
1000 public void writeElementsToXml(EwsServiceXmlWriter writer)
1001 throws XMLStreamException, ServiceXmlSerializationException {
1002 this.propertyDefinition.writeToXml(writer);
1003 }
1004
1005 /**
1006 * Gets the definition of the property that is involved in the search
1007 * filter.
1008 *
1009 * @return propertyDefinition
1010 */
1011 public PropertyDefinitionBase getPropertyDefinition() {
1012 return this.propertyDefinition;
1013 }
1014
1015 /**
1016 * Sets the definition of the property that is involved in the search
1017 * filter.
1018 *
1019 * @param propertyDefinition the new property definition
1020 */
1021 public void setPropertyDefinition(
1022 PropertyDefinitionBase propertyDefinition) {
1023 this.propertyDefinition = propertyDefinition;
1024 }
1025 }
1026
1027
1028 /**
1029 * Represents the base class for relational filter (for example, IsEqualTo,
1030 * IsGreaterThan or IsLessThanOrEqualTo).
1031 */
1032 @EditorBrowsable(state = EditorBrowsableState.Never)
1033 public abstract static class RelationalFilter extends PropertyBasedFilter {
1034
1035 /**
1036 * The other property definition.
1037 */
1038 private PropertyDefinitionBase otherPropertyDefinition;
1039
1040 /**
1041 * The value.
1042 */
1043 private Object value;
1044
1045 /**
1046 * Initializes a new instance of the class.
1047 */
1048 RelationalFilter() {
1049 super();
1050 }
1051
1052 /**
1053 * Initializes a new instance of the class.
1054 *
1055 * @param propertyDefinition The definition of the property that is being compared.
1056 * @param otherPropertyDefinition The definition of the property to compare with
1057 */
1058 RelationalFilter(PropertyDefinitionBase propertyDefinition,
1059 PropertyDefinitionBase otherPropertyDefinition) {
1060 super(propertyDefinition);
1061 this.otherPropertyDefinition = otherPropertyDefinition;
1062 }
1063
1064 /**
1065 * Initializes a new instance of the class.
1066 *
1067 * @param propertyDefinition The definition of the property that is being compared.
1068 * @param value The value to compare with.
1069 */
1070 RelationalFilter(PropertyDefinitionBase propertyDefinition,
1071 Object value) {
1072 super(propertyDefinition);
1073 this.value = value;
1074 }
1075
1076 /**
1077 * validates the instance.
1078 *
1079 * @throws ServiceValidationException the service validation exception
1080 */
1081 @Override
1082 protected void internalValidate() throws ServiceValidationException {
1083 super.internalValidate();
1084
1085 if (this.otherPropertyDefinition == null && this.value == null) {
1086 throw new ServiceValidationException(
1087 "Either the OtherPropertyDefinition or the Value property must be set.");
1088 }
1089 }
1090
1091 /**
1092 * Tries to read element from XML.
1093 *
1094 * @param reader the reader
1095 * @return true if element was read
1096 * @throws Exception the exception
1097 */
1098 @Override
1099 public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
1100 throws Exception {
1101 boolean result = super.tryReadElementFromXml(reader);
1102
1103 if (!result) {
1104 if (reader.getLocalName().equals(
1105 XmlElementNames.FieldURIOrConstant)) {
1106 try {
1107 reader.read();
1108 reader.ensureCurrentNodeIsStartElement();
1109 } catch (ServiceXmlDeserializationException e) {
1110 LOG.error(e);
1111 } catch (XMLStreamException e) {
1112 LOG.error(e);
1113 }
1114
1115 if (reader.isStartElement(XmlNamespace.Types,
1116 XmlElementNames.Constant)) {
1117 this.value = reader
1118 .readAttributeValue(XmlAttributeNames.Value);
1119 result = true;
1120 } else {
1121 OutParam<PropertyDefinitionBase> outParam =
1122 new OutParam<PropertyDefinitionBase>();
1123 outParam.setParam(this.otherPropertyDefinition);
1124
1125 result = PropertyDefinitionBase.tryLoadFromXml(reader,
1126 outParam);
1127 }
1128 }
1129 }
1130
1131 return result;
1132 }
1133
1134 /**
1135 * Writes the elements to XML.
1136 *
1137 * @param writer the writer
1138 * @throws javax.xml.stream.XMLStreamException , ServiceXmlSerializationException
1139 * @throws ServiceXmlSerializationException the service xml serialization exception
1140 */
1141 @Override
1142 public void writeElementsToXml(EwsServiceXmlWriter writer)
1143 throws XMLStreamException, ServiceXmlSerializationException {
1144 super.writeElementsToXml(writer);
1145
1146 writer.writeStartElement(XmlNamespace.Types,
1147 XmlElementNames.FieldURIOrConstant);
1148
1149 if (this.value != null) {
1150 writer.writeStartElement(XmlNamespace.Types,
1151 XmlElementNames.Constant);
1152 writer.writeAttributeValue(XmlAttributeNames.Value,
1153 true /* alwaysWriteEmptyString */, this.value);
1154 writer.writeEndElement(); // Constant
1155 } else {
1156 this.otherPropertyDefinition.writeToXml(writer);
1157 }
1158
1159 writer.writeEndElement(); // FieldURIOrConstant
1160 }
1161
1162 /**
1163 * Gets the definition of the property to compare with.
1164 *
1165 * @return otherPropertyDefinition
1166 */
1167 public PropertyDefinitionBase getOtherPropertyDefinition() {
1168 return this.otherPropertyDefinition;
1169 }
1170
1171 /**
1172 * Sets the definition of the property to compare with.
1173 *
1174 * @param OtherPropertyDefinition the new other property definition
1175 */
1176 public void setOtherPropertyDefinition(
1177 PropertyDefinitionBase OtherPropertyDefinition) {
1178 this.otherPropertyDefinition = OtherPropertyDefinition;
1179 this.value = null;
1180 }
1181
1182 /**
1183 * Gets the value of the property to compare with.
1184 *
1185 * @return the value
1186 */
1187 public Object getValue() {
1188 return value;
1189 }
1190
1191 /**
1192 * Sets the value of the property to compare with.
1193 *
1194 * @param value the new value
1195 */
1196 public void setValue(Object value) {
1197 this.value = value;
1198 this.otherPropertyDefinition = null;
1199 }
1200
1201 /**
1202 * gets Xml Element name.
1203 *
1204 * @return the xml element name
1205 */
1206 @Override
1207 protected String getXmlElementName() {
1208 return null;
1209 }
1210 }
1211
1212
1213 /**
1214 * Represents a collection of search filter linked by a logical operator.
1215 * Applications can use SearchFilterCollection to define complex search
1216 * filter such as "Condition1 AND Condition2".
1217 */
1218 public static class SearchFilterCollection extends SearchFilter implements
1219 Iterable<SearchFilter>, IComplexPropertyChangedDelegate {
1220
1221 /**
1222 * The logical operator.
1223 */
1224 private LogicalOperator logicalOperator = LogicalOperator.And;
1225
1226 /**
1227 * The search filter.
1228 */
1229 private ArrayList<SearchFilter> searchFilters =
1230 new ArrayList<SearchFilter>();
1231
1232 /**
1233 * Initializes a new instance of the class.
1234 */
1235 public SearchFilterCollection() {
1236 super();
1237 }
1238
1239 /**
1240 * Initializes a new instance of the class.
1241 *
1242 * @param logicalOperator The logical operator used to initialize the collection.
1243 */
1244 public SearchFilterCollection(LogicalOperator logicalOperator) {
1245 this.logicalOperator = logicalOperator;
1246 }
1247
1248 /**
1249 * Initializes a new instance of the class.
1250 *
1251 * @param logicalOperator The logical operator used to initialize the collection.
1252 * @param searchFilters The search filter to add to the collection.
1253 */
1254 public SearchFilterCollection(LogicalOperator logicalOperator,
1255 SearchFilter... searchFilters) {
1256 this(logicalOperator);
1257 for (SearchFilter search : searchFilters) {
1258 Iterable<SearchFilter> searchFil = java.util.Arrays
1259 .asList(search);
1260 this.addRange(searchFil);
1261 }
1262 }
1263
1264 /**
1265 * Initializes a new instance of the class.
1266 *
1267 * @param logicalOperator The logical operator used to initialize the collection.
1268 * @param searchFilters The search filter to add to the collection.
1269 */
1270 public SearchFilterCollection(LogicalOperator logicalOperator,
1271 Iterable<SearchFilter> searchFilters) {
1272 this(logicalOperator);
1273 this.addRange(searchFilters);
1274 }
1275
1276 /**
1277 * Validate instance.
1278 *
1279 * @throws Exception
1280 */
1281 @Override
1282 protected void internalValidate() throws Exception {
1283 for (int i = 0; i < this.getCount(); i++) {
1284 try {
1285 this.searchFilters.get(i).internalValidate();
1286 } catch (ServiceValidationException e) {
1287 throw new ServiceValidationException(String.format("The search filter at index %d is invalid.", i),
1288 e);
1289 }
1290 }
1291 }
1292
1293 /**
1294 * A search filter has changed.
1295 *
1296 * @param complexProperty The complex property
1297 */
1298 private void searchFilterChanged(ComplexProperty complexProperty) {
1299 this.changed();
1300 }
1301
1302 /**
1303 * Gets the name of the XML element.
1304 *
1305 * @return xml element name
1306 */
1307 @Override
1308 protected String getXmlElementName() {
1309 return this.logicalOperator.toString();
1310 }
1311
1312 /**
1313 * Tries to read element from XML.
1314 *
1315 * @param reader the reader
1316 * @return true, if successful
1317 * @throws Exception the exception
1318 */
1319 @Override
1320 public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
1321 throws Exception {
1322
1323 this.add(SearchFilter.loadFromXml(reader));
1324 return true;
1325 }
1326
1327 /**
1328 * Writes the elements to XML.
1329 *
1330 * @param writer the writer
1331 * @throws Exception the exception
1332 */
1333 @Override
1334 public void writeElementsToXml(EwsServiceXmlWriter writer)
1335 throws Exception {
1336 for (SearchFilter searchFilter : this.searchFilters) {
1337 searchFilter.writeToXml(writer);
1338 }
1339 }
1340
1341 /**
1342 * Writes to XML.
1343 *
1344 * @param writer the writer
1345 * @throws Exception the exception
1346 */
1347 @Override public void writeToXml(EwsServiceXmlWriter writer) throws Exception {
1348 // If there is only one filter in the collection, which developers
1349 // tend
1350 // to do,
1351 // we need to not emit the collection and instead only emit the one
1352 // filter within
1353 // the collection. This is to work around the fact that EWS does not
1354 // allow filter
1355 // collections that have less than two elements.
1356 if (this.getCount() == 1) {
1357 this.searchFilters.get(0).writeToXml(writer);
1358 } else {
1359 super.writeToXml(writer);
1360 }
1361 }
1362
1363 /**
1364 * Adds a search filter of any type to the collection.
1365 *
1366 * @param searchFilter >The search filter to add. Available search filter classes
1367 * include SearchFilter.IsEqualTo,
1368 * SearchFilter.ContainsSubstring and
1369 * SearchFilter.SearchFilterCollection.
1370 */
1371 public void add(SearchFilter searchFilter) {
1372 if (searchFilter == null) {
1373 throw new IllegalArgumentException("searchFilter");
1374 }
1375 searchFilter.addOnChangeEvent(this);
1376 this.searchFilters.add(searchFilter);
1377 this.changed();
1378 }
1379
1380 /**
1381 * Adds multiple search filter to the collection.
1382 *
1383 * @param searchFilters The search filter to add. Available search filter classes
1384 * include SearchFilter.IsEqualTo,
1385 * SearchFilter.ContainsSubstring and
1386 * SearchFilter.SearchFilterCollection
1387 */
1388 public void addRange(Iterable<SearchFilter> searchFilters) {
1389 if (searchFilters == null) {
1390 throw new IllegalArgumentException("searchFilters");
1391 }
1392
1393 for (SearchFilter searchFilter : searchFilters) {
1394 searchFilter.addOnChangeEvent(this);
1395 this.searchFilters.add(searchFilter);
1396 }
1397 this.changed();
1398 }
1399
1400 /**
1401 * Clears the collection.
1402 */
1403 public void clear() {
1404 if (this.getCount() > 0) {
1405 for (SearchFilter searchFilter : this.searchFilters) {
1406 searchFilter.removeChangeEvent(this);
1407 }
1408 this.searchFilters.clear();
1409 this.changed();
1410 }
1411 }
1412
1413 /**
1414 * Determines whether a specific search filter is in the collection.
1415 *
1416 * @param searchFilter The search filter to locate in the collection.
1417 * @return True is the search filter was found in the collection, false
1418 * otherwise.
1419 */
1420 public boolean contains(SearchFilter searchFilter) {
1421 return this.searchFilters.contains(searchFilter);
1422 }
1423
1424 /**
1425 * Removes a search filter from the collection.
1426 *
1427 * @param searchFilter The search filter to remove
1428 */
1429 public void remove(SearchFilter searchFilter) {
1430 if (searchFilter == null) {
1431 throw new IllegalArgumentException("searchFilter");
1432 }
1433
1434 if (this.contains(searchFilter)) {
1435 searchFilter.removeChangeEvent(this);
1436 this.searchFilters.remove(searchFilter);
1437 this.changed();
1438 }
1439 }
1440
1441 /**
1442 * Removes the search filter at the specified index from the collection.
1443 *
1444 * @param index The zero-based index of the search filter to remove.
1445 */
1446 public void removeAt(int index) {
1447 if (index < 0 || index >= this.getCount()) {
1448 throw new IllegalArgumentException(
1449 String.format("index %d is out of range [0..%d[.", index, this.getCount()));
1450 }
1451
1452 this.searchFilters.get(index).removeChangeEvent(this);
1453 this.searchFilters.remove(index);
1454 this.changed();
1455 }
1456
1457 /**
1458 * Gets the total number of search filter in the collection.
1459 *
1460 * @return the count
1461 */
1462 public int getCount() {
1463
1464 return this.searchFilters.size();
1465 }
1466
1467 /**
1468 * Gets the search filter at the specified index.
1469 *
1470 * @param index the index
1471 * @return The search filter at the specified index.
1472 */
1473 public SearchFilter getSearchFilter(int index) {
1474 if (index < 0 || index >= this.getCount()) {
1475 throw new IllegalArgumentException(
1476 String.format("index %d is out of range [0..%d[.", index, this.getCount())
1477 );
1478 }
1479 return this.searchFilters.get(index);
1480 }
1481
1482 /**
1483 * Sets the search filter at the specified index.
1484 *
1485 * @param index the index
1486 * @param searchFilter the search filter
1487 */
1488 public void setSearchFilter(int index, SearchFilter searchFilter) {
1489 if (index < 0 || index >= this.getCount()) {
1490 throw new IllegalArgumentException(
1491 String.format("index %d is out of range [0..%d[.", index, this.getCount())
1492 );
1493 }
1494 this.searchFilters.add(index, searchFilter);
1495 }
1496
1497 /**
1498 * Gets the logical operator that links the serach filter in this
1499 * collection.
1500 *
1501 * @return LogicalOperator
1502 */
1503 public LogicalOperator getLogicalOperator() {
1504 return logicalOperator;
1505 }
1506
1507 /**
1508 * Sets the logical operator that links the serach filter in this
1509 * collection.
1510 *
1511 * @param logicalOperator the new logical operator
1512 */
1513 public void setLogicalOperator(LogicalOperator logicalOperator) {
1514 this.logicalOperator = logicalOperator;
1515 }
1516
1517 /*
1518 * (non-Javadoc)
1519 *
1520 * @see
1521 * microsoft.exchange.webservices.
1522 * ComplexPropertyChangedDelegateInterface#
1523 * complexPropertyChanged(microsoft.exchange.webservices.ComplexProperty
1524 * )
1525 */
1526 @Override
1527 public void complexPropertyChanged(ComplexProperty complexProperty) {
1528 searchFilterChanged(complexProperty);
1529 }
1530
1531 /*
1532 * (non-Javadoc)
1533 *
1534 * @see java.lang.Iterable#iterator()
1535 */
1536 @Override
1537 public Iterator<SearchFilter> iterator() {
1538 return this.searchFilters.iterator();
1539 }
1540
1541 }
1542 }