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.util.HashMap;
025import java.util.HashSet;
026import java.util.Map;
027import java.util.Objects;
028import java.util.Properties;
029import java.util.Set;
030
031import cascading.flow.planner.ScopedElement;
032
033import static cascading.util.Util.isEmpty;
034
035/** Class PropertyUtil is a static helper class for handling properties. */
036public class PropertyUtil
037  {
038  public static String getProperty( Map<Object, Object> properties, String property )
039    {
040    return getProperty( properties, property, (String) null );
041    }
042
043  public static boolean getBooleanProperty( Map<Object, Object> properties, String property, boolean defaultValue )
044    {
045    return getBooleanProperty( System.getProperties(), properties, property, defaultValue );
046    }
047
048  public static boolean getBooleanProperty( Properties defaultProperties, Map<Object, Object> properties, String property, boolean defaultValue )
049    {
050    String result;
051
052    if( defaultProperties == null )
053      result = getProperty( properties, property );
054    else
055      result = getProperty( properties, property, defaultProperties.getProperty( property ) );
056
057    if( isEmpty( result ) )
058      return defaultValue;
059
060    return Boolean.parseBoolean( result );
061    }
062
063  public static int getIntProperty( Map<Object, Object> properties, String property, int defaultValue )
064    {
065    return getIntProperty( System.getProperties(), properties, property, defaultValue );
066    }
067
068  public static int getIntProperty( Properties defaultProperties, Map<Object, Object> properties, String property, int defaultValue )
069    {
070    String result;
071
072    if( defaultProperties == null )
073      result = getProperty( properties, property );
074    else
075      result = getProperty( properties, property, defaultProperties.getProperty( property ) );
076
077    if( isEmpty( result ) )
078      return defaultValue;
079
080    return Integer.parseInt( result );
081    }
082
083  public static String getStringProperty( Map<Object, Object> properties, String property )
084    {
085    return getStringProperty( System.getProperties(), properties, property );
086    }
087
088  public static String getStringProperty( Properties defaultProperties, Map<Object, Object> properties, String property )
089    {
090    if( defaultProperties == null )
091      return getProperty( properties, property );
092
093    return getProperty( properties, property, defaultProperties.getProperty( property ) );
094    }
095
096  public static <A> A getProperty( Map<Object, Object> properties, String key, A defaultValue )
097    {
098    if( properties == null )
099      return defaultValue;
100
101    A value;
102
103    if( properties instanceof Properties )
104      value = (A) ( (Properties) properties ).getProperty( key );
105    else
106      value = (A) properties.get( key );
107
108    return value == null ? defaultValue : value;
109    }
110
111  public static String getProperty( final Map<Object, Object> properties, ScopedElement flowElement, String property )
112    {
113    if( flowElement != null && flowElement.hasConfigDef() )
114      return PropertyUtil.getProperty( properties, flowElement.getConfigDef(), property );
115
116    return PropertyUtil.getProperty( properties, property );
117    }
118
119  public static String getProperty( final Map<Object, Object> properties, ConfigDef configDef, String property )
120    {
121    return configDef.apply( property, new ConfigDef.Getter()
122      {
123      @Override
124      public String update( String key, String value )
125        {
126        return value;
127        }
128
129      @Override
130      public String get( String key )
131        {
132        return getProperty( properties, key );
133        }
134      } );
135    }
136
137  public static void setProperty( Map<Object, Object> properties, String key, String value )
138    {
139    if( properties == null || value == null || value.isEmpty() )
140      return;
141
142    if( properties instanceof Properties )
143      ( (Properties) properties ).setProperty( key, value );
144    else
145      properties.put( key, value );
146    }
147
148  public static Properties createProperties( Iterable<Map.Entry<String, String>> defaultProperties )
149    {
150    Properties properties = new Properties();
151
152    for( Map.Entry<String, String> property : defaultProperties )
153      properties.setProperty( property.getKey(), property.getValue() );
154
155    return properties;
156    }
157
158  public static Properties createProperties( Map<Object, Object> properties, Properties defaultProperties )
159    {
160    Properties results = defaultProperties == null ? new Properties() : new Properties( defaultProperties );
161
162    if( properties == null )
163      return results;
164
165    Set<Object> keys = new HashSet<Object>( properties.keySet() );
166
167    // keys will only be grabbed if both key/value are String, so keep orig keys
168    if( properties instanceof Properties )
169      keys.addAll( ( (Properties) properties ).stringPropertyNames() );
170
171    for( Object key : keys )
172      {
173      Object value = properties.get( key );
174
175      if( value == null && properties instanceof Properties && key instanceof String )
176        value = ( (Properties) properties ).getProperty( (String) key );
177
178      if( value == null ) // don't stuff null values
179        continue;
180
181      // don't let these objects pass, even though toString is called below.
182      if( value instanceof Class )
183        continue;
184
185      results.setProperty( key.toString(), value.toString() );
186      }
187
188    return results;
189    }
190
191  public static Map<Object, Object> asFlatMap( Map<Object, Object> properties )
192    {
193    if( !( properties instanceof Properties ) )
194      return properties;
195
196    Map<Object, Object> map = new HashMap<>();
197    Properties props = (Properties) properties;
198
199    for( String property : props.stringPropertyNames() )
200      map.put( property, props.getProperty( property ) );
201
202    return map;
203    }
204
205  /**
206   * Merge all properties instances into one.
207   * <p>
208   * Always returns a new Properties instance.
209   * <p>
210   * Subsequent values to not overwrite earlier values, that is, the left most instances
211   * have more precedence, that is, won't be replaced.
212   *
213   * @param properties
214   * @return a new Properties instance.
215   */
216  public static Properties merge( Properties... properties )
217    {
218    Properties results = new Properties();
219
220    for( int i = properties.length - 1; i >= 0; i-- )
221      {
222      Properties current = properties[ i ];
223
224      if( current == null )
225        continue;
226
227      Set<String> currentNames = current.stringPropertyNames();
228
229      for( String currentName : currentNames )
230        results.setProperty( currentName, current.getProperty( currentName ) );
231      }
232
233    return results;
234    }
235
236  public static Properties remove( Properties properties, Properties... remove )
237    {
238    Objects.requireNonNull( properties, "properties may not be null" );
239
240    Set<String> keys = new HashSet<>();
241
242    for( Properties current : remove )
243      keys.addAll( current.stringPropertyNames() );
244
245    return remove( properties, keys );
246    }
247
248  public static Properties remove( Properties properties, Set<String> keys )
249    {
250    Objects.requireNonNull( properties, "properties may not be null" );
251
252    Properties results = new Properties();
253
254    for( String name : properties.stringPropertyNames() )
255      {
256      if( keys.contains( name ) )
257        continue;
258
259      results.setProperty( name, properties.getProperty( name ) );
260      }
261
262    return results;
263    }
264
265  public static Properties retain( Properties properties, Properties... retain )
266    {
267    Objects.requireNonNull( properties, "properties may not be null" );
268
269    Set<String> keys = new HashSet<>();
270
271    for( Properties current : retain )
272      {
273      if( current != null )
274        keys.addAll( current.stringPropertyNames() );
275      }
276
277    return remove( properties, keys );
278    }
279
280  public static Properties retain( Properties properties, Set<String> keys )
281    {
282    Objects.requireNonNull( properties, "properties may not be null" );
283
284    Properties results = new Properties();
285
286    for( String name : properties.stringPropertyNames() )
287      {
288      if( !keys.contains( name ) )
289        continue;
290
291      results.setProperty( name, properties.getProperty( name ) );
292      }
293
294    return results;
295    }
296  }