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 OR.
015 * @author Pavel Vlasov.
016 *
017 * @param <T>
018 */
019public class Or<T, C> extends ExtractorBase<T, Boolean, C> implements CompositePredicate<T, List<Predicate<T, C>>, C, Or<T,C>>, Serializable {
020        
021        private List<Predicate<T, C>> parts = new ArrayList<Predicate<T, C>>();
022        
023        public Or(double initialCost, TimeUnit costUnit) {
024                super(initialCost, costUnit);
025        }
026        
027        public Or(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 Or(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 Or) {
043                        for (Predicate<T, C> pp: ((Or<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 Or && parts.equals(((Or) otherPredicate).parts)) {
063                        return ComparisonResult.EQUAL_NM;
064                }
065                
066                boolean isTrue = false;
067                boolean isFalse = true;
068                for (Predicate<T, C> part: getParts()) {  
069                        ComparisonResult pcr = part.compareTo((Predicate<T, C>) True.getInstance());
070                        if (pcr.getType().equals(ComparisonResult.Type.EQUAL)) {
071                                isTrue = true;
072                                isFalse = false;
073                                break;                          
074                        } else if (!pcr.getType().equals(ComparisonResult.Type.OPPOSITE)) {
075                                isFalse = 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().iterator().next().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 LESS_RESTRICTIVE:
096                                        return ComparisonResult.LESS_RESTRICTIVE_NM;
097//                              case OPPOSITE:
098//                              case OPPOSITE_LESS_RESTRICTIVE:
099//                                      return ComparisonResult.OPPOSITE_MORE_RESTRICTIVE_NM;
100                                }
101                        }
102                }
103                
104                return super.compareTo(otherPredicate);
105        }
106
107        protected Boolean extractInternal(
108                        C context,
109                        Map<C, Map<Extractor<T, ? super Boolean, C>, ? super Boolean>> cache,
110                        T... obj) {
111                
112                for (Predicate<T, C> part: parts) {
113                        if (part.extract(context, cache, obj)) {
114                                return true;
115                        }
116                }
117                return false;
118        }
119
120        public Set<Integer> parameterIndices() {
121                Set<Integer> ret = new TreeSet<Integer>();
122                for (Predicate<T, C> part: parts) {
123                        ret.addAll(part.parameterIndices());
124                }
125                return ret;
126        }
127
128
129        @SuppressWarnings("unchecked")
130        public Predicate<T, C> remove(Predicate<T, C> part) {
131                if (!parts.contains(part)) {
132                        return this;
133                }
134                try {
135                        Or<T, C> ret = (Or<T, C>) clone();
136                        ret.parts.remove(part);
137                        return ret.parts.size()==1 ? ret.parts.get(0) : ret;
138                } catch (CloneNotSupportedException e) {
139                        throw new ExtractorException(e);
140                }
141        }
142
143        @SuppressWarnings("unchecked")
144        public Predicate<T, C> normalize() {
145                if (getParts().isEmpty()) {
146                        return True.getInstance();
147                }
148                if (getParts().size()==1) {
149                        Predicate<T, C> ret = getParts().get(0);
150                        if (ret instanceof CompositePredicate) {
151                                ((CompositePredicate) ret).normalize();
152                        }
153                        return ret;
154                }
155                ComparisonResult cr = compareTo((Predicate<T, C>) True.getInstance());
156                if (cr.getType().equals(ComparisonResult.Type.EQUAL)) {
157                        return True.getInstance();
158                }
159                if (cr.getType().equals(ComparisonResult.Type.OPPOSITE)) {
160                        return False.getInstance();
161                }
162                Predicate<T, C>[] pa = getParts().toArray(new Predicate[getParts().size()]);
163                Z: for (int i=0; i<pa.length; ++i) {
164                        for (int j=0; j<pa.length; ++j) {
165                                if (j!=i && pa[i]!=null && pa[j]!=null) {
166                                        ComparisonResult pcr1 = pa[i].compareTo(pa[j]);
167                                        if (pcr1.isOneToOneMapping()) {
168                                                switch (pcr1.getType()) {
169                                                case EQUAL:
170                                                case MORE_RESTRICTIVE:
171                                                        pa[j]=null;
172                                                        continue;
173                                                case OPPOSITE:
174                                                case OPPOSITE_MORE_RESTRICTIVE:
175                                                        return True.getInstance();
176                                                }
177                                        }
178                                        
179                                        ComparisonResult pcr2 = pa[j].compareTo(pa[i]);
180                                        if (pcr2.isOneToOneMapping()) {
181                                                switch (pcr2.getType()) {
182                                                case EQUAL:
183                                                case MORE_RESTRICTIVE:
184                                                        pa[i]=null;
185                                                        continue Z;
186                                                case OPPOSITE:
187                                                case OPPOSITE_MORE_RESTRICTIVE:
188                                                        return True.getInstance();
189                                                }
190                                        }                                       
191                                }
192                        }
193                }
194                
195                int pc = 0; 
196                for (Predicate<T, C> part: pa) {
197                        if (part!=null) {
198                                ++pc;
199                        }
200                }
201                
202                if (pc==0) {
203                        return True.getInstance();
204                }
205                
206                if (pc==1) {
207                        for (Predicate<T, C> part: pa) {
208                                if (part!=null) {
209                                        if (part instanceof CompositePredicate) {
210                                                ((CompositePredicate) part).normalize();
211                                        }
212                                        return part;
213                                }
214                        }
215                }
216                
217                if (pc!=pa.length) {
218                        Or<T, C> ret = new Or<T, C>(cost, costUnit);
219                        for (Predicate<T, C> part: pa) {
220                                if (part!=null) {
221                                        ret.addPart(part);
222                                }
223                        }
224                        return ret;
225                }
226                                
227                return this;
228        }
229
230        public boolean isContextDependent() {
231                for (Predicate<T, C> part: getParts()) {
232                        if (part.isContextDependent()) {
233                                return true;
234                        }
235                }
236                return false;
237        }
238        
239        @Override
240        public String toString() {
241                return getClass().getName()+getParts();
242        }
243        
244        @Override
245        public double getCost() {
246                if (cost==0 && costUnit==null) {
247                        double ret = 0;
248                        for (Predicate<T,C> part: parts) {
249                                ret+=part.getCost();
250                        }
251                        return ret;
252                }
253                return super.getCost();
254        }
255        
256
257        @Override
258        public Extractor<T, Boolean, C> map(int[] map) {
259                Or<T,C> ret = new Or<T, C>(initialCost, costUnit);
260                for (Predicate<T,C> part: parts) {
261                        ret.addPart(MappedPredicate.mapPredicate(part, map));
262                }
263                return ret;
264        }
265        
266        @Override
267        public Object clone() throws CloneNotSupportedException {
268                @SuppressWarnings("unchecked")
269                Or<T,C> ret = (Or<T, C>) super.clone();
270                ret.parts = new ArrayList<Predicate<T,C>>(parts); 
271                return ret;
272        }
273        
274        @Override
275        public Or<T, C> add(Predicate<T, C> part) {
276                Or<T, C> ret = new Or<T, C>(initialCost, costUnit, parts);
277                ret.parts.add(part);
278                return ret;
279        }
280}