1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.internal;
18
19 import java.lang.reflect.Array;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.IdentityHashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.commons.jexl3.JexlArithmetic;
27 import org.apache.commons.jexl3.internal.introspection.ClassMisc;
28
29
30
31
32 public class ArrayBuilder implements JexlArithmetic.ArrayBuilder {
33
34
35 private static final int PRIMITIVE_SIZE = 8;
36
37
38 private static final Map<Class<?>, Class<?>> BOXING_CLASSES;
39 static {
40 BOXING_CLASSES = new IdentityHashMap<>(PRIMITIVE_SIZE);
41 BOXING_CLASSES.put(Boolean.class, Boolean.TYPE);
42 BOXING_CLASSES.put(Byte.class, Byte.TYPE);
43 BOXING_CLASSES.put(Character.class, Character.TYPE);
44 BOXING_CLASSES.put(Double.class, Double.TYPE);
45 BOXING_CLASSES.put(Float.class, Float.TYPE);
46 BOXING_CLASSES.put(Integer.class, Integer.TYPE);
47 BOXING_CLASSES.put(Long.class, Long.TYPE);
48 BOXING_CLASSES.put(Short.class, Short.TYPE);
49 }
50
51
52
53
54
55
56
57 protected static Class<?> unboxingClass(final Class<?> parm) {
58 return BOXING_CLASSES.getOrDefault(parm, parm);
59 }
60
61
62 protected Class<?> commonClass;
63
64
65 protected boolean isNumber = true;
66
67
68 protected boolean unboxing = true;
69
70
71 protected final Object[] untyped;
72
73
74 protected int added;
75
76
77 protected final boolean extended;
78
79
80
81
82
83
84 public ArrayBuilder(final int size) {
85 this(size, false);
86 }
87
88
89
90
91
92
93
94 public ArrayBuilder(final int size, final boolean extended) {
95 this.untyped = new Object[size];
96 this.extended = extended;
97 }
98 @Override
99 public void add(final Object value) {
100
101 if (!Object.class.equals(commonClass)) {
102 if (value == null) {
103 isNumber = false;
104 unboxing = false;
105 } else {
106 final Class<?> eclass = value.getClass();
107
108 if (commonClass == null) {
109 commonClass = eclass;
110 isNumber = isNumber && Number.class.isAssignableFrom(commonClass);
111 } else if (!commonClass.isAssignableFrom(eclass)) {
112
113 if (isNumber && Number.class.isAssignableFrom(eclass)) {
114 commonClass = Number.class;
115 } else {
116 isNumber = false;
117 commonClass = getCommonSuperClass(commonClass, eclass);
118 }
119 }
120 }
121 }
122 if (added >= untyped.length) {
123 throw new IllegalArgumentException("add() over size");
124 }
125 untyped[added++] = value;
126 }
127
128 @Override
129 public Object create(final boolean e) {
130 if (untyped == null) {
131 return new Object[0];
132 }
133 final int size = added;
134 if (extended || e) {
135 final List<Object> list = newList(commonClass, size);
136 list.addAll(Arrays.asList(untyped).subList(0, size));
137 return list;
138 }
139
140 if (commonClass == null || Object.class.equals(commonClass)) {
141 return untyped.clone();
142 }
143
144 if (unboxing) {
145 commonClass = unboxingClass(commonClass);
146 }
147
148 final Object typed = Array.newInstance(commonClass, size);
149 for (int i = 0; i < size; ++i) {
150 Array.set(typed, i, untyped[i]);
151 }
152 return typed;
153 }
154
155
156
157
158
159
160
161
162
163 protected Class<?> getCommonSuperClass(final Class<?> baseClass, final Class<?> other) {
164 return ClassMisc.getCommonSuperClass(baseClass, other);
165 }
166
167
168
169
170
171
172
173
174
175 protected <T> List<T> newList(final Class<? extends T> clazz, final int size) {
176 return new ArrayList<>(size);
177 }
178 }