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