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.util.ArrayList;
020 import java.util.Iterator;
021 import java.util.LinkedHashMap;
022 import java.util.List;
023 import java.util.Map;
024 import java.util.SortedMap;
025 import java.util.TreeMap;
026
027 /**
028 * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $
029 */
030 public class MapRecipe extends AbstractRecipe {
031 private final List<Object[]> entries;
032 private final String type;
033
034 public MapRecipe() {
035 type = LinkedHashMap.class.getName();
036 entries = new ArrayList<Object[]>();
037 }
038
039 public MapRecipe(String type) {
040 this.type = type;
041 entries = new ArrayList<Object[]>();
042 }
043
044 public MapRecipe(Class type) {
045 this.type = type.getName();
046 if (!RecipeHelper.hasDefaultConstructor(type)) throw new IllegalArgumentException("Type does not have a default constructor " + type);
047 entries = new ArrayList<Object[]>();
048 }
049
050 public MapRecipe(Map map) {
051 if (map == null) throw new NullPointerException("map is null");
052
053 entries = new ArrayList<Object[]>(map.size());
054
055 // If the specified set has a default constructor we will recreate the set, otherwise we use a LinkedHashMap or TreeMap
056 if (RecipeHelper.hasDefaultConstructor(map.getClass())) {
057 this.type = map.getClass().getName();
058 } else if (map instanceof SortedMap) {
059 this.type = TreeMap.class.getName();
060 } else {
061 this.type = LinkedHashMap.class.getName();
062 }
063 putAll(map);
064 }
065
066 public MapRecipe(String type, Map map) {
067 if (map == null) throw new NullPointerException("map is null");
068 this.type = type;
069 entries = new ArrayList<Object[]>(map.size());
070 putAll(map);
071 }
072
073 public MapRecipe(Class type, Map map) {
074 if (map == null) throw new NullPointerException("map is null");
075 if (!RecipeHelper.hasDefaultConstructor(type)) throw new IllegalArgumentException("Type does not have a default constructor " + type);
076 this.type = type.getName();
077 entries = new ArrayList<Object[]>(map.size());
078 putAll(map);
079 }
080
081 public MapRecipe(MapRecipe mapRecipe) {
082 if (mapRecipe == null) throw new NullPointerException("mapRecipe is null");
083 this.type = mapRecipe.type;
084 entries = new ArrayList<Object[]>(mapRecipe.entries);
085 }
086
087 public boolean canCreate(Class type, ClassLoader classLoader) {
088 Class myType = getType(classLoader);
089 return type.isAssignableFrom(myType);
090 }
091
092 public Map create(ClassLoader classLoader) {
093 Class mapType = getType(classLoader);
094
095 if (!RecipeHelper.hasDefaultConstructor(mapType)) {
096 throw new ConstructionException("Type does not have a default constructor " + type);
097 }
098
099 Object o;
100 try {
101 o = mapType.newInstance();
102 } catch (Exception e) {
103 throw new ConstructionException("Error while creating set instance: " + type);
104 }
105
106 if(!(o instanceof Map)) {
107 throw new ConstructionException("Specified map type does not implement the Map interface: " + type);
108 }
109
110 Map instance = (Map) o;
111 for (Object[] entry : entries) {
112 Object key = entry[0];
113 if (key instanceof Recipe) {
114 Recipe recipe = (Recipe) key;
115 try {
116 key = recipe.create(classLoader);
117 } catch (ConstructionException e) {
118 e.setPrependAttributeName("[" + type + " " + key + "]");
119 throw e;
120 }
121 }
122
123 Object value = entry[1];
124 if (value instanceof Recipe) {
125 Recipe recipe = (Recipe) value;
126 try {
127 value = recipe.create(classLoader);
128 } catch (ConstructionException e) {
129 e.setPrependAttributeName("[" + type + " " + key + "]");
130 throw e;
131 }
132 }
133
134 //noinspection unchecked
135 instance.put(key, value);
136 }
137 return instance;
138 }
139
140 private Class getType(ClassLoader classLoader) {
141 Class mapType = null;
142 try {
143 mapType = Class.forName(type, true, classLoader);
144 } catch (ClassNotFoundException e) {
145 throw new ConstructionException("Type class could not be found: " + type);
146 }
147 return mapType;
148 }
149
150 public void put(Object key, Object value) {
151 if (key == null) throw new NullPointerException("key is null");
152 if (!RecipeHelper.isSimpleType(key)) {
153 key = new ValueRecipe(key);
154 }
155 if (!RecipeHelper.isSimpleType(value)) {
156 value = new ValueRecipe(value);
157 }
158 entries.add(new Object[] { key, value});
159 }
160
161 public void putAll(Map map) {
162 if (map == null) throw new NullPointerException("map is null");
163 for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
164 Map.Entry entry = (Map.Entry) iterator.next();
165 Object key = entry.getKey();
166 Object value = entry.getValue();
167 put(key, value);
168 }
169 }
170 }