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 020package org.apache.james.mime4j.message; 021 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.LinkedList; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.james.mime4j.dom.Header; 030import org.apache.james.mime4j.stream.Field; 031 032/** 033 * Abstract MIME header. 034 */ 035public abstract class AbstractHeader implements Header { 036 037 private List<Field> fields = new LinkedList<Field>(); 038 private Map<String, List<Field>> fieldMap = new HashMap<String, List<Field>>(); 039 040 /** 041 * Creates a new empty <code>Header</code>. 042 */ 043 public AbstractHeader() { 044 } 045 046 /** 047 * Creates a new <code>Header</code> from the specified 048 * <code>Header</code>. The <code>Header</code> instance is initialized 049 * with a copy of the list of {@link Field}s of the specified 050 * <code>Header</code>. The <code>Field</code> objects are not copied 051 * because they are immutable and can safely be shared between headers. 052 * 053 * @param other 054 * header to copy. 055 */ 056 public AbstractHeader(Header other) { 057 for (Field otherField : other.getFields()) { 058 addField(otherField); 059 } 060 } 061 062 /** 063 * Adds a field to the end of the list of fields. 064 * 065 * @param field the field to add. 066 */ 067 public void addField(Field field) { 068 List<Field> values = fieldMap.get(field.getName().toLowerCase()); 069 if (values == null) { 070 values = new LinkedList<Field>(); 071 fieldMap.put(field.getName().toLowerCase(), values); 072 } 073 values.add(field); 074 fields.add(field); 075 } 076 077 /** 078 * Gets the fields of this header. The returned list will not be 079 * modifiable. 080 * 081 * @return the list of <code>Field</code> objects. 082 */ 083 public List<Field> getFields() { 084 return Collections.unmodifiableList(fields); 085 } 086 087 /** 088 * Gets a <code>Field</code> given a field name. If there are multiple 089 * such fields defined in this header the first one will be returned. 090 * 091 * @param name the field name (e.g. From, Subject). 092 * @return the field or <code>null</code> if none found. 093 */ 094 public Field getField(String name) { 095 List<Field> l = fieldMap.get(name.toLowerCase()); 096 if (l != null && !l.isEmpty()) { 097 return l.get(0); 098 } 099 return null; 100 } 101 102 /** 103 * Gets all <code>Field</code>s having the specified field name. 104 * 105 * @param name the field name (e.g. From, Subject). 106 * @return the list of fields. 107 */ 108 public List<Field> getFields(final String name) { 109 final String lowerCaseName = name.toLowerCase(); 110 final List<Field> l = fieldMap.get(lowerCaseName); 111 final List<Field> results; 112 if (l == null || l.isEmpty()) { 113 results = Collections.emptyList(); 114 } else { 115 results = Collections.unmodifiableList(l); 116 } 117 return results; 118 } 119 120 /** 121 * Returns an iterator over the list of fields of this header. 122 * 123 * @return an iterator. 124 */ 125 public Iterator<Field> iterator() { 126 return Collections.unmodifiableList(fields).iterator(); 127 } 128 129 /** 130 * Removes all <code>Field</code>s having the specified field name. 131 * 132 * @param name 133 * the field name (e.g. From, Subject). 134 * @return number of fields removed. 135 */ 136 public int removeFields(String name) { 137 final String lowerCaseName = name.toLowerCase(); 138 List<Field> removed = fieldMap.remove(lowerCaseName); 139 if (removed == null || removed.isEmpty()) 140 return 0; 141 142 for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext();) { 143 Field field = iterator.next(); 144 if (field.getName().equalsIgnoreCase(name)) 145 iterator.remove(); 146 } 147 148 return removed.size(); 149 } 150 151 /** 152 * Sets or replaces a field. This method is useful for header fields such as 153 * Subject or Message-ID that should not occur more than once in a message. 154 * 155 * If this <code>Header</code> does not already contain a header field of 156 * the same name as the given field then it is added to the end of the list 157 * of fields (same behavior as {@link #addField(Field)}). Otherwise the 158 * first occurrence of a field with the same name is replaced by the given 159 * field and all further occurrences are removed. 160 * 161 * @param field the field to set. 162 */ 163 public void setField(Field field) { 164 final String lowerCaseName = field.getName().toLowerCase(); 165 List<Field> l = fieldMap.get(lowerCaseName); 166 if (l == null || l.isEmpty()) { 167 addField(field); 168 return; 169 } 170 171 l.clear(); 172 l.add(field); 173 174 int firstOccurrence = -1; 175 int index = 0; 176 for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext(); index++) { 177 Field f = iterator.next(); 178 if (f.getName().equalsIgnoreCase(field.getName())) { 179 iterator.remove(); 180 181 if (firstOccurrence == -1) 182 firstOccurrence = index; 183 } 184 } 185 186 fields.add(firstOccurrence, field); 187 } 188 189 /** 190 * Return Header Object as String representation. Each headerline is 191 * seperated by "\r\n" 192 * 193 * @return headers 194 */ 195 @Override 196 public String toString() { 197 StringBuilder str = new StringBuilder(128); 198 for (Field field : fields) { 199 str.append(field.toString()); 200 str.append("\r\n"); 201 } 202 return str.toString(); 203 } 204 205}