1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.xbean.recipe;
18
19 import java.lang.reflect.Type;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Dictionary;
24 import java.util.EnumSet;
25 import java.util.LinkedHashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.SortedSet;
30 import java.util.TreeSet;
31
32
33
34
35 public class CollectionRecipe extends AbstractRecipe {
36 private final List<Object> list;
37 private String typeName;
38 private Class typeClass;
39 private final EnumSet<Option> options = EnumSet.noneOf(Option.class);
40
41 public CollectionRecipe() {
42 list = new ArrayList<Object>();
43 }
44
45 public CollectionRecipe(String type) {
46 list = new ArrayList<Object>();
47 this.typeName = type;
48 }
49
50 public CollectionRecipe(Class type) {
51 if (type == null) throw new NullPointerException("type is null");
52 this.list = new ArrayList<Object>();
53 this.typeClass = type;
54 }
55
56 public CollectionRecipe(Collection<?> collection) {
57 if (collection == null) throw new NullPointerException("collection is null");
58
59 this.list = new ArrayList<Object>(collection);
60
61
62 if (RecipeHelper.hasDefaultConstructor(collection.getClass())) {
63 this.typeClass = collection.getClass();
64 } else if (collection instanceof SortedSet) {
65 this.typeClass = SortedSet.class;
66 } else if (collection instanceof Set) {
67 this.typeClass = Set.class;
68 } else if (collection instanceof List) {
69 this.typeClass = List.class;
70 } else {
71 this.typeClass = Collection.class;
72 }
73 }
74
75 public CollectionRecipe(CollectionRecipe collectionRecipe) {
76 if (collectionRecipe == null) throw new NullPointerException("setRecipe is null");
77 this.typeName = collectionRecipe.typeName;
78 this.typeClass = collectionRecipe.typeClass;
79 list = new ArrayList<Object>(collectionRecipe.list);
80 }
81
82 public void allow(Option option) {
83 options.add(option);
84 }
85
86 public void disallow(Option option) {
87 options.remove(option);
88 }
89
90 public List<Recipe> getNestedRecipes() {
91 List<Recipe> nestedRecipes = new ArrayList<Recipe>(list.size());
92 for (Object o : list) {
93 if (o instanceof Recipe) {
94 Recipe recipe = (Recipe) o;
95 nestedRecipes.add(recipe);
96 }
97 }
98 return nestedRecipes;
99 }
100
101 public List<Recipe> getConstructorRecipes() {
102 if (!options.contains(Option.LAZY_ASSIGNMENT)) {
103 return getNestedRecipes();
104 }
105 return Collections.emptyList();
106 }
107
108 public boolean canCreate(Type expectedType) {
109 Class myType = getType(expectedType);
110 return RecipeHelper.isAssignable(expectedType, myType);
111 }
112
113 protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException {
114 Class type = getType(expectedType);
115
116 if (!RecipeHelper.hasDefaultConstructor(type)) {
117 throw new ConstructionException("Type does not have a default constructor " + type.getName());
118 }
119
120
121 Object o;
122 try {
123 o = type.newInstance();
124 } catch (Exception e) {
125 throw new ConstructionException("Error while creating collection instance: " + type.getName());
126 }
127 if (!(o instanceof Collection)) {
128 throw new ConstructionException("Specified collection type does not implement the Collection interface: " + type.getName());
129 }
130 Collection instance = (Collection) o;
131
132
133 if (getName() != null) {
134 ExecutionContext.getContext().addObject(getName(), instance);
135 }
136
137
138 Type[] typeParameters = RecipeHelper.getTypeParameters(Collection.class, expectedType);
139 Type componentType = Object.class;
140 if (typeParameters != null && typeParameters.length == 1 && typeParameters[0] instanceof Class) {
141 componentType = typeParameters[0];
142 }
143
144 boolean refAllowed = options.contains(Option.LAZY_ASSIGNMENT);
145
146 int index = 0;
147 for (Object value : list) {
148 value = RecipeHelper.convert(componentType, value, refAllowed);
149
150 if (value instanceof Reference) {
151 Reference reference = (Reference) value;
152 if (instance instanceof List) {
153
154
155 instance.add(null);
156 reference.setAction(new UpdateList((List) instance, index));
157 } else {
158 reference.setAction(new UpdateCollection(instance));
159 }
160 } else {
161
162 instance.add(value);
163 }
164 index++;
165 }
166 return instance;
167 }
168
169 private Class getType(Type expectedType) {
170 Class expectedClass = RecipeHelper.toClass(expectedType);
171 if (typeClass != null || typeName != null) {
172 Class type = typeClass;
173 if (type == null) {
174 try {
175 type = RecipeHelper.loadClass(typeName);
176 } catch (ClassNotFoundException e) {
177 throw new ConstructionException("Type class could not be found: " + typeName);
178 }
179 }
180
181
182
183 if (type.isAssignableFrom(expectedClass)) {
184 return getCollection(expectedClass);
185 } else {
186 return getCollection(type);
187 }
188 }
189
190
191 return getCollection(expectedClass);
192 }
193
194 private Class getCollection(Class type) {
195 if (RecipeHelper.hasDefaultConstructor(type)) {
196 return type;
197 } else if (SortedSet.class.isAssignableFrom(type)) {
198 return TreeSet.class;
199 } else if (Set.class.isAssignableFrom(type)) {
200 return LinkedHashSet.class;
201 } else if (List.class.isAssignableFrom(type)) {
202 return ArrayList.class;
203 } else {
204 return ArrayList.class;
205 }
206 }
207
208 public void add(Object value) {
209 list.add(value);
210 }
211
212 public void addAll(Collection<?> value) {
213 list.addAll(value);
214 }
215
216 public void remove(Object value) {
217 list.remove(value);
218 }
219
220 public void removeAll(Object value) {
221 list.remove(value);
222 }
223
224 public List<Object> getAll() {
225 return Collections.unmodifiableList(list);
226 }
227
228 private static class UpdateCollection implements Reference.Action {
229 private final Collection collection;
230
231 public UpdateCollection(Collection collection) {
232 this.collection = collection;
233 }
234
235 @SuppressWarnings({"unchecked"})
236 public void onSet(Reference ref) {
237 collection.add(ref.get());
238 }
239 }
240
241 private static class UpdateList implements Reference.Action {
242 private final List list;
243 private final int index;
244
245 public UpdateList(List list, int index) {
246 this.list = list;
247 this.index = index;
248 }
249
250 @SuppressWarnings({"unchecked"})
251 public void onSet(Reference ref) {
252 list.set(index, ref.get());
253 }
254 }
255 }