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 16:55:47 +0200 (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    }