001package com.hammurapi.extract;
002
003import java.io.Serializable;
004import java.util.Collections;
005import java.util.HashSet;
006import java.util.Map;
007import java.util.Set;
008import java.util.TreeSet;
009import java.util.concurrent.TimeUnit;
010
011
012/**
013 * Commutative OR.
014 * @author Pavel Vlasov.
015 *
016 * @param <T>
017 */
018public class CommutativeOr<T, C> extends ExtractorBase<T, Boolean, C> implements CommutativeCompositePredicate<T, C, CommutativeOr<T,C>>, Serializable {
019        
020        private Set<Predicate<T, C>> parts = new HashSet<Predicate<T, C>>();
021                
022        public CommutativeOr(double initialCost, TimeUnit costUnit) {
023                super(initialCost,costUnit);
024        }
025        
026        public CommutativeOr(double initialCost,TimeUnit costUnit, Predicate<T,C>... parts) {
027                super(initialCost,costUnit);
028                for (Predicate<T,C> part: parts) {
029                        addPart(part);
030                }
031        }
032        
033        public CommutativeOr(double initialCost,TimeUnit costUnit, Iterable<Predicate<T,C>> parts) {
034                super(initialCost,costUnit);
035                for (Predicate<T,C> part: parts) {
036                        addPart(part);
037                }
038        }
039        
040        private void addPart(Predicate<T, C> part) {
041                if (part instanceof CommutativeOr) {
042                        for (Predicate<T, C> pp: ((CommutativeOr<T, C>) part).getParts()) {
043                                addPart(pp);
044                        }
045                } else {
046                        parts.add(part);
047                }
048        }
049
050        public Set<Predicate<T, C>> getParts() {
051                return Collections.unmodifiableSet(parts);
052        }
053
054        @Override
055        @SuppressWarnings("unchecked")
056        public ComparisonResult compareTo(Extractor<T, Boolean, C> otherPredicate) {
057                if (getParts().isEmpty()) {
058                        return True.getInstance().compareTo((Extractor<Object, Boolean, Object>) otherPredicate);
059                }
060                
061                if (otherPredicate instanceof CommutativeOr && parts.equals(((CommutativeOr) otherPredicate).parts)) {
062                        return ComparisonResult.EQUAL_NM;
063                }
064                
065                boolean isTrue = false;
066                boolean isFalse = true;
067                for (Predicate<T, C> part: getParts()) {  
068                        ComparisonResult pcr = part.compareTo((Predicate<T, C>) True.getInstance());
069                        if (pcr.getType().equals(ComparisonResult.Type.EQUAL)) {
070                                isTrue = true;
071                                isFalse = false;
072                                break;                          
073                        } else if (!pcr.getType().equals(ComparisonResult.Type.OPPOSITE)) {
074                                isFalse = false;
075                        }                                               
076                }
077                
078                if (isTrue) {
079                        return otherPredicate.compareTo((Predicate<T, C>) True.getInstance());
080                }
081                
082                if (isFalse) {
083                        return otherPredicate.compareTo((Predicate<T, C>) False.getInstance());
084                }
085                
086                if (getParts().size()==1) {
087                        return getParts().iterator().next().compareTo(otherPredicate);
088                }
089                for (Predicate<T, C> part: getParts()) {  
090                        ComparisonResult pcr = part.compareTo(otherPredicate);
091                        if (pcr.isOneToOneMapping()) {
092                                switch (pcr.getType()) {
093                                case EQUAL:
094                                case LESS_RESTRICTIVE:
095                                        return ComparisonResult.LESS_RESTRICTIVE_NM;
096//                              case OPPOSITE:
097//                              case OPPOSITE_LESS_RESTRICTIVE:
098//                                      return ComparisonResult.OPPOSITE_MORE_RESTRICTIVE_NM;
099                                }
100                        }
101                }
102                
103                return super.compareTo(otherPredicate);
104        }
105
106        protected Boolean extractInternal(C context, Map<C, Map<Extractor<T, ? super Boolean, C>, ? super Boolean>> cache, T... obj) {
107                for (Predicate<T, C> part: parts) {
108                        if (part.extract(context, cache, obj)) {
109                                return true;
110                        }
111                }
112                return false;
113        }
114
115        public Set<Integer> parameterIndices() {
116                Set<Integer> ret = new TreeSet<Integer>();
117                for (Predicate<T, C> part: parts) {
118                        ret.addAll(part.parameterIndices());
119                }
120                return ret;
121        }
122
123
124        @SuppressWarnings("unchecked")
125        public Predicate<T, C> remove(Predicate<T, C> part) {
126                if (!parts.contains(part)) {
127                        return this;
128                }
129                try {
130                        CommutativeOr<T, C> ret = (CommutativeOr<T, C>) clone();
131                        ret.parts.remove(part);
132                        return ret.parts.size()==1 ? ret.parts.iterator().next() : ret;
133                } catch (CloneNotSupportedException e) {
134                        throw new ExtractorException(e);
135                }
136        }
137
138        @SuppressWarnings("unchecked")
139        public Predicate<T, C> normalize() {
140                if (getParts().isEmpty()) {
141                        return True.getInstance();
142                }
143                if (getParts().size()==1) {
144                        Predicate<T, C> ret = getParts().iterator().next();
145                        if (ret instanceof CompositePredicate) {
146                                ((CompositePredicate) ret).normalize();
147                        }
148                        return ret;
149                }
150                ComparisonResult cr = compareTo((Predicate<T, C>) True.getInstance());
151                if (cr.getType().equals(ComparisonResult.Type.EQUAL)) {
152                        return True.getInstance();
153                }
154                if (cr.getType().equals(ComparisonResult.Type.OPPOSITE)) {
155                        return False.getInstance();
156                }
157                
158                Predicate<T, C>[] pa = getParts().toArray(new Predicate[getParts().size()]);
159                Z: for (int i=0; i<pa.length; ++i) {
160                        for (int j=0; j<pa.length; ++j) {
161                                if (j!=i && pa[i]!=null && pa[j]!=null) {
162                                        ComparisonResult pcr1 = pa[i].compareTo(pa[j]);
163                                        if (pcr1.isOneToOneMapping()) {
164                                                switch (pcr1.getType()) {
165                                                case EQUAL:
166                                                case MORE_RESTRICTIVE:
167                                                        pa[j]=null;
168                                                        continue;
169                                                case OPPOSITE:
170                                                case OPPOSITE_MORE_RESTRICTIVE:
171                                                        return True.getInstance();
172                                                }
173                                        }
174                                        
175                                        ComparisonResult pcr2 = pa[j].compareTo(pa[i]);
176                                        if (pcr2.isOneToOneMapping()) {
177                                                switch (pcr2.getType()) {
178                                                case EQUAL:
179                                                case MORE_RESTRICTIVE:
180                                                        pa[i]=null;
181                                                        continue Z;
182                                                case OPPOSITE:
183                                                case OPPOSITE_MORE_RESTRICTIVE:
184                                                        return True.getInstance();
185                                                }
186                                        }                                       
187                                }
188                        }
189                }
190                
191                int pc = 0; 
192                for (Predicate<T, C> part: pa) {
193                        if (part!=null) {
194                                ++pc;
195                        }
196                }
197                
198                if (pc==0) {
199                        return True.getInstance();
200                }
201                
202                if (pc==1) {
203                        for (Predicate<T, C> part: pa) {
204                                if (part!=null) {
205                                        if (part instanceof CompositePredicate) {
206                                                ((CompositePredicate) part).normalize();
207                                        }
208                                        return part;
209                                }
210                        }
211                }
212                
213                if (pc!=pa.length) {
214                        CommutativeOr<T, C> ret = new CommutativeOr<T, C>(cost,costUnit);
215                        for (Predicate<T, C> part: pa) {
216                                if (part!=null) {
217                                        ret.addPart(part);
218                                }
219                        }
220                        return ret;
221                }
222                
223                
224                return this;
225        }
226
227        public boolean isContextDependent() {
228                for (Predicate<T, C> part: getParts()) {
229                        if (part.isContextDependent()) {
230                                return true;
231                        }
232                }
233                return false;
234        }
235        
236        @Override
237        public String toString() {
238                return getClass().getName()+getParts();
239        }
240        
241        @Override
242        public double getCost() {
243                if (cost==0 && costUnit==null) {
244                        double ret = 0;
245                        for (Predicate<T,C> part: parts) {
246                                ret+=part.getCost();
247                        }
248                        return ret;
249                }
250                return super.getCost();
251        }
252        
253
254        @Override
255        public Extractor<T, Boolean, C> map(int[] map) {
256                CommutativeOr<T,C> ret = new CommutativeOr<T, C>(initialCost, costUnit);
257                for (Predicate<T,C> part: parts) {
258                        ret.addPart(MappedPredicate.mapPredicate(part, map));
259                }
260                return ret;
261        }
262        
263        @Override
264        public Object clone() throws CloneNotSupportedException {
265                @SuppressWarnings("unchecked")
266                CommutativeOr<T,C> ret = (CommutativeOr<T, C>) super.clone();
267                ret.parts = new HashSet<Predicate<T,C>>(parts); 
268                return ret;
269        }
270
271        @Override
272        public CommutativeOr<T, C> add(Predicate<T, C> part) {
273                CommutativeOr<T, C> ret = new CommutativeOr<T, C>(initialCost, costUnit, parts);
274                ret.parts.add(part);
275                return ret;
276        }
277
278}