001    // Copyright 2006-2007 Daniel Gredler
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package net.sf.beanform.binding;
016    
017    import java.util.Map;
018    import java.util.Map.Entry;
019    
020    import org.apache.hivemind.Location;
021    import org.apache.tapestry.IBinding;
022    import org.apache.tapestry.IComponent;
023    
024    /**
025     * A special binding type that allows us to simulate adding and removing
026     * bindings to components. This binding behaves like a custom binding unless
027     * there is no custom binding, in which case it behaves like the standard
028     * (default) binding for a given binding name.
029     *
030     * @author Daniel Gredler
031     */
032    public class DualBinding implements IBinding {
033    
034        private IBinding custom;
035        private IBinding standard;
036    
037        public DualBinding( IBinding custom, IBinding standard ) {
038            this.custom = custom;
039            this.standard = standard;
040        }
041    
042        public void setCustom( IBinding custom ) {
043            this.custom = custom;
044        }
045    
046        public Object getObject() {
047            if( this.custom != null ) return this.custom.getObject();
048            else if( this.standard != null ) return this.standard.getObject();
049            else return null;
050        }
051    
052        public Object getObject( Class type ) {
053            if( this.custom != null ) return this.custom.getObject( type );
054            else if( this.standard != null ) return this.standard.getObject( type );
055            else return null;
056        }
057    
058        public boolean isInvariant() {
059            return false;
060        }
061    
062        public void setObject( Object value ) {
063            if( this.custom != null ) this.custom.setObject( value );
064            else if( this.standard != null ) this.standard.setObject( value );
065        }
066    
067        public String getDescription() {
068            if( this.custom != null ) return this.custom.getDescription();
069            else if( this.standard != null ) return this.standard.getDescription();
070            else return null;
071        }
072    
073        public Location getLocation() {
074            if( this.custom != null ) return this.custom.getLocation();
075            else if( this.standard != null ) return this.standard.getLocation();
076            else return null;
077        }
078    
079        /**
080         * Adds temporary custom bindings to a component by overriding the existing bindings
081         * with {@link DualBinding} instances whose behavior can later be reverted.
082         */
083        public static void addCustomBindings( IComponent component, Map<String, IBinding> bindings, boolean clearFirst ) {
084            if( clearFirst ) {
085                DualBinding.removeCustomBindings( component );
086            }
087            for( Entry<String, IBinding> entry : bindings.entrySet() ) {
088                String bindingName = entry.getKey();
089                IBinding custom = entry.getValue();
090                IBinding existing = component.getBinding( bindingName );
091                DualBinding dual;
092                if( existing instanceof DualBinding ) {
093                    dual = (DualBinding) existing;
094                    dual.setCustom( custom );
095                }
096                else {
097                    dual = new DualBinding( custom, existing );
098                }
099                component.setBinding( bindingName, dual );
100            }
101        }
102    
103        /**
104         * Reverts the behavior of the specified component's temporarily customized bindings to
105         * whatever it was prior to customization.
106         */
107        @SuppressWarnings( "unchecked" )
108        public static void removeCustomBindings( IComponent component ) {
109            Map<String, IBinding> bindings = component.getBindings();
110            for( IBinding binding : bindings.values() ) {
111                if( binding instanceof DualBinding ) {
112                    DualBinding dual = (DualBinding) binding;
113                    dual.setCustom( null );
114                }
115            }
116        }
117    
118    }