package com.limegroup.gnutella;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import com.limegroup.gnutella.messages.QueryRequest;
import com.limegroup.gnutella.util.Comparators;
/**
* A generic type of media, i.e., "video" or "audio".
* Many different file formats can be of the same media type.
* MediaType's are immutable.
*
* MediaType is Serializable so that older downloads.dat files
* with serialized wishlist downloaders can be deserialized.
* Note that we no longer serialize MediaType though.
*
* // See http://www.mrc-cbu.cam.ac.uk/Help/mimedefault.html
*/
public class MediaType implements Serializable {
private static final long serialVersionUID = 3999062781289258389L;
// These values should match standard MIME content-type
// categories and/or XSD schema names.
public static final String SCHEMA_ANY_TYPE = "*";
public static final String SCHEMA_DOCUMENTS = "document";
public static final String SCHEMA_PROGRAMS = "application";
public static final String SCHEMA_AUDIO = "audio";
public static final String SCHEMA_VIDEO = "video";
public static final String SCHEMA_IMAGES = "image";
// These are used as resource keys to retreive descriptions in the GUI
public static final String ANY_TYPE = "MEDIA_ANY_TYPE";
public static final String DOCUMENTS = "MEDIA_DOCUMENTS";
public static final String PROGRAMS = "MEDIA_PROGRAMS";
public static final String AUDIO = "MEDIA_AUDIO";
public static final String VIDEO = "MEDIA_VIDEO";
public static final String IMAGES = "MEDIA_IMAGES";
/**
* Type for 'any file'
*/
private static final MediaType TYPE_ANY =
new MediaType(SCHEMA_ANY_TYPE, ANY_TYPE, null) {
public boolean matches(String ext) {
return true;
}
};
/**
* Type for 'documents'
*/
private static final MediaType TYPE_DOCUMENTS =
new MediaType(SCHEMA_DOCUMENTS, DOCUMENTS,
new String[] {
"html", "htm", "xhtml", "mht", "mhtml", "xml",
"txt", "ans", "asc", "diz", "eml",
"pdf", "ps", "eps", "epsf", "dvi",
"rtf", "wri", "doc", "mcw", "wps",
"xls", "wk1", "dif", "csv", "ppt", "tsv",
"hlp", "chm", "lit",
"tex", "texi", "latex", "info", "man",
"wp", "wpd", "wp5", "wk3", "wk4", "shw",
"sdd", "sdw", "sdp", "sdc",
"sxd", "sxw", "sxp", "sxc",
"abw", "kwd"
});
/**
* Type for linux/osx programs, used for Aggregator.
*/
private static final MediaType TYPE_LINUX_OSX_PROGRAMS =
new MediaType(SCHEMA_PROGRAMS, PROGRAMS,
new String[] {
"bin", "mdb", "sh", "csh", "awk", "pl",
"rpm", "deb", "gz", "gzip", "z", "bz2", "zoo", "tar", "tgz",
"taz", "shar", "hqx", "sit", "dmg", "7z", "jar", "zip", "nrg",
"cue", "iso", "jnlp", "rar", "sh"
});
/**
* Type for windows programs, used for Aggregator.
*/
private static final MediaType TYPE_WINDOWS_PROGRAMS =
new MediaType(SCHEMA_PROGRAMS, PROGRAMS,
new String[] {
"exe", "zip", "jar", "cab", "msi", "msp",
"arj", "rar", "ace", "lzh", "lha", "bin", "nrg", "cue",
"iso", "jnlp"
});
/**
* Type for 'programs'
*/
private static final MediaType TYPE_PROGRAMS =
new MediaType(SCHEMA_PROGRAMS, PROGRAMS,
makeArray(TYPE_LINUX_OSX_PROGRAMS.exts,
TYPE_WINDOWS_PROGRAMS.exts)
);
/**
* Type for 'audio'
*/
private static final MediaType TYPE_AUDIO =
new MediaType(SCHEMA_AUDIO, AUDIO,
new String[] {
"mp3", "mpa", "mp1", "mpga", "mp2",
"ra", "rm", "ram", "rmj",
"wma", "wav", "m4a", "m4p","mp4",
"lqt", "ogg", "med",
"aif", "aiff", "aifc",
"au", "snd", "s3m", "aud",
"mid", "midi", "rmi", "mod", "kar",
"ac3", "shn", "fla", "flac", "cda",
"mka"
});
/**
* Type for 'video'
*/
private static final MediaType TYPE_VIDEO =
new MediaType(SCHEMA_VIDEO, VIDEO,
new String[] {
"mpg", "mpeg", "mpe", "mng", "mpv", "m1v",
"vob", "mp2", "mpv2", "mp2v", "m2p", "m2v", "mpgv",
"vcd", "mp4", "dv", "dvd", "div", "divx", "dvx",
"smi", "smil", "rm", "ram", "rv", "rmm", "rmvb",
"avi", "asf", "asx", "wmv", "qt", "mov",
"fli", "flc", "flx", "flv",
"wml", "vrml", "swf", "dcr", "jve", "nsv",
"mkv", "ogm",
"cdg", "srt", "sub", "idx"
});
/**
* Type for 'images'
*/
private static final MediaType TYPE_IMAGES =
new MediaType(SCHEMA_IMAGES, IMAGES,
new String[] {
"gif", "png",
"jpg", "jpeg", "jpe", "jif", "jiff", "jfif",
"tif", "tiff", "iff", "lbm", "ilbm", "eps",
"mac", "drw", "pct", "img",
"bmp", "dib", "rle", "ico", "ani", "icl", "cur",
"emf", "wmf", "pcx",
"pcd", "tga", "pic", "fig",
"psd", "wpg", "dcx", "cpt", "mic",
"pbm", "pnm", "ppm", "xbm", "xpm", "xwd",
"sgi", "fax", "rgb", "ras"
});
/**
* All media types.
*/
private static final MediaType[] ALL_MEDIA_TYPES =
new MediaType[] { TYPE_ANY, TYPE_DOCUMENTS, TYPE_PROGRAMS,
TYPE_AUDIO, TYPE_VIDEO, TYPE_IMAGES };
/**
* The description of this MediaType.
*/
private final String schema;
/**
* The key to look up this MediaType.
*/
private final String descriptionKey;
/**
* The list of extensions within this MediaType.
*/
private final Set exts;
/**
* Whether or not this is one of the default media types.
*/
private final boolean isDefault;
/**
* Constructs a MediaType with only a MIME-Type.
*/
public MediaType(String schema) {
if (schema == null) {
throw new NullPointerException("schema must not be null");
}
this.schema = schema;
this.descriptionKey = null;
this.exts = Collections.EMPTY_SET;
this.isDefault = false;
}
/**
* @param schema a MIME compliant non-localizable identifier,
* that matches file categories (and XSD schema names).
* @param descriptionKey a media identifier that can be used
* to retreive a localizable descriptive text.
* @param extensions a list of all file extensions of this
* type. Must be all lowercase. If null, this matches
* any file.
*/
public MediaType(String schema, String descriptionKey,
String[] extensions) {
if (schema == null) {
throw new NullPointerException("schema must not be null");
}
this.schema = schema;
this.descriptionKey = descriptionKey;
this.isDefault = true;
if(extensions == null) {
this.exts = Collections.EMPTY_SET;
} else {
Set set =
new TreeSet(Comparators.caseInsensitiveStringComparator());
set.addAll(Arrays.asList(extensions));
this.exts = set;
}
}
/**
* Returns true if a file with the given name is of this
* media type, i.e., the suffix of the filename matches
* one of this' extensions.
*/
public boolean matches(String filename) {
if (exts == null)
return true;
//Get suffix of filename.
int j = filename.lastIndexOf(".");
if (j == -1 || j == filename.length())
return false;
String suffix = filename.substring(j+1);
// Match with extensions.
return exts.contains(suffix);
}
/**
* Returns this' media-type (a MIME content-type category)
* (previously returned a description key)
*/
public String toString() {
return schema;
}
/**
* Returns this' description key in localizable resources
* (now distinct from the result of the toString method)
*/
public String getDescriptionKey() {
return descriptionKey;
}
/**
* Returns the MIME-Type of this.
*/
public String getMimeType() {
return schema;
}
/**
* Determines whether or not this is a default media type.
*/
public boolean isDefault() {
return isDefault;
}
/**
* Returns the extensions for this media type.
*/
public Set getExtensions() {
return exts;
}
/**
* Returns all default media types.
*/
public static final MediaType[] getDefaultMediaTypes() {
return ALL_MEDIA_TYPES;
}
/**
* Retrieves the media type for the specified schema's description.
*/
public static MediaType getMediaTypeForSchema(String schema) {
for (int i = ALL_MEDIA_TYPES.length; --i >= 0;)
if (schema.equals(ALL_MEDIA_TYPES[i].schema))
return ALL_MEDIA_TYPES[i];
return null;
}
/**
* Retrieves the media type for the specified extension.
*/
public static MediaType getMediaTypeForExtension(String ext) {
for(int i = ALL_MEDIA_TYPES.length; --i >= 0;)
if(ALL_MEDIA_TYPES[i].exts.contains(ext))
return ALL_MEDIA_TYPES[i];
return null;
}
/**
* Determines whether or not the specified schema is a default.
*/
public static boolean isDefaultType(String schema) {
for (int i = ALL_MEDIA_TYPES.length; --i >= 0;)
if (schema.equals(ALL_MEDIA_TYPES[i].schema))
return true;
return false;
}
/**
* Retrieves the any media type.
*/
public static MediaType getAnyTypeMediaType() {
return TYPE_ANY;
}
/**
* Retrieves the audio media type.
*/
public static MediaType getAudioMediaType() {
return TYPE_AUDIO;
}
/**
* Retrieves the video media type.
*/
public static MediaType getVideoMediaType() {
return TYPE_VIDEO;
}
/**
* Retrieves the image media type.
*/
public static MediaType getImageMediaType() {
return TYPE_IMAGES;
}
/**
* Retrieves the document media type.
*/
public static MediaType getDocumentMediaType() {
return TYPE_DOCUMENTS;
}
/**
* Retrieves the programs media type.
*/
public static MediaType getProgramMediaType() {
return TYPE_PROGRAMS;
}
/** @return a MediaType.Aggregator to use for your query. Null is a
* possible return value.
*/
public static Aggregator getAggregator(QueryRequest query) {
if (query.desiresAll())
return null;
Aggregator retAggr = new Aggregator();
if (query.desiresLinuxOSXPrograms())
retAggr.addFilter(TYPE_LINUX_OSX_PROGRAMS);
if (query.desiresWindowsPrograms())
retAggr.addFilter(TYPE_WINDOWS_PROGRAMS);
if (query.desiresDocuments())
retAggr.addFilter(TYPE_DOCUMENTS);
if (query.desiresAudio())
retAggr.addFilter(TYPE_AUDIO);
if (query.desiresVideo())
retAggr.addFilter(TYPE_VIDEO);
if (query.desiresImages())
retAggr.addFilter(TYPE_IMAGES);
return retAggr;
}
/** Utility class for aggregating MediaTypes.
* This class is not synchronized - it should never be used in a fashion
* where synchronization is necessary. If that changes, add synch.
*/
public static class Aggregator {
/** A list of MediaType objects.
*/
private List _filters = new LinkedList();
private Aggregator() {}
/** I don't check for duplicates. */
private void addFilter(MediaType filter) {
_filters.add(filter);
}
/** @return true if the Response falls within one of the MediaTypes
* this aggregates.
*/
public boolean allow(final String fName) {
Iterator iter = _filters.iterator();
while (iter.hasNext()) {
MediaType currType = (MediaType)iter.next();
if (currType.matches(fName))
return true;
}
return false;
}
}
/**
* Utility that makes an array out of two sets.
*/
private static String[] makeArray(Set one, Set two) {
Set all = new HashSet(one);
all.addAll(two);
return (String[])all.toArray(new String[all.size()]);
}
}