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 }