1 | package com.hammurapi.extract; |
2 | |
3 | import java.util.Map; |
4 | import java.util.Set; |
5 | |
6 | import com.hammurapi.extract.ComparisonResult.Type; |
7 | |
8 | |
9 | /** |
10 | * This predicate evaluates to true if argument is of a particular type. |
11 | * @author Pavel Vlasov. |
12 | * |
13 | * @param <T> |
14 | */ |
15 | public class InstanceOfPredicate<T, C> implements Predicate<T, C>, Mappable<T,Boolean,C> { |
16 | |
17 | private Class<?> instanceType; |
18 | private Extractor<T, Object, C> extractor; |
19 | |
20 | @SuppressWarnings("unchecked") |
21 | public InstanceOfPredicate(Extractor<T, Object, C> extractor, Class<?> instanceType) { |
22 | this.extractor = extractor; |
23 | if (instanceType.isPrimitive()) { |
24 | if (boolean.class.equals(instanceType)) { |
25 | this.instanceType = (Class<? extends T>) Boolean.class; |
26 | } else if (char.class.equals(instanceType)) { |
27 | this.instanceType = (Class<? extends T>) Character.class; |
28 | } else if (byte.class.equals(instanceType)) { |
29 | this.instanceType = (Class<? extends T>) Byte.class; |
30 | } else if (short.class.equals(instanceType)) { |
31 | this.instanceType = (Class<? extends T>) Short.class; |
32 | } else if (int.class.equals(instanceType)) { |
33 | this.instanceType = (Class<? extends T>) Integer.class; |
34 | } else if (long.class.equals(instanceType)) { |
35 | this.instanceType = (Class<? extends T>) Long.class; |
36 | } else if (float.class.equals(instanceType)) { |
37 | this.instanceType = (Class<? extends T>) Float.class; |
38 | } else if (double.class.equals(instanceType)) { |
39 | this.instanceType = (Class<? extends T>) Double.class; |
40 | } else if (void.class.equals(instanceType)) { |
41 | this.instanceType = null; |
42 | } |
43 | } else { |
44 | this.instanceType = instanceType; |
45 | } |
46 | } |
47 | |
48 | @SuppressWarnings("unchecked") |
49 | public ComparisonResult compareTo(Extractor<T, Boolean, C> otherPredicate) { |
50 | if (instanceType==null) { |
51 | return False.getInstance().compareTo((Extractor<Object, Boolean, Object>) otherPredicate); |
52 | } |
53 | |
54 | if (otherPredicate instanceof InstanceOfPredicate) { |
55 | ComparisonResult ecr = extractor.compareTo(((InstanceOfPredicate<T, C>) otherPredicate).extractor); |
56 | if (ecr == null || ecr.getType()!=ComparisonResult.Type.EQUAL) { |
57 | return ComparisonResult.NOT_EQUAL_NM; |
58 | } |
59 | |
60 | Class<?> otherInstanceType = ((InstanceOfPredicate<T, C>) otherPredicate).instanceType; |
61 | if (otherInstanceType.equals(instanceType)) { |
62 | return new ComparisonResult(Type.EQUAL, ecr.getIndexMap()); |
63 | } |
64 | if (instanceType.isAssignableFrom(otherInstanceType)) { |
65 | return new ComparisonResult(Type.LESS_RESTRICTIVE, ecr.getIndexMap()); |
66 | } |
67 | if (otherInstanceType.isAssignableFrom(instanceType)) { |
68 | return new ComparisonResult(Type.MORE_RESTRICTIVE, ecr.getIndexMap()); |
69 | } |
70 | |
71 | // No multiple inheritance in Java - if both types are classes, then they |
72 | // are mutually exclusive - i.e. a instanceof String is opposite less restrictive than |
73 | // a instanceof Integer. |
74 | if (!instanceType.isInterface() && !otherInstanceType.isInterface()) { |
75 | return new ComparisonResult(Type.OPPOSITE_LESS_RESTRICTIVE, ecr.getIndexMap()); |
76 | } |
77 | |
78 | } |
79 | |
80 | // True is less restrictive than anything but self. |
81 | if (otherPredicate instanceof True) { |
82 | return ComparisonResult.MORE_RESTRICTIVE_NM; |
83 | } |
84 | |
85 | // False is more restrictive than anything but self. |
86 | if (otherPredicate instanceof False) { |
87 | return ComparisonResult.LESS_RESTRICTIVE_NM; |
88 | } |
89 | |
90 | if (otherPredicate instanceof FacadeExtractor) { |
91 | ComparisonResult cr = otherPredicate.compareTo(this); |
92 | if (cr==null) { |
93 | return cr; |
94 | } |
95 | switch (cr.getType()) { |
96 | case EQUAL: |
97 | case NOT_EQUAL: |
98 | case OPPOSITE: |
99 | 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 | } |