001/*
002 * Copyright (c) 2007-2017 Xplenty, 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
021package cascading.property;
022
023import java.io.Serializable;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.Map;
029import 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 */
043public 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  }