blob: 085224550d799bce92b8c30cbca18aaa280f52a8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.tika.mime;
import java.io.Serializable;
import java.util.ArrayList;
/**
* Internet media type.
*/
public final class MimeType implements Comparable<MimeType>, Serializable {
/**
* Serial version UID.
*/
private static final long serialVersionUID = 4357830439860729201L;
/**
* Checks that the given string is a valid Internet media type name
* based on rules from RFC 2054 section 5.3. For validation purposes the
* rules can be simplified to the following:
* <pre>
* name := token "/" token
* token := 1*&lt;any (US-ASCII) CHAR except SPACE, CTLs, or tspecials&gt;
* tspecials := "(" / ")" / "&lt;" / "&gt;" / "@" / "," / ";" / ":" /
* "\" / <"> / "/" / "[" / "]" / "?" / "="
* </pre>
*
* @param name name string
* @return <code>true</code> if the string is a valid media type name,
* <code>false</code> otherwise
*/
public static boolean isValid(String name) {
if (name == null) {
throw new IllegalArgumentException("Name is missing");
}
boolean slash = false;
for (int i = 0; i < name.length(); i++) {
char ch = name.charAt(i);
if (ch <= ' ' || ch >= 127 || ch == '(' || ch == ')' ||
ch == '<' || ch == '>' || ch == '@' || ch == ',' ||
ch == ';' || ch == ':' || ch == '\\' || ch == '"' ||
ch == '[' || ch == ']' || ch == '?' || ch == '=') {
return false;
} else if (ch == '/') {
if (slash || i == 0 || i + 1 == name.length()) {
return false;
}
slash = true;
}
}
return slash;
}
/**
* The normalized media type name.
*/
private final MediaType type;
/**
* Description of this media type.
*/
private String description = "";
/** The magics associated to this Mime-Type */
private final ArrayList<Magic> magics = new ArrayList<Magic>();
/** The root-XML associated to this Mime-Type */
private final ArrayList<RootXML> rootXML = new ArrayList<RootXML>();
/** The minimum length of data to provides for magic analyzis */
private int minLength = 0;
/** Preferred extension with starting dot or empty string */
private String extension = "";
/**
* Creates a media type with the give name and containing media type
* registry. The name is expected to be valid and normalized to lower
* case. This constructor should only be called by
* {@link MimeTypes#forName(String)} to keep the media type registry
* up to date.
*
* @param type normalized media type name
*/
MimeType(MediaType type) {
if (type == null) {
throw new IllegalArgumentException("Media type name is missing");
}
this.type = type;
}
/**
* Returns the normalized media type name.
*
* @return media type
*/
public MediaType getType() {
return type;
}
/**
* Returns the name of this media type.
*
* @return media type name (lower case)
*/
public String getName() {
return type.toString();
}
/**
* Returns the description of this media type.
*
* @return media type description
*/
public String getDescription() {
return description;
}
/**
* Set the description of this media type.
*
* @param description media type description
*/
public void setDescription(String description) {
if (description == null) {
throw new IllegalArgumentException("Description is missing");
}
this.description = description;
}
/**
* Add some rootXML info to this mime-type
*
* @param namespaceURI
* @param localName
*/
void addRootXML(String namespaceURI, String localName) {
rootXML.add(new RootXML(this, namespaceURI, localName));
}
boolean matchesXML(String namespaceURI, String localName) {
for (RootXML xml : rootXML) {
if (xml.matches(namespaceURI, localName)) {
return true;
}
}
return false;
}
boolean hasRootXML() {
return (rootXML.size() > 0);
}
RootXML[] getRootXMLs() {
return rootXML.toArray(new RootXML[rootXML.size()]);
}
Magic[] getMagics() {
return magics.toArray(new Magic[magics.size()]);
}
void addMagic(Magic magic) {
if (magic == null) {
return;
}
magics.add(magic);
}
int getMinLength() {
return minLength;
}
public boolean hasMagic() {
return (magics.size() > 0);
}
public boolean matchesMagic(byte[] data) {
for (int i = 0; i < magics.size(); i++) {
Magic magic = magics.get(i);
if (magic.eval(data)) {
return true;
}
}
return false;
}
public boolean matches(byte[] data) {
return matchesMagic(data);
}
/**
* Defines a RootXML description. RootXML is made of a localName and/or a
* namespaceURI.
*/
class RootXML implements Serializable {
/**
* Serial version UID.
*/
private static final long serialVersionUID = 5140496601491000730L;
private MimeType type = null;
private String namespaceURI = null;
private String localName = null;
RootXML(MimeType type, String namespaceURI, String localName) {
if (isEmpty(namespaceURI) && isEmpty(localName)) {
throw new IllegalArgumentException(
"Both namespaceURI and localName cannot be empty");
}
this.type = type;
this.namespaceURI = namespaceURI;
this.localName = localName;
}
boolean matches(String namespaceURI, String localName) {
//Compare namespaces
if (!isEmpty(this.namespaceURI)) {
if (!this.namespaceURI.equals(namespaceURI)) {
return false;
}
}
else{
// else if it was empty then check to see if the provided namespaceURI
// is empty. If it is not, then these two aren't equal and return false
if(!isEmpty(namespaceURI)){
return false;
}
}
//Compare root element's local name
if (!isEmpty(this.localName)) {
if (!this.localName.equals(localName)) {
return false;
}
}
else{
// else if it was empty then check to see if the provided localName
// is empty. If it is not, then these two aren't equal and return false
if(!isEmpty(localName)){
return false;
}
}
return true;
}
/**
* Checks if a string is null or empty.
*/
private boolean isEmpty(String str) {
return (str == null) || (str.equals(""));
}
MimeType getType() {
return type;
}
String getNameSpaceURI() {
return namespaceURI;
}
String getLocalName() {
return localName;
}
public String toString() {
return type + ", " + namespaceURI + ", " + localName;
}
}
//----------------------------------------------------------< Comparable >
public int compareTo(MimeType mime) {
return type.compareTo(mime.type);
}
//--------------------------------------------------------------< Object >
/**
* Returns the name of this media type.
*
* @return media type name
*/
public String toString() {
return type.toString();
}
/**
* Get preferred extension
*
* @return extension (with starting dot) or empty string
*/
public String getExtension() {
return extension;
}
void setExtension(String extension) {
this.extension = extension;
}
}