1 package net.sourceforge.jparam.conversion.creators;
2
3 import java.util.Arrays;
4 import java.util.HashMap;
5 import java.util.Iterator;
6 import java.util.LinkedList;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10 import java.util.TreeMap;
11
12 import net.sourceforge.jparam.JParamException;
13 import net.sourceforge.jparam.conversion.weights.ScalarConversionWeight;
14 import net.sourceforge.jparam.parser.SimpleLeaf;
15 import net.sourceforge.jparam.parser.TreeNode;
16 import net.sourceforge.jparam.util.MultiMap;
17
18 public class ObjectCreator {
19 private static ObjectCreator objectCreator = null;
20
21 private Map primitiveTypeMap = new HashMap();
22
23 private ObjectCreator() {
24 primitiveTypeMap.put(Boolean.TYPE, Boolean.class);
25 primitiveTypeMap.put(Character.TYPE, Character.class);
26 primitiveTypeMap.put(Byte.TYPE, Byte.class);
27 primitiveTypeMap.put(Short.TYPE, Short.class);
28 primitiveTypeMap.put(Integer.TYPE, Integer.class);
29 primitiveTypeMap.put(Long.TYPE, Long.class);
30 primitiveTypeMap.put(Float.TYPE, Float.class);
31 primitiveTypeMap.put(Double.TYPE, Double.class);
32 }
33
34 public boolean isPrimitive(Class c) {
35 return (c.isPrimitive() || primitiveTypeMap.containsKey(c));
36 }
37
38 public Class getNonPrimitiveClass(Class c) {
39 if (c.isPrimitive()) {
40 return (Class) primitiveTypeMap.get(c);
41 }
42 return c;
43 }
44
45 public static ObjectCreator getInstance() {
46 if (objectCreator == null) {
47 objectCreator = new ObjectCreator();
48 }
49 return objectCreator;
50 }
51
52 public Object create(Class bt, TreeNode[] paramNodes)
53 throws JParamException {
54 Set availableCreators = CreatorRegistry.getInstance().getCreators(bt);
55 boolean isArgNull[] = new boolean[paramNodes.length];
56 for (int i = 0; i < paramNodes.length; i++) {
57 TreeNode node = paramNodes[i];
58
59 if (node instanceof SimpleLeaf) {
60 Object val = node.getConstructedValue(Object.class);
61 if (val == null) {
62 isArgNull[i] = true;
63 }
64 }
65 }
66
67
68
69 List possibleCreators = new LinkedList();
70 for (Iterator iter = availableCreators.iterator(); iter.hasNext();) {
71 ICreator creator = (ICreator) iter.next();
72 Class[] expectedParameters = creator.getParameterTypes();
73
74 if (expectedParameters.length == paramNodes.length) {
75 boolean impossible = false;
76 ScalarConversionWeight[] parametersConversionWeights = new ScalarConversionWeight[paramNodes.length];
77 for (int i = 0; i < expectedParameters.length; i++) {
78 ScalarConversionWeight cw = paramNodes[i]
79 .getConversionWeight(expectedParameters[i]);
80 if ((!creator.canBeNull(i) && isArgNull[i])
81 || (cw
82 .compareTo(ScalarConversionWeight.CONV_IMPOSSIBLE) == 0)) {
83 impossible = true;
84 continue;
85 }
86 parametersConversionWeights[i] = cw;
87 }
88
89 if (!impossible) {
90 possibleCreators.add(new WeightedCreator(creator,
91 parametersConversionWeights));
92 }
93 }
94 }
95
96 if (possibleCreators.size() == 0) {
97 throw new JParamException("No creator available for type: "
98 + bt.getName() + " with parameters: "
99 + Arrays.asList(paramNodes));
100 }
101
102 ICreator[] bestCreators = findBest((WeightedCreator[]) possibleCreators
103 .toArray(new WeightedCreator[possibleCreators.size()]));
104
105 if (bestCreators.length > 1) {
106 throw new JParamException("Ambiguity creating: " + bt.getName()
107 + " with parameters: " + Arrays.asList(paramNodes)
108 + ", available creators: " + Arrays.asList(bestCreators));
109 }
110 ICreator creator = bestCreators[0];
111 Object[] args = new Object[paramNodes.length];
112 Class[] creatorParameters = creator.getParameterTypes();
113 for (int i = 0; i < args.length; i++) {
114 args[i] = paramNodes[i].getConstructedValue(creatorParameters[i]);
115 }
116 return creator.create(args);
117 }
118
119 private ICreator[] findBest(WeightedCreator[] creators) {
120 MultiMap weightedCreators = new MultiMap(new TreeMap());
121
122 Set bestMatches = null;
123 for (int i = 0; i < creators.length; i++) {
124 WeightedCreator curCreator = creators[i];
125
126 int creatorIsAsGoodAs = 0;
127 for (int j = 0; j < creators.length; j++) {
128 if (j == i) {
129 continue;
130 }
131
132 if (curCreator.isAsGood(creators[j])) {
133 creatorIsAsGoodAs++;
134 }
135 }
136
137 weightedCreators.put(new Integer(creatorIsAsGoodAs), curCreator);
138 }
139
140 bestMatches = weightedCreators.get(new Integer(creators.length - 1));
141 ICreator[] c;
142
143 if (bestMatches == null) {
144 c = new ICreator[creators.length];
145 for (int i = 0; i < c.length; i++) {
146 c[i] = creators[i].creator;
147 }
148 } else {
149 boolean areAllAsGood = true;
150
151 if (bestMatches.size() > 1) {
152 for (Iterator i = bestMatches.iterator(); i.hasNext()
153 && areAllAsGood;) {
154 WeightedCreator ic = (WeightedCreator) i.next();
155 for (Iterator j = bestMatches.iterator(); j.hasNext()
156 && areAllAsGood;) {
157 WeightedCreator jc = (WeightedCreator) j.next();
158 if (!jc.isAsGood(ic)) {
159 areAllAsGood = false;
160 }
161 }
162 }
163 }
164
165 if (!areAllAsGood) {
166 c = new ICreator[bestMatches.size()];
167 int i = 0;
168 for (Iterator iter = bestMatches.iterator(); iter.hasNext(); i++) {
169 WeightedCreator wc = (WeightedCreator) iter.next();
170 c[i] = wc.creator;
171 }
172 } else {
173 c = new ICreator[1];
174 c[0] = ((WeightedCreator) bestMatches.iterator().next()).creator;
175 }
176 }
177 return c;
178 }
179
180 public static void clearSingleton() {
181 objectCreator = null;
182 }
183
184 private class WeightedCreator {
185 ICreator creator;
186
187 ScalarConversionWeight parameterWeights[];
188
189 /***
190 * @param creator
191 * @param parameterWeights
192 */
193 public WeightedCreator(ICreator creator,
194 ScalarConversionWeight[] parameterWeights) {
195 super();
196 this.creator = creator;
197 this.parameterWeights = parameterWeights;
198 }
199
200 public boolean isAsGood(WeightedCreator other) {
201 ScalarConversionWeight[] otherParameterWeights = other.parameterWeights;
202
203 int better = 0;
204 int worse = 0;
205
206 for (int i = 0; i < parameterWeights.length; i++) {
207 int comparedWeight = parameterWeights[i]
208 .compareTo(otherParameterWeights[i]);
209 if (comparedWeight > 0) {
210 worse++;
211 } else if (comparedWeight < 0) {
212 better++;
213 }
214
215 return better - worse >= 0;
216 }
217
218 return true;
219 }
220
221 public String toString() {
222 return "Weights: " + Arrays.asList(parameterWeights) + " Creator: "
223 + creator;
224 }
225 }
226 }