package com.cloudhopper.sxmp; /* * #%L * ch-sxmp * %% * Copyright (C) 2012 - 2013 Cloudhopper by Twitter * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import com.cloudhopper.commons.charset.CharsetUtil; import com.cloudhopper.commons.charset.ModifiedUTF8Charset; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; /** * Map<String, Object> for StratusRequest optional params. * Keys are limited to 255 UTF8 bytes max. * Objects can be: short, int, long, double, string (32767 UTF8 bytes max) * * @author dhoffman */ public class OptionalParamMap implements Map<String, Object> { static public final int HASH_MAP = 1; static public final int TREE_MAP = 2; static private final ModifiedUTF8Charset MODIFIED_UTF8_CHARSET = (ModifiedUTF8Charset)CharsetUtil.CHARSET_MODIFIED_UTF8; private final Map<String, Object> map; /** * Create an optional param map based on the specified map type * @param type * @throws IllegalArgumentException */ public OptionalParamMap(int type) throws IllegalArgumentException { switch (type) { case HASH_MAP: map = new HashMap<String, Object>(); break; case TREE_MAP: map = new TreeMap<String, Object>(); break; default: throw new IllegalArgumentException("Invalid map type"); } } @Override public Object put(String key, Object value) { if (map.size() + 1 > 255) throw new IllegalArgumentException("more than 255 entries"); validateEntry(key, value); return map.put(key, value); } @Override public void putAll(Map map) { if (this.map.size() + map.size() > 255) throw new IllegalArgumentException("more than 255 entries"); for (Object s : map.keySet()) { if (!(s instanceof String)) { throw new IllegalArgumentException("keys must be strings"); } put((String) s, map.get(s)); } } @Override public Object get(Object key) { return map.get(key); } public String getString(String key, String defaultValue) { Object o = get(key); if (o != null && o instanceof String) return (String) o; return defaultValue; } public Integer getInteger(String key, Integer defaultValue) { Object o = get(key); if (o != null && o instanceof Integer) return (Integer) o; return defaultValue; } /* * getLong will successfully return for both Long values and Integer values * it will convert the Integer to a Long */ public Long getLong(String key, Long defaultValue) { Object o = get(key); if (o != null) { if (o instanceof Long) { return (Long) o; } else if (o instanceof Integer) { return new Long((Integer)o); } } return defaultValue; } public Double getDouble(String key, Double defaultValue) { Object o = get(key); if (o != null && o instanceof Double) return (Double) o; return defaultValue; } @Override public Set<String> keySet() { return map.keySet(); } @Override public Collection<Object> values() { return map.values(); } @Override public int size() { return map.size(); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public boolean containsKey(Object o) { return map.containsKey(o); } @Override public boolean containsValue(Object o) { return map.containsValue(o); } @Override public Object remove(Object o) { return map.remove(o); } @Override public void clear() { map.clear(); } @Override public Set<Entry<String, Object>> entrySet() { return map.entrySet(); } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("{"); for (String key : keySet()) { sb.append(key).append("="); Object o = get(key); if (o instanceof Integer) sb.append("i"); else if (o instanceof Long) sb.append("l"); else if (o instanceof Double) sb.append("d"); sb.append(o).append(", "); } sb.append("}"); return sb.toString(); } @Override public boolean equals(Object o) { if (o instanceof OptionalParamMap) return map.equals(((OptionalParamMap) o).map); return false; } /** * validation: * enforces key length less than 256 UTF8 bytes * enforces valid value type (cannot be null) * enforces valid String value length less than 32767 UTF8 bytes */ private void validateEntry(String key, Object value) { if (isBlank(key)) { throw new IllegalArgumentException("key cannot be null/blank"); } if (ModifiedUTF8Charset.calculateByteLength(key) > 255) { throw new IllegalArgumentException("key length > 255 bytes"); } else if (value == null) { throw new IllegalArgumentException("value cannot be null"); } else if (value instanceof String) { if (ModifiedUTF8Charset.calculateByteLength((String) value) > 32767) { throw new IllegalArgumentException("string value length > 34767 bytes"); } } else if (value instanceof Integer || value instanceof Long || value instanceof Double) { // valid } else { throw new IllegalArgumentException("Illegal value type: "+value.getClass().toString()); } } // Replaces org.apache.commons.lang.StringUtils.isBlank private static boolean isBlank(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if ((Character.isWhitespace(str.charAt(i)) == false)) { return false; } } return true; } }