001    /*
002     * Copyright 2002-2004 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.richclient.form.binding.swing;
017    
018    import java.util.Map;
019    
020    import javax.swing.JComponent;
021    import javax.swing.JScrollPane;
022    
023    import org.springframework.binding.form.FormModel;
024    import org.springframework.richclient.form.binding.BinderSelectionStrategy;
025    import org.springframework.richclient.form.binding.Binding;
026    import org.springframework.richclient.form.binding.support.AbstractBinder;
027    import org.springframework.util.Assert;
028    
029    /**
030     * A binder that binds a scroll pane and the scroll pane's view. If the 
031     * scroll pane does not have a view a default binding will be created and
032     * set as the scroll pane's view.
033     * 
034     * @author Oliver Hutchison
035     */
036    public class ScrollPaneBinder extends AbstractBinder {
037    
038        private final BinderSelectionStrategy viewBinderSelectionStrategy;
039    
040        private final Class defaultViewType;
041    
042        /**
043         * Constructs a new ScrollPaneBinder
044         * 
045         * @param viewBinderSelectionStrategy the {@link BinderSelectionStrategy} which will be used 
046         * to select a Binder for the scrollpane's view component.
047         * @param defaultViewType the type of the component that will be created and bound if the 
048         * scroll pane does not already have a view    
049         */
050        public ScrollPaneBinder(BinderSelectionStrategy viewBinderSelectionStrategy, Class defaultViewType) {
051            super(null);
052            this.viewBinderSelectionStrategy = viewBinderSelectionStrategy;
053            this.defaultViewType = defaultViewType;
054        }
055    
056        protected JComponent createControl(Map context) {
057            return getComponentFactory().createScrollPane();
058        }
059    
060        protected Binding doBind(JComponent control, FormModel formModel, String formPropertyPath, Map context) {
061            Assert.isTrue(control instanceof JScrollPane, "Control must be an instance of JScrollPane.");
062            JScrollPane scrollPane = (JScrollPane)control;
063            Binding viewBinding = getViewBinding(scrollPane, formModel, formPropertyPath, context);
064            return new ScrollPaneDecoratedBinding(viewBinding, scrollPane);
065        }
066    
067        protected Binding getViewBinding(JScrollPane scrollPane, FormModel formModel, String formPropertyPath, Map context) {
068            JComponent view = (JComponent)scrollPane.getViewport().getView();
069            if (view == null) {
070                Binding viewBinding = viewBinderSelectionStrategy.selectBinder(defaultViewType, formModel, formPropertyPath)
071                        .bind(formModel, formPropertyPath, context);
072                scrollPane.setViewportView(viewBinding.getControl());
073                return viewBinding;
074            }
075            Binding existingBinding = (Binding)view.getClientProperty(BINDING_CLIENT_PROPERTY_KEY);
076            if (existingBinding != null) {
077                return existingBinding;
078            }
079            return viewBinderSelectionStrategy.selectBinder(view.getClass(), formModel, formPropertyPath).bind(
080                    view, formModel, formPropertyPath, context);
081        }
082    
083        protected void validateContextKeys(Map context) {
084            // do nothing as we pass the context on to
085            // the scroll pane's view binder
086        }
087    }