1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /*
4 **********************************************************************
5 * Copyright (c) 2002-2010, International Business Machines
6 * Corporation and others.  All Rights Reserved.
7 **********************************************************************
8 * Author: M. Davis
9 * Created: December 2002 (moved from UnicodeSet)
10 * Since: ICU 2.4
11 **********************************************************************
12 */
13 package com.ibm.icu.impl;
14 
15 import java.util.Iterator;
16 import java.util.SortedSet;
17 import java.util.TreeSet;
18 
19 /**
20  * Computationally efficient determination of the relationship between
21  * two SortedSets.
22  */
23 public class SortedSetRelation {
24 
25     /**
26      * The relationship between two sets A and B can be determined by looking at:
27      * A - B
28      * A & B (intersection)
29      * B - A
30      * These are represented by a set of bits.
31      * Bit 2 is true if A - B is not empty
32      * Bit 1 is true if A & B is not empty
33      * BIT 0 is true if B - A is not empty
34      */
35     public static final int
36         A_NOT_B = 4,
37         A_AND_B = 2,
38         B_NOT_A = 1;
39 
40     /**
41      * There are 8 combinations of the relationship bits. These correspond to
42      * the filters (combinations of allowed bits) in hasRelation. They also
43      * correspond to the modification functions, listed in comments.
44      */
45     public static final int
46        ANY =            A_NOT_B |   A_AND_B |   B_NOT_A,    // union,           addAll
47        CONTAINS =       A_NOT_B |   A_AND_B,                // A                (unnecessary)
48        DISJOINT =       A_NOT_B |               B_NOT_A,    // A xor B,         missing Java function
49        ISCONTAINED =                A_AND_B |   B_NOT_A,    // B                (unnecessary)
50        NO_B =           A_NOT_B,                            // A setDiff B,     removeAll
51        EQUALS =                     A_AND_B,                // A intersect B,   retainAll
52        NO_A =                                   B_NOT_A,    // B setDiff A,     removeAll
53        NONE =           0,                                  // null             (unnecessary)
54 
55        ADDALL = ANY,                // union,           addAll
56        A = CONTAINS,                // A                (unnecessary)
57        COMPLEMENTALL = DISJOINT,    // A xor B,         missing Java function
58        B = ISCONTAINED,             // B                (unnecessary)
59        REMOVEALL = NO_B,            // A setDiff B,     removeAll
60        RETAINALL = EQUALS,          // A intersect B,   retainAll
61        B_REMOVEALL = NO_A;          // B setDiff A,     removeAll
62 
63 
64     /**
65      * Utility that could be on SortedSet. Faster implementation than
66      * what is in Java for doing contains, equals, etc.
67      * @param a first set
68      * @param allow filter, using ANY, CONTAINS, etc.
69      * @param b second set
70      * @return whether the filter relationship is true or not.
71      */
hasRelation(SortedSet<T> a, int allow, SortedSet<T> b)72     public static <T extends Object & Comparable<? super T>> boolean hasRelation(SortedSet<T> a, int allow, SortedSet<T> b) {
73         if (allow < NONE || allow > ANY) {
74             throw new IllegalArgumentException("Relation " + allow + " out of range");
75         }
76 
77         // extract filter conditions
78         // these are the ALLOWED conditions Set
79 
80         boolean anb = (allow & A_NOT_B) != 0;
81         boolean ab = (allow & A_AND_B) != 0;
82         boolean bna = (allow & B_NOT_A) != 0;
83 
84         // quick check on sizes
85         switch(allow) {
86             case CONTAINS: if (a.size() < b.size()) return false; break;
87             case ISCONTAINED: if (a.size() > b.size()) return false; break;
88             case EQUALS: if (a.size() != b.size()) return false; break;
89         }
90 
91         // check for null sets
92         if (a.size() == 0) {
93             if (b.size() == 0) return true;
94             return bna;
95         } else if (b.size() == 0) {
96             return anb;
97         }
98 
99         // pick up first strings, and start comparing
100         Iterator<? extends T> ait = a.iterator();
101         Iterator<? extends T> bit = b.iterator();
102 
103         T aa = ait.next();
104         T bb = bit.next();
105 
106         while (true) {
107             int comp = aa.compareTo(bb);
108             if (comp == 0) {
109                 if (!ab) return false;
110                 if (!ait.hasNext()) {
111                     if (!bit.hasNext()) return true;
112                     return bna;
113                 } else if (!bit.hasNext()) {
114                     return anb;
115                 }
116                 aa = ait.next();
117                 bb = bit.next();
118             } else if (comp < 0) {
119                 if (!anb) return false;
120                 if (!ait.hasNext()) {
121                     return bna;
122                 }
123                 aa = ait.next();
124             } else  {
125                 if (!bna) return false;
126                 if (!bit.hasNext()) {
127                     return anb;
128                 }
129                 bb = bit.next();
130             }
131         }
132     }
133 
134     /**
135      * Utility that could be on SortedSet. Allows faster implementation than
136      * what is in Java for doing addAll, removeAll, retainAll, (complementAll).
137      * @param a first set
138      * @param relation the relation filter, using ANY, CONTAINS, etc.
139      * @param b second set
140      * @return the new set
141      */
doOperation(SortedSet<T> a, int relation, SortedSet<T> b)142     public static <T extends Object & Comparable<? super T>> SortedSet<? extends T> doOperation(SortedSet<T> a, int relation, SortedSet<T> b) {
143         // TODO: optimize this as above
144         TreeSet<? extends T> temp;
145         switch (relation) {
146             case ADDALL:
147                 a.addAll(b);
148                 return a;
149             case A:
150                 return a; // no action
151             case B:
152                 a.clear();
153                 a.addAll(b);
154                 return a;
155             case REMOVEALL:
156                 a.removeAll(b);
157                 return a;
158             case RETAINALL:
159                 a.retainAll(b);
160                 return a;
161             // the following is the only case not really supported by Java
162             // although all could be optimized
163             case COMPLEMENTALL:
164                 temp = new TreeSet<T>(b);
165                 temp.removeAll(a);
166                 a.removeAll(b);
167                 a.addAll(temp);
168                 return a;
169             case B_REMOVEALL:
170                 temp = new TreeSet<T>(b);
171                 temp.removeAll(a);
172                 a.clear();
173                 a.addAll(temp);
174                 return a;
175             case NONE:
176                 a.clear();
177                 return a;
178             default:
179                 throw new IllegalArgumentException("Relation " + relation + " out of range");
180         }
181     }
182 }
183