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 */ 017package org.apache.xbean.recipe; 018 019import java.lang.reflect.Array; 020import java.lang.reflect.Type; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.EnumSet; 025import java.util.List; 026 027import org.apache.xbean.propertyeditor.PropertyEditorRegistry; 028 029/** 030 * @version $Rev: 1837693 $ $Date: 2018-08-09 09:36:49 +0200 (Thu, 09 Aug 2018) $ 031 */ 032public class ArrayRecipe extends AbstractRecipe { 033 private final List<Object> list; 034 private String typeName; 035 private Class typeClass; 036 private PropertyEditorRegistry registry; 037 private final EnumSet<Option> options = EnumSet.noneOf(Option.class); 038 039 public ArrayRecipe() { 040 list = new ArrayList<Object>(); 041 } 042 043 public ArrayRecipe(String type) { 044 list = new ArrayList<Object>(); 045 this.typeName = type; 046 } 047 048 public ArrayRecipe(Class type) { 049 if (type == null) throw new NullPointerException("type is null"); 050 051 this.list = new ArrayList<Object>(); 052 this.typeClass = type; 053 } 054 055 public ArrayRecipe(ArrayRecipe collectionRecipe) { 056 if (collectionRecipe == null) throw new NullPointerException("setRecipe is null"); 057 this.typeName = collectionRecipe.typeName; 058 this.typeClass = collectionRecipe.typeClass; 059 list = new ArrayList<Object>(collectionRecipe.list); 060 } 061 062 public void setRegistry(final PropertyEditorRegistry registry) { 063 this.registry = registry; 064 } 065 066 public void allow(Option option) { 067 options.add(option); 068 } 069 070 public void disallow(Option option) { 071 options.remove(option); 072 } 073 074 public List<Recipe> getNestedRecipes() { 075 List<Recipe> nestedRecipes = new ArrayList<Recipe>(list.size()); 076 for (Object o : list) { 077 if (o instanceof Recipe) { 078 Recipe recipe = (Recipe) o; 079 nestedRecipes.add(recipe); 080 } 081 } 082 return nestedRecipes; 083 } 084 085 public List<Recipe> getConstructorRecipes() { 086 if (!options.contains(Option.LAZY_ASSIGNMENT)) { 087 return getNestedRecipes(); 088 } 089 return Collections.emptyList(); 090 } 091 092 public boolean canCreate(Type expectedType) { 093 Class expectedClass = RecipeHelper.toClass(expectedType); 094 Class myType = getType(expectedType); 095 return expectedClass.isArray() && RecipeHelper.isAssignable(expectedClass.getComponentType(), myType); 096 } 097 098 protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException { 099 Class type = getType(expectedType); 100 101 // create array instance 102 Object array; 103 try { 104 array = Array.newInstance(type, list.size()); 105 } catch (Exception e) { 106 throw new ConstructionException("Error while creating array instance: " + type.getName()); 107 } 108 109 // add to execution context if name is specified 110 if (getName() != null) { 111 ExecutionContext.getContext().addObject(getName(), array); 112 } 113 114 boolean refAllowed = options.contains(Option.LAZY_ASSIGNMENT); 115 116 int index = 0; 117 for (Object value : list) { 118 value = RecipeHelper.convert(type, value, refAllowed, registry); 119 120 if (value instanceof Reference) { 121 Reference reference = (Reference) value; 122 reference.setAction(new UpdateArray(array, index)); 123 } else { 124 //noinspection unchecked 125 Array.set(array, index, value); 126 } 127 index++; 128 } 129 130 return array; 131 } 132 133 private Class getType(Type expectedType) { 134 Class expectedClass = RecipeHelper.toClass(expectedType); 135 if (expectedClass.isArray()) { 136 expectedClass = expectedClass.getComponentType(); 137 } 138 Class type = expectedClass; 139 if (typeClass != null || typeName != null) { 140 type = typeClass; 141 if (type == null) { 142 try { 143 type = RecipeHelper.loadClass(typeName); 144 } catch (ClassNotFoundException e) { 145 throw new ConstructionException("Type class could not be found: " + typeName); 146 } 147 } 148 149 // in case where expectedType is a subclass of the assigned type 150 if (type.isAssignableFrom(expectedClass)) { 151 type = expectedClass; 152 } 153 } 154 155 return type; 156 } 157 158 public void add(Object value) { 159 list.add(value); 160 } 161 162 public void addAll(Collection<?> value) { 163 list.addAll(value); 164 } 165 166 public void remove(Object value) { 167 list.remove(value); 168 } 169 170 public void removeAll(Object value) { 171 list.remove(value); 172 } 173 174 public List<Object> getAll() { 175 return Collections.unmodifiableList(list); 176 } 177 178 private static class UpdateArray implements Reference.Action { 179 private final Object array; 180 private final int index; 181 182 public UpdateArray(Object array, int index) { 183 this.array = array; 184 this.index = index; 185 } 186 187 @SuppressWarnings({"unchecked"}) 188 public void onSet(Reference ref) { 189 Array.set(array, index, ref.get()); 190 } 191 } 192}