001 /**************************************************************** 002 * Licensed to the Apache Software Foundation (ASF) under one * 003 * or more contributor license agreements. See the NOTICE file * 004 * distributed with this work for additional information * 005 * regarding copyright ownership. The ASF licenses this file * 006 * to you under the Apache License, Version 2.0 (the * 007 * "License"); you may not use this file except in compliance * 008 * with the License. You may obtain a copy of the License at * 009 * * 010 * http://www.apache.org/licenses/LICENSE-2.0 * 011 * * 012 * Unless required by applicable law or agreed to in writing, * 013 * software distributed under the License is distributed on an * 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 015 * KIND, either express or implied. See the License for the * 016 * specific language governing permissions and limitations * 017 * under the License. * 018 ****************************************************************/ 019 020 package org.apache.james.mime4j.util; 021 022 import java.io.Serializable; 023 import java.util.ArrayList; 024 import java.util.Collection; 025 import java.util.Collections; 026 import java.util.Enumeration; 027 import java.util.HashMap; 028 import java.util.List; 029 import java.util.Map; 030 import java.util.NoSuchElementException; 031 032 import org.apache.james.mime4j.message.Header; 033 import org.apache.james.mime4j.parser.ContentHandler; 034 import org.apache.james.mime4j.parser.Field; 035 036 /** 037 * An object, which may be used to implement header, or parameter 038 * maps. The maps keys are the header or parameter names. The 039 * maps values are strings (single value), lists, or arrays. 040 * <p> 041 * Note that this class is not directly used anywhere in Mime4j. 042 * Instead a user might choose to use it instead of {@link Header} 043 * and {@link Field} in a custom {@link ContentHandler} implementation. 044 * See also MIME4j-24. 045 */ 046 public class StringArrayMap implements Serializable { 047 private static final long serialVersionUID = -5833051164281786907L; 048 private final Map<String, Object> map = new HashMap<String, Object>(); 049 050 /** 051 * <p>Converts the given object into a string. The object may be either of: 052 * <ul> 053 * <li>a string, which is returned without conversion</li> 054 * <li>a list of strings, in which case the first element is returned</li> 055 * <li>an array of strings, in which case the first element is returned</li> 056 * </ul> 057 */ 058 public static String asString(Object pValue) { 059 if (pValue == null) { 060 return null; 061 } 062 if (pValue instanceof String) { 063 return (String) pValue; 064 } 065 if (pValue instanceof String[]) { 066 return ((String[]) pValue)[0]; 067 } 068 if (pValue instanceof List) { 069 return (String) ((List<?>) pValue).get(0); 070 } 071 throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName()); 072 } 073 074 /** 075 * <p>Converts the given object into a string array. The object may be either of: 076 * <ul> 077 * <li>a string, which is returned as an array with one element</li> 078 * <li>a list of strings, which is being converted into a string array</li> 079 * <li>an array of strings, which is returned without conversion</li> 080 * </ul> 081 */ 082 public static String[] asStringArray(Object pValue) { 083 if (pValue == null) { 084 return null; 085 } 086 if (pValue instanceof String) { 087 return new String[]{(String) pValue}; 088 } 089 if (pValue instanceof String[]) { 090 return (String[]) pValue; 091 } 092 if (pValue instanceof List) { 093 final List<?> l = (List<?>) pValue; 094 return l.toArray(new String[l.size()]); 095 } 096 throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName()); 097 } 098 099 /** 100 * <p>Converts the given object into a string enumeration. The object may be either of: 101 * <ul> 102 * <li>a string, which is returned as an enumeration with one element</li> 103 * <li>a list of strings, which is being converted into a string enumeration</li> 104 * <li>an array of strings, which is being converted into a string enumeration</li> 105 * </ul> 106 */ 107 public static Enumeration<String> asStringEnum(final Object pValue) { 108 if (pValue == null) { 109 return null; 110 } 111 if (pValue instanceof String) { 112 return new Enumeration<String>(){ 113 private Object value = pValue; 114 public boolean hasMoreElements() { 115 return value != null; 116 } 117 public String nextElement() { 118 if (value == null) { 119 throw new NoSuchElementException(); 120 } 121 final String s = (String) value; 122 value = null; 123 return s; 124 } 125 }; 126 } 127 if (pValue instanceof String[]) { 128 final String[] values = (String[]) pValue; 129 return new Enumeration<String>() { 130 private int offset; 131 public boolean hasMoreElements() { 132 return offset < values.length; 133 } 134 public String nextElement() { 135 if (offset >= values.length) { 136 throw new NoSuchElementException(); 137 } 138 return values[offset++]; 139 } 140 }; 141 } 142 if (pValue instanceof List) { 143 @SuppressWarnings("unchecked") 144 final List<String> stringList = (List<String>) pValue; 145 return Collections.enumeration(stringList); 146 } 147 throw new IllegalStateException("Invalid parameter class: " + pValue.getClass().getName()); 148 } 149 150 /** 151 * Converts the given map into a string array map: The map values 152 * are string arrays. 153 */ 154 public static Map<String, String[]> asMap(final Map<String, Object> pMap) { 155 Map<String, String[]> result = new HashMap<String, String[]>(pMap.size()); 156 for (Map.Entry<String, Object> entry : pMap.entrySet()) { 157 final String[] value = asStringArray(entry.getValue()); 158 result.put(entry.getKey(), value); 159 } 160 return Collections.unmodifiableMap(result); 161 } 162 163 /** 164 * Adds a value to the given map. 165 */ 166 protected void addMapValue(Map<String, Object> pMap, String pName, String pValue) { 167 Object o = pMap.get(pName); 168 if (o == null) { 169 o = pValue; 170 } else if (o instanceof String) { 171 final List<Object> list = new ArrayList<Object>(); 172 list.add(o); 173 list.add(pValue); 174 o = list; 175 } else if (o instanceof List) { 176 @SuppressWarnings("unchecked") 177 final List<String> stringList = (List<String>) o; 178 stringList.add(pValue); 179 } else if (o instanceof String[]) { 180 final List<String> list = new ArrayList<String>(); 181 final String[] arr = (String[]) o; 182 for (String str : arr) { 183 list.add(str); 184 } 185 list.add(pValue); 186 o = list; 187 } else { 188 throw new IllegalStateException("Invalid object type: " + o.getClass().getName()); 189 } 190 pMap.put(pName, o); 191 } 192 193 /** 194 * Lower cases the given name. 195 */ 196 protected String convertName(String pName) { 197 return pName.toLowerCase(); 198 } 199 200 /** 201 * Returns the requested value. 202 */ 203 public String getValue(String pName) { 204 return asString(map.get(convertName(pName))); 205 } 206 207 /** 208 * Returns the requested values as a string array. 209 */ 210 public String[] getValues(String pName) { 211 return asStringArray(map.get(convertName(pName))); 212 } 213 214 /** 215 * Returns the requested values as an enumeration. 216 */ 217 public Enumeration<String> getValueEnum(String pName) { 218 return asStringEnum(map.get(convertName(pName))); 219 } 220 221 /** 222 * Returns the set of registered names as an enumeration. 223 * @see #getNameArray() 224 */ 225 public Enumeration<String> getNames() { 226 return Collections.enumeration(map.keySet()); 227 } 228 229 /** 230 * Returns an unmodifiable map of name/value pairs. The map keys 231 * are the lower cased parameter/header names. The map values are 232 * string arrays. 233 */ 234 public Map<String, String[]> getMap() { 235 return asMap(map); 236 } 237 238 /** 239 * Adds a new name/value pair. 240 */ 241 public void addValue(String pName, String pValue) { 242 addMapValue(map, convertName(pName), pValue); 243 } 244 245 /** 246 * Returns the set of registered names. 247 * @see #getNames() 248 */ 249 public String[] getNameArray() { 250 final Collection<String> c = map.keySet(); 251 return c.toArray(new String[c.size()]); 252 } 253 }