/*
* Copyright 2007-2009 Medsea Business Solutions S.L.
*
* 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.
*/
package eu.medsea.mimeutil;
import java.io.Serializable;
import java.util.regex.Pattern;
import eu.medsea.mimeutil.MimeException;
/**
* This class represents a simple MimeType object. A mime type is made up of
* two parts <code><media type>/<sub type></code>.
* The media type can be something like <code>application</code> or <code>text</code> and
* the the sub type can be something like <code>xml</code> or <code>plain</code>.
*
* Both the media type and sub type can also be the wild card <code>*</code> such as
* <code>*/*</code> and <code>text/*</code>. Note, if the media type is the wild card
* then the sub type must also be a wild card.
*
* @author Steven McArdle
*
*/
public class MimeType implements Comparable, Serializable {
private static final long serialVersionUID = -1324243127744494894L;
private static final Pattern mimeSplitter = Pattern.compile ("[/;]++" );
protected String mediaType = "*";
protected String subType = "*";
//This is a estimate of how specific this mime type is
private int specificity = 1;
/**
* Construct a MimeType from another MimeType instance
* @param mimeType
*/
public MimeType(final MimeType mimeType) {
this.mediaType = mimeType.mediaType;
this.subType = mimeType.subType;
this.specificity = mimeType.specificity;
}
/**
* Construct a mime type from a String such as <code>text/plain</code>.
* It tries to ensure that the mime type pattern passed in is correctly
* formatted.
*
* @param mimeType
* @throws MimeException
*/
public MimeType(final String mimeType) throws MimeException {
if(mimeType == null || mimeType.trim().length() == 0){
throw new MimeException("Invalid MimeType [" + mimeType + "]");
}
String [] parts = mimeSplitter.split(mimeType.trim());
if(parts.length > 0) {
// Treat as the mediaType
mediaType = getValidMediaType(parts[0]);
} if(parts.length > 1) {
subType = getValidSubType(parts[1]);
}
}
/**
* Get the media type part of the mime type.
* @return media type
*/
public String getMediaType() {
return mediaType;
}
/**
* Get the sub type of the mime type
* @return sub type
*/
public String getSubType() {
return subType;
}
/**
* See if this MimeType is the same as the passed in mime type string
* @param mimeType as a String
* @return true if the MimeType passed in has the same media and sub types, else returns false.
*/
private boolean match(final String mimeType) {
return toString().equals(mimeType);
}
/**
* Get the hashCode of this MimeType.
* The hashCode is calculate as (31 * mediaType.hashCode()) + subType.hashCode()
* @return calculated hashCode
* @see Object#hashCode()
*/
public int hashCode() {
return (31 * mediaType.hashCode()) + subType.hashCode();
}
/**
* Overrides the equals method of <code>java.lang.Object</code>. This is able to compare
* against another MimeType instance or a string representation of a mime type.
* @return true if the types match else false.
* @see Object#equals(Object o)
*/
public boolean equals(Object o) {
if(o instanceof MimeType) {
if(this.mediaType.equals(((MimeType)o).mediaType) && this.subType.equals(((MimeType)o).subType)) {
return true;
}
} else if(o instanceof String) {
return match((String)o);
}
return false;
}
/**
* Overrides the toString method of <code>java.lang.Object</code>.
* @return String representation i.e. <code><media type>/<sub type>.
* @see Object#toString()
*/
public String toString() {
return mediaType + "/" + subType;
}
/**
* This indicates how specific the mime types is i.e. how good a match
* the mime type is when returned from the getMimeTypes(...) calls.
* <p>
* This is calculated by the number of times this MimeType would be returned
* if the Collection was not normalised. The higher the count the more MimeDetectors
* have matched this type. As this can be a false positive for types such as application/octect-stream
* and text/plain where they would be returned by multiple MimeDetector(s). These types are referred to as root
* mime types where ALL mime types derive from application/octet-stream and all text/* types derive from text/plan
* so in these cases we set the specificity to 0 no matter how many times they match. This ensures they are regarded
* as the least specific in the returned Collection.
* </p>
* @return how specific this MimeType is according to the rest of the MimeTypes in a Collection.
*/
public int getSpecificity() {
return specificity;
}
/*
* Set the value of the specificity. The higher the value the more specific a MimeType is.
*/
void setSpecificity(final int specificity) {
this.specificity = specificity;
}
/*
* Check the media type at least looks valid.
* TODO: Enforce more rigorous checking of valid media types.
*/
private String getValidMediaType(final String mediaType) {
if(mediaType == null || mediaType.trim().length() == 0) {
return "*";
}
return mediaType;
}
/*
* Check the sub type at least looks valid.
* TODO: Enforce more rigorous checking of valid sub types.
*/
private String getValidSubType(final String subType) {
if(subType == null || subType.trim().length() == 0 || "*".equals(mediaType)) {
// If the mediaType is a wild card then the sub type must also be a wild card
return "*";
}
return subType;
}
/**
* Allows us to use MimeType(s) in Sortable Set's such as the TreeSet.
*/
public int compareTo(Object arg0) {
if(arg0 instanceof MimeType) {
return toString().compareTo(((MimeType)arg0).toString());
}
return 0;
}
}