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    }