1 /*
2 * Copyright 2007-2009 Medsea Business Solutions S.L.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package eu.medsea.mimeutil;
17
18 import java.io.Serializable;
19 import java.util.regex.Pattern;
20
21 import eu.medsea.mimeutil.MimeException;
22
23 /**
24 * This class represents a simple MimeType object. A mime type is made up of
25 * two parts <code><media type>/<sub type></code>.
26 * The media type can be something like <code>application</code> or <code>text</code> and
27 * the the sub type can be something like <code>xml</code> or <code>plain</code>.
28 *
29 * Both the media type and sub type can also be the wild card <code>*</code> such as
30 * <code>*/*</code> and <code>text/*</code>. Note, if the media type is the wild card
31 * then the sub type must also be a wild card.
32 *
33 * @author Steven McArdle
34 *
35 */
36 public class MimeType implements Comparable, Serializable {
37
38 private static final long serialVersionUID = -1324243127744494894L;
39
40 private static final Pattern mimeSplitter = Pattern.compile ("[/;]++" );
41
42 protected String mediaType = "*";
43 protected String subType = "*";
44
45 //This is a estimate of how specific this mime type is
46 private int specificity = 1;
47
48 /**
49 * Construct a MimeType from another MimeType instance
50 * @param mimeType
51 */
52 public MimeType(final MimeType mimeType) {
53 this.mediaType = mimeType.mediaType;
54 this.subType = mimeType.subType;
55 this.specificity = mimeType.specificity;
56 }
57
58 /**
59 * Construct a mime type from a String such as <code>text/plain</code>.
60 * It tries to ensure that the mime type pattern passed in is correctly
61 * formatted.
62 *
63 * @param mimeType
64 * @throws MimeException
65 */
66 public MimeType(final String mimeType) throws MimeException {
67 if(mimeType == null || mimeType.trim().length() == 0){
68 throw new MimeException("Invalid MimeType [" + mimeType + "]");
69 }
70 String [] parts = mimeSplitter.split(mimeType.trim());
71
72 if(parts.length > 0) {
73 // Treat as the mediaType
74 mediaType = getValidMediaType(parts[0]);
75 } if(parts.length > 1) {
76 subType = getValidSubType(parts[1]);
77 }
78 }
79
80 /**
81 * Get the media type part of the mime type.
82 * @return media type
83 */
84 public String getMediaType() {
85 return mediaType;
86 }
87
88 /**
89 * Get the sub type of the mime type
90 * @return sub type
91 */
92 public String getSubType() {
93 return subType;
94 }
95
96
97 /**
98 * See if this MimeType is the same as the passed in mime type string
99 * @param mimeType as a String
100 * @return true if the MimeType passed in has the same media and sub types, else returns false.
101 */
102 private boolean match(final String mimeType) {
103 return toString().equals(mimeType);
104 }
105
106 /**
107 * Get the hashCode of this MimeType.
108 * The hashCode is calculate as (31 * mediaType.hashCode()) + subType.hashCode()
109 * @return calculated hashCode
110 * @see Object#hashCode()
111 */
112 public int hashCode() {
113 return (31 * mediaType.hashCode()) + subType.hashCode();
114 }
115
116 /**
117 * Overrides the equals method of <code>java.lang.Object</code>. This is able to compare
118 * against another MimeType instance or a string representation of a mime type.
119 * @return true if the types match else false.
120 * @see Object#equals(Object o)
121 */
122 public boolean equals(Object o) {
123 if(o instanceof MimeType) {
124 if(this.mediaType.equals(((MimeType)o).mediaType) && this.subType.equals(((MimeType)o).subType)) {
125 return true;
126 }
127 } else if(o instanceof String) {
128 return match((String)o);
129 }
130 return false;
131 }
132
133 /**
134 * Overrides the toString method of <code>java.lang.Object</code>.
135 * @return String representation i.e. <code><media type>/<sub type>.
136 * @see Object#toString()
137 */
138 public String toString() {
139 return mediaType + "/" + subType;
140 }
141
142 /**
143 * This indicates how specific the mime types is i.e. how good a match
144 * the mime type is when returned from the getMimeTypes(...) calls.
145 * <p>
146 * This is calculated by the number of times this MimeType would be returned
147 * if the Collection was not normalised. The higher the count the more MimeDetectors
148 * have matched this type. As this can be a false positive for types such as application/octect-stream
149 * and text/plain where they would be returned by multiple MimeDetector(s). These types are referred to as root
150 * mime types where ALL mime types derive from application/octet-stream and all text/* types derive from text/plan
151 * so in these cases we set the specificity to 0 no matter how many times they match. This ensures they are regarded
152 * as the least specific in the returned Collection.
153 * </p>
154 * @return how specific this MimeType is according to the rest of the MimeTypes in a Collection.
155 */
156 public int getSpecificity() {
157 return specificity;
158 }
159
160 /*
161 * Set the value of the specificity. The higher the value the more specific a MimeType is.
162 */
163 void setSpecificity(final int specificity) {
164 this.specificity = specificity;
165 }
166
167 /*
168 * Check the media type at least looks valid.
169 * TODO: Enforce more rigorous checking of valid media types.
170 */
171 private String getValidMediaType(final String mediaType) {
172 if(mediaType == null || mediaType.trim().length() == 0) {
173 return "*";
174 }
175 return mediaType;
176 }
177
178 /*
179 * Check the sub type at least looks valid.
180 * TODO: Enforce more rigorous checking of valid sub types.
181 */
182 private String getValidSubType(final String subType) {
183 if(subType == null || subType.trim().length() == 0 || "*".equals(mediaType)) {
184 // If the mediaType is a wild card then the sub type must also be a wild card
185 return "*";
186 }
187 return subType;
188 }
189
190 /**
191 * Allows us to use MimeType(s) in Sortable Set's such as the TreeSet.
192 */
193 public int compareTo(Object arg0) {
194 if(arg0 instanceof MimeType) {
195 return toString().compareTo(((MimeType)arg0).toString());
196 }
197 return 0;
198 }
199 }