001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.recipe;
018
019 import java.lang.reflect.Array;
020 import java.lang.reflect.Type;
021 import java.util.ArrayList;
022 import java.util.Collection;
023 import java.util.Collections;
024 import java.util.EnumSet;
025 import java.util.List;
026
027 /**
028 * @version $Rev: 766039 $ $Date: 2009-04-17 10:55:47 -0400 (Fri, 17 Apr 2009) $
029 */
030 public class ArrayRecipe extends AbstractRecipe {
031 private final List<Object> list;
032 private String typeName;
033 private Class typeClass;
034 private final EnumSet<Option> options = EnumSet.noneOf(Option.class);
035
036 public ArrayRecipe() {
037 list = new ArrayList<Object>();
038 }
039
040 public ArrayRecipe(String type) {
041 list = new ArrayList<Object>();
042 this.typeName = type;
043 }
044
045 public ArrayRecipe(Class type) {
046 if (type == null) throw new NullPointerException("type is null");
047
048 this.list = new ArrayList<Object>();
049 this.typeClass = type;
050 }
051
052 public ArrayRecipe(ArrayRecipe collectionRecipe) {
053 if (collectionRecipe == null) throw new NullPointerException("setRecipe is null");
054 this.typeName = collectionRecipe.typeName;
055 this.typeClass = collectionRecipe.typeClass;
056 list = new ArrayList<Object>(collectionRecipe.list);
057 }
058
059 public void allow(Option option) {
060 options.add(option);
061 }
062
063 public void disallow(Option option) {
064 options.remove(option);
065 }
066
067 public List<Recipe> getNestedRecipes() {
068 List<Recipe> nestedRecipes = new ArrayList<Recipe>(list.size());
069 for (Object o : list) {
070 if (o instanceof Recipe) {
071 Recipe recipe = (Recipe) o;
072 nestedRecipes.add(recipe);
073 }
074 }
075 return nestedRecipes;
076 }
077
078 public List<Recipe> getConstructorRecipes() {
079 if (!options.contains(Option.LAZY_ASSIGNMENT)) {
080 return getNestedRecipes();
081 }
082 return Collections.emptyList();
083 }
084
085 public boolean canCreate(Type expectedType) {
086 Class expectedClass = RecipeHelper.toClass(expectedType);
087 Class myType = getType(expectedType);
088 return expectedClass.isArray() && RecipeHelper.isAssignable(expectedClass.getComponentType(), myType);
089 }
090
091 protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException {
092 Class type = getType(expectedType);
093
094 // create array instance
095 Object array;
096 try {
097 array = Array.newInstance(type, list.size());
098 } catch (Exception e) {
099 throw new ConstructionException("Error while creating array instance: " + type.getName());
100 }
101
102 // add to execution context if name is specified
103 if (getName() != null) {
104 ExecutionContext.getContext().addObject(getName(), array);
105 }
106
107 boolean refAllowed = options.contains(Option.LAZY_ASSIGNMENT);
108
109 int index = 0;
110 for (Object value : list) {
111 value = RecipeHelper.convert(type, value, refAllowed);
112
113 if (value instanceof Reference) {
114 Reference reference = (Reference) value;
115 reference.setAction(new UpdateArray(array, index));
116 } else {
117 //noinspection unchecked
118 Array.set(array, index, value);
119 }
120 index++;
121 }
122
123 return array;
124 }
125
126 private Class getType(Type expectedType) {
127 Class expectedClass = RecipeHelper.toClass(expectedType);
128 if (expectedClass.isArray()) {
129 expectedClass = expectedClass.getComponentType();
130 }
131 Class type = expectedClass;
132 if (typeClass != null || typeName != null) {
133 type = typeClass;
134 if (type == null) {
135 try {
136 type = RecipeHelper.loadClass(typeName);
137 } catch (ClassNotFoundException e) {
138 throw new ConstructionException("Type class could not be found: " + typeName);
139 }
140 }
141
142 // in case where expectedType is a subclass of the assigned type
143 if (type.isAssignableFrom(expectedClass)) {
144 type = expectedClass;
145 }
146 }
147
148 return type;
149 }
150
151 public void add(Object value) {
152 list.add(value);
153 }
154
155 public void addAll(Collection<?> value) {
156 list.addAll(value);
157 }
158
159 public void remove(Object value) {
160 list.remove(value);
161 }
162
163 public void removeAll(Object value) {
164 list.remove(value);
165 }
166
167 public List<Object> getAll() {
168 return Collections.unmodifiableList(list);
169 }
170
171 private static class UpdateArray implements Reference.Action {
172 private final Object array;
173 private final int index;
174
175 public UpdateArray(Object array, int index) {
176 this.array = array;
177 this.index = index;
178 }
179
180 @SuppressWarnings({"unchecked"})
181 public void onSet(Reference ref) {
182 Array.set(array, index, ref.get());
183 }
184 }
185 }