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 AND.
014 * @author Pavel Vlasov.
015 *
016 * @param <T>
017 */
018public class CommutativeAnd<T, C> extends ExtractorBase<T, Boolean, C> implements CommutativeCompositePredicate<T, C, CommutativeAnd<T,C>>, Serializable {
019        
020        private Set<Predicate<T, C>> parts = new HashSet<Predicate<T, C>>();
021        
022        public CommutativeAnd(double initialCost, TimeUnit costUnit) {
023                super(initialCost,costUnit);
024        }
025        
026        public CommutativeAnd(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 CommutativeAnd(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 CommutativeAnd) {
042                        for (Predicate<T, C> pp: ((CommutativeAnd<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 CommutativeAnd && parts.equals(((CommutativeAnd) otherPredicate).parts)) {
062                        return ComparisonResult.EQUAL_NM;
063                }
064                
065                boolean isTrue = true;
066                boolean isFalse = false;
067                for (Predicate<T, C> part: getParts()) {  
068                        ComparisonResult pcr = part.compareTo((Predicate<T, C>) False.getInstance());
069                        if (pcr.getType().equals(ComparisonResult.Type.EQUAL)) {
070                                isFalse = true;
071                                isTrue = false;
072                                break;                          
073                        } else if (!pcr.getType().equals(ComparisonResult.Type.OPPOSITE)) {
074                                isTrue = 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 MORE_RESTRICTIVE:
095                                        return ComparisonResult.MORE_RESTRICTIVE_NM;
096//                              case OPPOSITE:
097//                              case OPPOSITE_MORE_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 false;
110                        }
111                }
112                return true;
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                        CommutativeAnd<T, C> ret = (CommutativeAnd<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                Predicate<T, C>[] pa = getParts().toArray(new Predicate[getParts().size()]);
158                Z: for (int i=0; i<pa.length; ++i) {
159                        for (int j=0; j<pa.length; ++j) {
160                                if (j!=i && pa[i]!=null && pa[j]!=null) {
161                                        ComparisonResult pcr1 = pa[i].compareTo(pa[j]);
162                                        if (pcr1.isOneToOneMapping()) {
163                                                switch (pcr1.getType()) {
164                                                case EQUAL:
165                                                case LESS_RESTRICTIVE:
166                                                        pa[j]=null;
167                                                        continue;
168                                                case OPPOSITE:
169                                                case OPPOSITE_LESS_RESTRICTIVE:
170                                                        return False.getInstance();
171                                                }
172                                        }
173                                        
174                                        ComparisonResult pcr2 = pa[j].compareTo(pa[i]);
175                                        if (pcr2.isOneToOneMapping()) {
176                                                switch (pcr2.getType()) {
177                                                case EQUAL:
178                                                case LESS_RESTRICTIVE:
179                                                        pa[i]=null;
180                                                        continue Z;
181                                                case OPPOSITE:
182                                                case OPPOSITE_LESS_RESTRICTIVE:
183                                                        return False.getInstance();
184                                                }
185                                        }                                       
186                                }
187                        }
188                }
189                
190                int pc = 0; 
191                for (Predicate<T, C> part: pa) {
192                        if (part!=null) {
193                                ++pc;
194                        }
195                }
196                
197                if (pc==0) {
198                        return True.getInstance();
199                }
200                
201                if (pc==1) {
202                        for (Predicate<T, C> part: pa) {
203                                if (part!=null) {
204                                        if (part instanceof CompositePredicate) {
205                                                ((CompositePredicate) part).normalize();
206                                        }
207                                        return part;
208                                }
209                        }
210                }
211                
212                if (pc!=pa.length) {
213                        CommutativeAnd<T, C> ret = new CommutativeAnd<T, C>(cost, costUnit);
214                        for (Predicate<T, C> part: pa) {
215                                if (part!=null) {
216                                        ret.addPart(part);
217                                }
218                        }
219                        return ret;
220                }
221                
222                return this;
223        }
224
225        public boolean isContextDependent() {
226                for (Predicate<T, C> part: getParts()) {
227                        if (part.isContextDependent()) {
228                                return true;
229                        }
230                }
231                return false;
232        }
233        
234        @Override
235        public String toString() {
236                return getClass().getName()+getParts();
237        }
238        
239        @Override
240        public double getCost() {
241                if (cost==0 && costUnit==null) {
242                        double ret = 0;
243                        for (Predicate<T,C> part: parts) {
244                                ret+=part.getCost();
245                        }
246                        return ret;
247                }
248                return super.getCost();
249        }
250        
251
252        @Override
253        public Extractor<T, Boolean, C> map(int[] map) {
254                CommutativeAnd<T,C> ret = new CommutativeAnd<T, C>(initialCost, costUnit);
255                for (Predicate<T,C> part: parts) {
256                        ret.addPart(MappedPredicate.mapPredicate(part, map));
257                }
258                return ret;
259        }
260        
261        @Override
262        public Object clone() throws CloneNotSupportedException {
263                @SuppressWarnings("unchecked")
264                CommutativeAnd<T,C> ret = (CommutativeAnd<T, C>) super.clone();
265                ret.parts = new HashSet<Predicate<T,C>>(parts); 
266                return ret;
267        }
268        
269        @Override
270        public CommutativeAnd<T, C> add(Predicate<T, C> part) {
271                CommutativeAnd<T, C> ret = new CommutativeAnd<T, C>(initialCost, costUnit, parts);
272                ret.parts.add(part);
273                return ret;
274        }
275}