View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.xbean.recipe;
18  
19  import java.lang.reflect.Array;
20  import java.lang.reflect.Type;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.EnumSet;
25  import java.util.List;
26  
27  /**
28   * @version $Rev: 766039 $ $Date: 2009-04-17 07:55:47 -0700 (Fri, 17 Apr 2009) $
29   */
30  public class ArrayRecipe extends AbstractRecipe {
31      private final List<Object> list;
32      private String typeName;
33      private Class typeClass;
34      private final EnumSet<Option> options = EnumSet.noneOf(Option.class);
35  
36      public ArrayRecipe() {
37          list = new ArrayList<Object>();
38      }
39  
40      public ArrayRecipe(String type) {
41          list = new ArrayList<Object>();
42          this.typeName = type;
43      }
44  
45      public ArrayRecipe(Class type) {
46          if (type == null) throw new NullPointerException("type is null");
47  
48          this.list = new ArrayList<Object>();
49          this.typeClass = type;
50      }
51  
52      public ArrayRecipe(ArrayRecipe collectionRecipe) {
53          if (collectionRecipe == null) throw new NullPointerException("setRecipe is null");
54          this.typeName = collectionRecipe.typeName;
55          this.typeClass = collectionRecipe.typeClass;
56          list = new ArrayList<Object>(collectionRecipe.list);
57      }
58  
59      public void allow(Option option) {
60          options.add(option);
61      }
62  
63      public void disallow(Option option) {
64          options.remove(option);
65      }
66  
67      public List<Recipe> getNestedRecipes() {
68          List<Recipe> nestedRecipes = new ArrayList<Recipe>(list.size());
69          for (Object o : list) {
70              if (o instanceof Recipe) {
71                  Recipe recipe = (Recipe) o;
72                  nestedRecipes.add(recipe);
73              }
74          }
75          return nestedRecipes;
76      }
77  
78      public List<Recipe> getConstructorRecipes() {
79          if (!options.contains(Option.LAZY_ASSIGNMENT)) {
80              return getNestedRecipes();
81          }
82          return Collections.emptyList();
83      }
84  
85      public boolean canCreate(Type expectedType) {
86          Class expectedClass = RecipeHelper.toClass(expectedType);
87          Class myType = getType(expectedType);
88          return expectedClass.isArray() && RecipeHelper.isAssignable(expectedClass.getComponentType(), myType);
89      }
90  
91      protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException {
92          Class type = getType(expectedType);
93  
94          // create array instance
95          Object array;
96          try {
97              array = Array.newInstance(type, list.size());
98          } catch (Exception e) {
99              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 }