001package com.hammurapi.extract;
002
003import java.util.Map;
004import java.util.Set;
005
006import com.hammurapi.extract.ComparisonResult.Type;
007
008
009/**
010 * This predicate evaluates to true if argument is of a particular type.
011 * @author Pavel Vlasov.
012 *
013 * @param <T>
014 */
015public class InstanceOfPredicate<T, C> implements Predicate<T, C>, Mappable<T,Boolean,C> {
016        
017        private Class<?> instanceType;
018        private Extractor<T, Object, C> extractor;
019
020        @SuppressWarnings("unchecked")
021        public InstanceOfPredicate(Extractor<T, Object, C> extractor, Class<?> instanceType) {
022                this.extractor = extractor;
023                if (instanceType.isPrimitive()) {
024                        if (boolean.class.equals(instanceType)) {
025                                this.instanceType = (Class<? extends T>) Boolean.class;
026                        } else if (char.class.equals(instanceType)) {
027                                this.instanceType = (Class<? extends T>) Character.class;
028                        } else if (byte.class.equals(instanceType)) {
029                                this.instanceType = (Class<? extends T>) Byte.class;
030                        } else if (short.class.equals(instanceType)) {
031                                this.instanceType = (Class<? extends T>) Short.class;
032                        } else if (int.class.equals(instanceType)) {
033                                this.instanceType = (Class<? extends T>) Integer.class;
034                        } else if (long.class.equals(instanceType)) {
035                                this.instanceType = (Class<? extends T>) Long.class;
036                        } else if (float.class.equals(instanceType)) {
037                                this.instanceType = (Class<? extends T>) Float.class;
038                        } else if (double.class.equals(instanceType)) {
039                                this.instanceType = (Class<? extends T>) Double.class;
040                        } else if (void.class.equals(instanceType)) {
041                                this.instanceType = null;
042                        }
043                } else {
044                        this.instanceType = instanceType;
045                }
046        }
047
048        @SuppressWarnings("unchecked")
049        public ComparisonResult compareTo(Extractor<T, Boolean, C> otherPredicate) {
050                if (instanceType==null) {
051                        return False.getInstance().compareTo((Extractor<Object, Boolean, Object>) otherPredicate);
052                }
053                
054                if (otherPredicate instanceof InstanceOfPredicate) {
055                        ComparisonResult ecr = extractor.compareTo(((InstanceOfPredicate<T, C>) otherPredicate).extractor);
056                        if (ecr == null || ecr.getType()!=ComparisonResult.Type.EQUAL) {
057                                return ComparisonResult.NOT_EQUAL_NM;
058                        }
059                        
060                        Class<?> otherInstanceType = ((InstanceOfPredicate<T, C>) otherPredicate).instanceType;
061                        if (otherInstanceType.equals(instanceType)) {
062                                return new ComparisonResult(Type.EQUAL, ecr.getIndexMap());
063                        }
064                        if (instanceType.isAssignableFrom(otherInstanceType)) {
065                                return new ComparisonResult(Type.LESS_RESTRICTIVE, ecr.getIndexMap());
066                        }
067                        if (otherInstanceType.isAssignableFrom(instanceType)) {
068                                return new ComparisonResult(Type.MORE_RESTRICTIVE, ecr.getIndexMap());
069                        }
070                        
071                        // No multiple inheritance in Java - if both types are classes, then they
072                        // are mutually exclusive - i.e. a instanceof String is opposite less restrictive than
073                        // a instanceof Integer.
074                        if (!instanceType.isInterface() && !otherInstanceType.isInterface()) {
075                                return new ComparisonResult(Type.OPPOSITE_LESS_RESTRICTIVE, ecr.getIndexMap());
076                        }
077                        
078                }
079                
080                // True is less restrictive than anything but self.
081                if (otherPredicate instanceof True) {
082                        return ComparisonResult.MORE_RESTRICTIVE_NM;
083                }
084                
085                // False is more restrictive than anything but self.
086                if (otherPredicate instanceof False) {
087                        return ComparisonResult.LESS_RESTRICTIVE_NM;
088                }
089                
090                if (otherPredicate instanceof FacadeExtractor) {
091                        ComparisonResult cr = otherPredicate.compareTo(this);
092                        if (cr==null) {
093                                return cr;
094                        }
095                        switch (cr.getType()) {
096                        case EQUAL:
097                        case NOT_EQUAL:
098                        case OPPOSITE:
099                                return cr;
100                        case LESS_RESTRICTIVE:
101                                return new ComparisonResult(Type.MORE_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices()));
102                        case MORE_RESTRICTIVE:
103                                return new ComparisonResult(Type.LESS_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices()));
104                        case OPPOSITE_LESS_RESTRICTIVE:
105                                return new ComparisonResult(Type.OPPOSITE_LESS_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices()));
106                        case OPPOSITE_MORE_RESTRICTIVE:
107                                return new ComparisonResult(Type.OPPOSITE_MORE_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices()));
108                        }
109                }
110                
111                return ComparisonResult.NOT_EQUAL_NM;
112        }
113
114        @SuppressWarnings({ "unchecked", "rawtypes" })
115        public Boolean extract(C context, Map<C, Map<Extractor<T, ? super Boolean, C>, ? super Boolean>> cache, T... obj) {
116                return instanceType==null ? false : instanceType.isInstance(extractor.extract(context, (Map) cache, obj));
117        }
118
119        public Set<Integer> parameterIndices() {
120                return extractor.parameterIndices();
121        }
122
123        public boolean isContextDependent() {
124                return false;
125        }
126
127        @Override
128        public String toString() {
129                return "InstanceOfPredicate [instanceType=" + instanceType + ", extractor=" + extractor + "]";
130        }
131
132        @Override
133        public double getCost() {
134                return 0;
135        }
136
137        @Override
138        public Extractor<T, Boolean, C> map(int[] map) {
139                if (extractor instanceof Mappable) {
140                        Extractor<T,Object,C> mapped = ((Mappable<T,Object,C>) extractor).map(map);
141                        if (mapped!=null) {
142                                return new InstanceOfPredicate<T, C>(mapped, instanceType);
143                        }
144                }
145                return null;
146        }
147        
148        /**
149         * @return Extractor which return value is checked for instanceof.
150         */
151        public Extractor<T, Object, C> getExtractor() {
152                return extractor;
153        }
154        
155        public Class<?> getInstanceType() {
156                return instanceType;
157        }
158}