Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Licensed to the Apache Software Foundation (ASF) under one or more
0003  * contributor license agreements.  See the NOTICE file distributed with
0004  * this work for additional information regarding copyright ownership.
0005  * The ASF licenses this file to You under the Apache License, Version 2.0
0006  * (the "License"); you may not use this file except in compliance with
0007  * the License.  You may obtain a copy of the License at
0008  *
0009  *    http://www.apache.org/licenses/LICENSE-2.0
0010  *
0011  * Unless required by applicable law or agreed to in writing, software
0012  * distributed under the License is distributed on an "AS IS" BASIS,
0013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014  * See the License for the specific language governing permissions and
0015  * limitations under the License.
0016  */
0017 
0018 package org.apache.spark.sql.util;
0019 
0020 import org.apache.spark.annotation.Experimental;
0021 import org.slf4j.Logger;
0022 import org.slf4j.LoggerFactory;
0023 
0024 import java.util.Collection;
0025 import java.util.Collections;
0026 import java.util.HashMap;
0027 import java.util.Locale;
0028 import java.util.Map;
0029 import java.util.Objects;
0030 import java.util.Set;
0031 
0032 /**
0033  * Case-insensitive map of string keys to string values.
0034  * <p>
0035  * This is used to pass options to v2 implementations to ensure consistent case insensitivity.
0036  * <p>
0037  * Methods that return keys in this map, like {@link #entrySet()} and {@link #keySet()}, return
0038  * keys converted to lower case. This map doesn't allow null key.
0039  *
0040  * @since 3.0.0
0041  */
0042 @Experimental
0043 public class CaseInsensitiveStringMap implements Map<String, String> {
0044   private final Logger logger = LoggerFactory.getLogger(CaseInsensitiveStringMap.class);
0045 
0046   private String unsupportedOperationMsg = "CaseInsensitiveStringMap is read-only.";
0047 
0048   public static CaseInsensitiveStringMap empty() {
0049     return new CaseInsensitiveStringMap(new HashMap<>(0));
0050   }
0051 
0052   private final Map<String, String> original;
0053 
0054   private final Map<String, String> delegate;
0055 
0056   public CaseInsensitiveStringMap(Map<String, String> originalMap) {
0057     original = new HashMap<>(originalMap);
0058     delegate = new HashMap<>(originalMap.size());
0059     for (Map.Entry<String, String> entry : originalMap.entrySet()) {
0060       String key = toLowerCase(entry.getKey());
0061       if (delegate.containsKey(key)) {
0062         logger.warn("Converting duplicated key " + entry.getKey() +
0063                 " into CaseInsensitiveStringMap.");
0064       }
0065       delegate.put(key, entry.getValue());
0066     }
0067   }
0068 
0069   @Override
0070   public int size() {
0071     return delegate.size();
0072   }
0073 
0074   @Override
0075   public boolean isEmpty() {
0076     return delegate.isEmpty();
0077   }
0078 
0079   private String toLowerCase(Object key) {
0080     return key.toString().toLowerCase(Locale.ROOT);
0081   }
0082 
0083   @Override
0084   public boolean containsKey(Object key) {
0085     return delegate.containsKey(toLowerCase(key));
0086   }
0087 
0088   @Override
0089   public boolean containsValue(Object value) {
0090     return delegate.containsValue(value);
0091   }
0092 
0093   @Override
0094   public String get(Object key) {
0095     return delegate.get(toLowerCase(key));
0096   }
0097 
0098   @Override
0099   public String put(String key, String value) {
0100     throw new UnsupportedOperationException(unsupportedOperationMsg);
0101   }
0102 
0103   @Override
0104   public String remove(Object key) {
0105     throw new UnsupportedOperationException(unsupportedOperationMsg);
0106   }
0107 
0108   @Override
0109   public void putAll(Map<? extends String, ? extends String> m) {
0110     throw new UnsupportedOperationException(unsupportedOperationMsg);
0111   }
0112 
0113   @Override
0114   public void clear() {
0115     throw new UnsupportedOperationException(unsupportedOperationMsg);
0116   }
0117 
0118   @Override
0119   public Set<String> keySet() {
0120     return delegate.keySet();
0121   }
0122 
0123   @Override
0124   public Collection<String> values() {
0125     return delegate.values();
0126   }
0127 
0128   @Override
0129   public Set<Map.Entry<String, String>> entrySet() {
0130     return delegate.entrySet();
0131   }
0132 
0133   /**
0134    * Returns the boolean value to which the specified key is mapped,
0135    * or defaultValue if there is no mapping for the key. The key match is case-insensitive.
0136    */
0137   public boolean getBoolean(String key, boolean defaultValue) {
0138     String value = get(key);
0139     // We can't use `Boolean.parseBoolean` here, as it returns false for invalid strings.
0140     if (value == null) {
0141       return defaultValue;
0142     } else if (value.equalsIgnoreCase("true")) {
0143       return true;
0144     } else if (value.equalsIgnoreCase("false")) {
0145       return false;
0146     } else {
0147       throw new IllegalArgumentException(value + " is not a boolean string.");
0148     }
0149   }
0150 
0151   /**
0152    * Returns the integer value to which the specified key is mapped,
0153    * or defaultValue if there is no mapping for the key. The key match is case-insensitive.
0154    */
0155   public int getInt(String key, int defaultValue) {
0156     String value = get(key);
0157     return value == null ? defaultValue : Integer.parseInt(value);
0158   }
0159 
0160   /**
0161    * Returns the long value to which the specified key is mapped,
0162    * or defaultValue if there is no mapping for the key. The key match is case-insensitive.
0163    */
0164   public long getLong(String key, long defaultValue) {
0165     String value = get(key);
0166     return value == null ? defaultValue : Long.parseLong(value);
0167   }
0168 
0169   /**
0170    * Returns the double value to which the specified key is mapped,
0171    * or defaultValue if there is no mapping for the key. The key match is case-insensitive.
0172    */
0173   public double getDouble(String key, double defaultValue) {
0174     String value = get(key);
0175     return value == null ? defaultValue : Double.parseDouble(value);
0176   }
0177 
0178   /**
0179    * Returns the original case-sensitive map.
0180    */
0181   public Map<String, String> asCaseSensitiveMap() {
0182     return Collections.unmodifiableMap(original);
0183   }
0184 
0185   @Override
0186   public boolean equals(Object o) {
0187     if (this == o) {
0188       return true;
0189     }
0190     if (o == null || getClass() != o.getClass()) {
0191       return false;
0192     }
0193     CaseInsensitiveStringMap that = (CaseInsensitiveStringMap) o;
0194     return delegate.equals(that.delegate);
0195   }
0196 
0197   @Override
0198   public int hashCode() {
0199     return Objects.hash(delegate);
0200   }
0201 }