001package com.hammurapi.extract;
002
003import java.io.Serializable;
004import java.util.ArrayList;
005import java.util.Collections;
006import java.util.List;
007import java.util.Map;
008import java.util.Set;
009import java.util.TreeSet;
010import java.util.concurrent.TimeUnit;
011
012
013/**
014 * Non-commutative AND.
015 * @author Pavel Vlasov.
016 *
017 * @param <T>
018 */
019public class And<T, C> extends ExtractorBase<T, Boolean, C> implements CompositePredicate<T, List<Predicate<T, C>>, C, And<T,C>>, Serializable, Cloneable {
020        
021        private List<Predicate<T, C>> parts = new ArrayList<Predicate<T, C>>();
022                
023        public And(double initialCost, TimeUnit costUnit) {
024                super(initialCost, costUnit);
025        }
026        
027        public And(double initialCost, TimeUnit costUnit, Predicate<T,C>... parts) {
028                super(initialCost, costUnit);
029                for (Predicate<T,C> part: parts) {
030                        addPart(part);
031                }
032        }
033        
034        public And(double initialCost, TimeUnit costUnit, Iterable<Predicate<T,C>> parts) {
035                super(initialCost, costUnit);
036                for (Predicate<T,C> part: parts) {
037                        addPart(part);
038                }
039        }
040        
041        private void addPart(Predicate<T, C> part) {
042                if (part instanceof And) {
043                        for (Predicate<T, C> pp: ((And<T, C>) part).getParts()) {
044                                addPart(pp);
045                        }
046                } else {
047                        parts.add(part);
048                }
049        }
050
051        public List<Predicate<T, C>> getParts() {
052                return Collections.unmodifiableList(parts);
053        }
054
055        @Override
056        @SuppressWarnings("unchecked")
057        public ComparisonResult compareTo(Extractor<T, Boolean, C> otherPredicate) {
058                if (getParts().isEmpty()) {
059                        return True.getInstance().compareTo((Extractor<Object, Boolean, Object>) otherPredicate);
060                }
061                
062                if (otherPredicate instanceof And && parts.equals(((And) otherPredicate).parts)) {
063                        return ComparisonResult.EQUAL_NM;
064                }
065                                
066                boolean isTrue = true;
067                boolean isFalse = false;
068                for (Predicate<T, C> part: getParts()) {  
069                        ComparisonResult pcr = part.compareTo((Predicate<T, C>) False.getInstance());                     
070                        if (pcr.getType().equals(ComparisonResult.Type.EQUAL)) {
071                                isFalse = true;
072                                isTrue = false;
073                                break;                          
074                        } else if (!pcr.getType().equals(ComparisonResult.Type.OPPOSITE)) {
075                                isTrue = false;
076                        }                                               
077                }
078                
079                if (isTrue) {
080                        return otherPredicate.compareTo((Predicate<T, C>) True.getInstance());
081                }
082                
083                if (isFalse) {
084                        return otherPredicate.compareTo((Predicate<T, C>) False.getInstance());
085                }
086                
087                if (getParts().size()==1) {
088                        return getParts().get(0).compareTo(otherPredicate);
089                }
090                for (Predicate<T, C> part: getParts()) {  
091                        ComparisonResult pcr = part.compareTo(otherPredicate);
092                        if (pcr.isOneToOneMapping()) {
093                                switch (pcr.getType()) {
094                                case EQUAL:
095                                case MORE_RESTRICTIVE:
096                                        return ComparisonResult.MORE_RESTRICTIVE_NM;
097//                              case OPPOSITE:
098//                              case OPPOSITE_MORE_RESTRICTIVE:
099//                                      return ComparisonResult.OPPOSITE_MORE_RESTRICTIVE_NM;
100                                }
101                        }
102                }
103                
104                return super.compareTo(otherPredicate);
105        }
106
107        protected Boolean extractInternal(C context, Map<C, Map<Extractor<T, ? super Boolean, C>, ? super Boolean>> cache, T... obj) {
108                for (Predicate<T, C> part: parts) {
109                        if (!part.extract(context, cache, obj)) {
110                                return false;
111                        }
112                }
113                return true;
114        }
115
116        public Set<Integer> parameterIndices() {
117                Set<Integer> ret = new TreeSet<Integer>();
118                for (Predicate<T, C> part: parts) {
119                        ret.addAll(part.parameterIndices());
120                }
121                return ret;
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                        And<T, C> ret = (And<T, C>) clone();
131                        ret.parts.remove(part);
132                        return ret.parts.size()==1 ? ret.parts.get(0) : 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().get(0);                  
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 LESS_RESTRICTIVE:
167                                                        pa[j]=null;
168                                                        continue;
169                                                case OPPOSITE:
170                                                case OPPOSITE_LESS_RESTRICTIVE:
171                                                        return False.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 LESS_RESTRICTIVE:
180                                                        pa[i]=null;
181                                                        continue Z;
182                                                case OPPOSITE:
183                                                case OPPOSITE_LESS_RESTRICTIVE:
184                                                        return False.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                        And<T, C> ret = new And<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                return this;
224        }
225
226        public boolean isContextDependent() {
227                for (Predicate<T, C> part: getParts()) {
228                        if (part.isContextDependent()) {
229                                return true;
230                        }
231                }
232                return false;
233        }
234
235        @Override
236        public String toString() {
237                return getClass().getName()+getParts();
238        }
239        
240        @Override
241        public double getCost() {
242                if (cost==0 && costUnit==null) {
243                        double ret = 0;
244                        for (Predicate<T,C> part: parts) {
245                                ret+=part.getCost();
246                        }
247                        return ret;
248                }
249                return super.getCost();
250        }
251
252        @Override
253        public Extractor<T, Boolean, C> map(int[] map) {
254                And<T,C> ret = new And<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                And<T,C> ret = (And<T, C>) super.clone();
265                ret.parts = new ArrayList<Predicate<T,C>>(parts); 
266                return ret;
267        }
268
269        @Override
270        public And<T, C> add(Predicate<T, C> part) {
271                And<T, C> ret = new And<T, C>(initialCost, costUnit, parts);
272                ret.parts.add(part);
273                return ret;
274        }
275}