001    /*
002     * Copyright (c) 2007-2014 Concurrent, Inc. All Rights Reserved.
003     *
004     * Project and contact information: http://www.cascading.org/
005     *
006     * This file is part of the Cascading project.
007     *
008     * Licensed under the Apache License, Version 2.0 (the "License");
009     * you may not use this file except in compliance with the License.
010     * You may obtain a copy of the License at
011     *
012     *     http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing, software
015     * distributed under the License is distributed on an "AS IS" BASIS,
016     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017     * See the License for the specific language governing permissions and
018     * limitations under the License.
019     */
020    
021    package cascading.property;
022    
023    import java.io.Serializable;
024    import java.util.Collection;
025    import java.util.Collections;
026    import java.util.HashMap;
027    import java.util.HashSet;
028    import java.util.Map;
029    import java.util.Set;
030    
031    /**
032     * The ConfigDef class allows for the creation of a configuration properties template to be applied to an existing
033     * properties configuration set.
034     * <p/>
035     * There are three property modes, {@link Mode#DEFAULT}, {@link Mode#REPLACE}, and {@link Mode#UPDATE}.
036     * <p/>
037     * <ul>
038     * <li>A DEFAULT property is only applied if there is no existing value in the property set.</li>
039     * <li>A REPLACE property is always applied overriding any previous values.</li>
040     * <li>An UPDATE property is always applied to an existing property. Usually when the property key represent a list of values.</li>
041     * </ul>
042     */
043    public class ConfigDef implements Serializable
044      {
045      public enum Mode
046        {
047          DEFAULT, REPLACE, UPDATE
048        }
049    
050      public interface Setter
051        {
052        String set( String key, String value );
053    
054        String update( String key, String value );
055    
056        String get( String key );
057        }
058    
059      public interface Getter
060        {
061        String update( String key, String value );
062    
063        String get( String key );
064        }
065    
066      protected Map<Mode, Map<String, String>> config;
067    
068      public ConfigDef()
069        {
070        }
071    
072      /**
073       * Method setProperty sets the value to the given key using the {@link Mode#REPLACE} mode.
074       *
075       * @param key   the key
076       * @param value the value
077       * @return the current ConfigDef instance
078       */
079      public ConfigDef setProperty( String key, String value )
080        {
081        return setProperty( Mode.REPLACE, key, value );
082        }
083    
084      /**
085       * Method setProperty sets the value to the given key using the given {@link Mode} value.
086       *
087       * @param key   the key
088       * @param value the value
089       * @return the current ConfigDef instance
090       */
091      public ConfigDef setProperty( Mode mode, String key, String value )
092        {
093        getMode( mode ).put( key, value );
094    
095        return this;
096        }
097    
098      protected Map<String, String> getMode( Mode mode )
099        {
100        if( config == null )
101          config = new HashMap<Mode, Map<String, String>>();
102    
103        if( !config.containsKey( mode ) )
104          config.put( mode, new HashMap<String, String>() );
105    
106        return config.get( mode );
107        }
108    
109      protected Map<String, String> getModeSafe( Mode mode )
110        {
111        if( config == null )
112          return Collections.EMPTY_MAP;
113    
114        if( !config.containsKey( mode ) )
115          return Collections.EMPTY_MAP;
116    
117        return config.get( mode );
118        }
119    
120      /**
121       * Returns {@code true} if there are no properties.
122       *
123       * @return true if no properties.
124       */
125      public boolean isEmpty()
126        {
127        return config == null || config.isEmpty();
128        }
129    
130      public String apply( String key, Getter getter )
131        {
132        String defaultValue = getModeSafe( Mode.DEFAULT ).get( key );
133        String replaceValue = getModeSafe( Mode.REPLACE ).get( key );
134        String updateValue = getModeSafe( Mode.UPDATE ).get( key );
135    
136        String currentValue = getter.get( key );
137    
138        if( currentValue == null && replaceValue == null && updateValue == null )
139          return defaultValue;
140    
141        if( replaceValue != null )
142          return replaceValue;
143    
144        if( updateValue == null )
145          return currentValue;
146    
147        if( currentValue == null )
148          return updateValue;
149    
150        return getter.update( key, updateValue );
151        }
152    
153      public void apply( Mode mode, Setter setter )
154        {
155        if( !config.containsKey( mode ) )
156          return;
157    
158        for( String key : config.get( mode ).keySet() )
159          {
160          switch( mode )
161            {
162            case DEFAULT:
163              if( setter.get( key ) == null )
164                setter.set( key, config.get( mode ).get( key ) );
165              break;
166            case REPLACE:
167              setter.set( key, config.get( mode ).get( key ) );
168              break;
169            case UPDATE:
170              setter.update( key, config.get( mode ).get( key ) );
171              break;
172            }
173          }
174        }
175    
176      public Collection<String> getAllKeys()
177        {
178        Set<String> keys = new HashSet<String>();
179    
180        for( Map<String, String> map : config.values() )
181          keys.addAll( map.keySet() );
182    
183        return Collections.unmodifiableSet( keys );
184        }
185      }