001    /*
002     * Copyright 2002-2005 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.binding.form.support;
017    
018    import java.util.HashMap;
019    import java.util.Map;
020    
021    import org.springframework.binding.form.FieldFace;
022    import org.springframework.binding.form.FieldFaceSource;
023    import org.springframework.util.Assert;
024    import org.springframework.util.CachingMapDecorator;
025    
026    /**
027     * A convenience superclass for FieldFaceSource's that require caching to improve the performance of FieldFace lookup.
028     * 
029     * <p>
030     * FieldFace retrieval is delegated to subclasses using the {@link #loadFieldFace(String, Object)} method.
031     * 
032     * @author Oliver Hutchison
033     * @author Mathias Broekelmann
034     */
035    public abstract class CachingFieldFaceSource implements FieldFaceSource {
036    
037        private static final Object DEFAULT_CONTEXT = new Object();
038    
039        /*
040         * A cache with context keys and Map from field to FieldFace values. The keys are held with week references so this
041         * class will not prevent GC of context instances.
042         */
043        private final CachingMapDecorator cachedFieldFaceDescriptors = new CachingMapDecorator(true) {
044            public Object create(Object key) {
045                return new HashMap();
046            }
047        };
048    
049        protected CachingFieldFaceSource() {
050        }
051    
052        public FieldFace getFieldFace(String field) {
053            return getFieldFace(field, null);
054        }
055    
056        public FieldFace getFieldFace(final String field, final Object context) {
057            Map faceDescriptors = (Map) cachedFieldFaceDescriptors.get(context == null ? DEFAULT_CONTEXT : context);
058            FieldFace fieldFaceDescriptor = (FieldFace) faceDescriptors.get(field);
059            if (fieldFaceDescriptor == null) {
060                fieldFaceDescriptor = loadFieldFace(field, context);
061                Assert.notNull(fieldFaceDescriptor, "FieldFace must not be null.");
062                faceDescriptors.put(field, fieldFaceDescriptor);
063            }
064            return fieldFaceDescriptor;
065        }
066    
067        /**
068         * Loads the FieldFace for the given field path and context id. This value will be cached so performance need not be
069         * a concern of this method.
070         * 
071         * @param field
072         *            the form field path
073         * @param context
074         *            optional context for which the FieldFace is being resolved
075         * @return the FieldFace for the given context id (never null).
076         */
077        protected abstract FieldFace loadFieldFace(String field, Object context);
078    }