001    package org.apache.commons.contract.constraints;
002    
003    import java.util.ArrayList;
004    import java.util.HashMap;
005    import java.util.Iterator;
006    import java.util.List;
007    import java.util.Map;
008    
009    import org.apache.commons.contract.Context;
010    import org.apache.commons.contract.descriptor.ParameterDescriptor;
011    import org.apache.commons.contract.i18n.ParameterBundle;
012    import org.apache.commons.i18n.bundles.ErrorBundle;
013    import org.apache.commons.i18n.bundles.TextBundle;
014    
015    public class MapConstraints implements Constraints {
016        public final static MapConstraints UNCONSTRAINED = new MapConstraints(new ParameterDescriptor(MapConstraints.ALL, new ParameterBundle("mapEntry/any"), Unconstrained.UNCONSTRAINED)); 
017        public final static String ALL = "*";
018            
019            protected List entryConstraints = new ArrayList();
020    
021            public MapConstraints() {
022            }
023            
024        public MapConstraints(List entryDescriptors) {
025            this.entryConstraints = entryDescriptors;
026        }
027    
028        public MapConstraints(ParameterDescriptor parameterDescriptor) {
029            entryConstraints.add(parameterDescriptor);
030        }
031    
032        public MapConstraints(ParameterDescriptor[] parameterDescriptors) {
033            for ( int i = 0; i < parameterDescriptors.length; i++ ) {
034                entryConstraints.add(parameterDescriptors[i]);
035            }
036        }
037    
038        public void addEntryDescriptor(ParameterDescriptor parameterDescriptor) {
039            entryConstraints.add(parameterDescriptor);
040        }
041    
042        public List getEntryDescriptors() {
043            return entryConstraints;
044        }
045        
046        public Object cast(Object value, Context context) throws CastException {
047            if ( entryConstraints.isEmpty() ) {
048                    throw new CastException(new ErrorBundle("noMapEntryDescriptorsFound"));
049            }
050            if ( value instanceof Map ) {
051                return castedMap((Map)value, context);
052            } else if ( value instanceof Map ) {
053                return castedMap((Map)value, context);
054            } else {
055                throw new CastException(new ErrorBundle("uncastableMapValue", new Object[] { value }));
056            }
057        }
058    
059        protected Map castedMap(Map map, Context context) throws CastException {
060            Map castedMap = new HashMap(map);
061            for ( Iterator i = entryConstraints.iterator(); i.hasNext(); ) {
062                ParameterDescriptor parameterDescriptor = (ParameterDescriptor)i.next();
063                Constraints entryDescriptor = parameterDescriptor.getConstraints();
064                String key = parameterDescriptor.getName();
065                if ( key.equals(ALL) ) {
066                    for ( Iterator j = castedMap.entrySet().iterator(); j.hasNext(); ) {
067                            Map.Entry entry = (Map.Entry)j.next();
068                            Object value = entry.getValue();
069                        if ( value instanceof Evaluatable ) {
070                            try {
071                                value = ((Evaluatable)value).evaluate(context);
072                            } catch (Exception e) {
073                                throw new CastException(new ErrorBundle("evaluatingAnyFailed"), e);
074                            }
075                        }
076                            castedMap.put(entry.getKey(), entryDescriptor.cast(value, context));
077                    }
078                } else {
079                    if ( !castedMap.containsKey(key) ) {
080                            if ( !parameterDescriptor.isRequired() ) {
081                                    castedMap.put(key, parameterDescriptor.getDefaultValue());
082                            }
083                    } else {
084                            Object object = castedMap.get(key);
085                            if ( object == null && parameterDescriptor.getDefaultValue() == null || object.equals(parameterDescriptor.getDefaultValue())) {
086                                    castedMap.put(key, object);
087                            } else {
088                            if ( object instanceof Evaluatable ) {
089                                try {
090                                    object = ((Evaluatable)object).evaluate(context);
091                                } catch (Exception e) {
092                                    throw new CastException(new ErrorBundle("evaluatingAnyFailed"), e);
093                                }
094                            }
095                                    castedMap.put(key, entryDescriptor.cast(object, context));
096                            }
097                    }
098                }
099            }
100            return castedMap;
101        }
102        
103        public void validate(Object value, Context context) throws ValidationException {
104            Map map = (Map)value;
105            for ( Iterator i = entryConstraints.iterator(); i.hasNext(); ) {
106                ParameterDescriptor parameterDescriptor = (ParameterDescriptor)i.next();
107                Constraints entryDescriptor = parameterDescriptor.getConstraints();
108                String key = parameterDescriptor.getName();
109                if ( key.equals(ALL) ) {
110                    for ( Iterator j = map.values().iterator(); j.hasNext(); ) {
111                            Object entryValue = j.next();
112                            if ( entryValue != null ) {
113                                    entryDescriptor.validate(entryValue, context);
114                            }
115                    }
116                } else {
117                    if ( !map.containsKey(key) ) {
118                            if ( parameterDescriptor.isRequired() ) {
119                                    throw new ValidationException(new ErrorBundle("mapEntryMissing", new String[] { key }));
120                            }
121                    } else {
122                            Object entryValue = map.get(key);
123                            if ( entryValue != null ) {
124                                    entryDescriptor.validate(entryValue, context);
125                            }
126                    }
127                }
128            }
129       }
130        
131        public TextBundle verboseConstraints() {
132            if ( entryConstraints.isEmpty() ) {
133                return new TextBundle("invalidMapConstraints");
134            }
135            return new TextBundle("unconstrainedMap");
136        }
137    
138    }