Home | History | Annotate | Download | only in okhttp
      1 /*
      2  * Copyright (C) 2013 Square, Inc.
      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 com.squareup.okhttp;
     17 
     18 import java.nio.charset.Charset;
     19 import java.util.Locale;
     20 import java.util.regex.Matcher;
     21 import java.util.regex.Pattern;
     22 
     23 /**
     24  * An <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a> Media Type,
     25  * appropriate to describe the content type of an HTTP request or response body.
     26  */
     27 public final class MediaType {
     28   private static final String TOKEN = "([a-zA-Z0-9-!#$%&'*+.^_`{|}~]+)";
     29   private static final String QUOTED = "\"([^\"]*)\"";
     30   private static final Pattern TYPE_SUBTYPE = Pattern.compile(TOKEN + "/" + TOKEN);
     31   private static final Pattern PARAMETER = Pattern.compile(
     32       ";\\s*(?:" + TOKEN + "=(?:" + TOKEN + "|" + QUOTED + "))?");
     33 
     34   private final String mediaType;
     35   private final String type;
     36   private final String subtype;
     37   private final String charset;
     38 
     39   private MediaType(String mediaType, String type, String subtype, String charset) {
     40     this.mediaType = mediaType;
     41     this.type = type;
     42     this.subtype = subtype;
     43     this.charset = charset;
     44   }
     45 
     46   /**
     47    * Returns a media type for {@code string}, or null if {@code string} is not a
     48    * well-formed media type.
     49    */
     50   public static MediaType parse(String string) {
     51     Matcher typeSubtype = TYPE_SUBTYPE.matcher(string);
     52     if (!typeSubtype.lookingAt()) return null;
     53     String type = typeSubtype.group(1).toLowerCase(Locale.US);
     54     String subtype = typeSubtype.group(2).toLowerCase(Locale.US);
     55 
     56     String charset = null;
     57     Matcher parameter = PARAMETER.matcher(string);
     58     for (int s = typeSubtype.end(); s < string.length(); s = parameter.end()) {
     59       parameter.region(s, string.length());
     60       if (!parameter.lookingAt()) return null; // This is not a well-formed media type.
     61 
     62       String name = parameter.group(1);
     63       if (name == null || !name.equalsIgnoreCase("charset")) continue;
     64       String charsetParameter = parameter.group(2) != null
     65           ? parameter.group(2)  // Value is a token.
     66           : parameter.group(3); // Value is a quoted string.
     67       if (charset != null && !charsetParameter.equalsIgnoreCase(charset)) {
     68         throw new IllegalArgumentException("Multiple different charsets: " + string);
     69       }
     70       charset = charsetParameter;
     71     }
     72 
     73     return new MediaType(string, type, subtype, charset);
     74   }
     75 
     76   /**
     77    * Returns the high-level media type, such as "text", "image", "audio",
     78    * "video", or "application".
     79    */
     80   public String type() {
     81     return type;
     82   }
     83 
     84   /**
     85    * Returns a specific media subtype, such as "plain" or "png", "mpeg",
     86    * "mp4" or "xml".
     87    */
     88   public String subtype() {
     89     return subtype;
     90   }
     91 
     92   /**
     93    * Returns the charset of this media type, or null if this media type doesn't
     94    * specify a charset.
     95    */
     96   public Charset charset() {
     97     return charset != null ? Charset.forName(charset) : null;
     98   }
     99 
    100   /**
    101    * Returns the charset of this media type, or {@code defaultValue} if this
    102    * media type doesn't specify a charset.
    103    */
    104   public Charset charset(Charset defaultValue) {
    105     return charset != null ? Charset.forName(charset) : defaultValue;
    106   }
    107 
    108   /**
    109    * Returns the encoded media type, like "text/plain; charset=utf-8",
    110    * appropriate for use in a Content-Type header.
    111    */
    112   @Override public String toString() {
    113     return mediaType;
    114   }
    115 
    116   @Override public boolean equals(Object o) {
    117     return o instanceof MediaType && ((MediaType) o).mediaType.equals(mediaType);
    118   }
    119 
    120   @Override public int hashCode() {
    121     return mediaType.hashCode();
    122   }
    123 }
    124