diff --git a/okhttp/src/main/java/com/squareup/okhttp/MediaType.java b/okhttp/src/main/java/com/squareup/okhttp/MediaType.java new file mode 100644 index 000000000..2c09596ff --- /dev/null +++ b/okhttp/src/main/java/com/squareup/okhttp/MediaType.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * 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 com.squareup.okhttp; + +import java.nio.charset.Charset; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * An RFC 2045 Media Type, + * appropriate to describe the content type of an HTTP request or response body. + */ +public final class MediaType { + private static final String TOKEN = "([a-zA-Z0-9-!#$%&'*+.^_`{|}~]+)"; + private static final String QUOTED = "\"([^\"]*)\""; + private static final Pattern TYPE_SUBTYPE = Pattern.compile(TOKEN + "/" + TOKEN); + private static final Pattern PARAMETER = Pattern.compile( + ";\\s*" + TOKEN + "=(?:" + TOKEN + "|" + QUOTED + ")"); + + private final String mediaType; + private final String type; + private final String subtype; + private final String charset; + + private MediaType(String mediaType, String type, String subtype, String charset) { + this.mediaType = mediaType; + this.type = type; + this.subtype = subtype; + this.charset = charset; + } + + /** + * Returns a media type for {@code string}, or null if {@code string} is not a + * well-formed media type. + */ + public static MediaType parse(String string) { + Matcher typeSubtype = TYPE_SUBTYPE.matcher(string); + if (!typeSubtype.lookingAt()) return null; + String type = typeSubtype.group(1).toLowerCase(Locale.US); + String subtype = typeSubtype.group(2).toLowerCase(Locale.US); + + String charset = null; + Matcher parameter = PARAMETER.matcher(string); + for (int s = typeSubtype.end(); s < string.length(); s = parameter.end()) { + parameter.region(s, string.length()); + if (!parameter.lookingAt()) return null; // This is not a well-formed media type. + + String name = parameter.group(1); + if (name == null || !name.equalsIgnoreCase("charset")) continue; + if (charset != null) throw new IllegalArgumentException("Multiple charsets: " + string); + charset = parameter.group(2) != null + ? parameter.group(2) // Value is a token. + : parameter.group(3); // Value is a quoted string. + } + + return new MediaType(string, type, subtype, charset); + } + + /** + * Returns the high-level media type, such as "text", "image", "audio", + * "video", or "application". + */ + public String type() { + return type; + } + + /** + * Returns a specific media subtype, such as "plain" or "png", "mpeg", + * "mp4" or "xml". + */ + public String subtype() { + return subtype; + } + + /** + * Returns the charset of this media type, or null if this media type doesn't + * specify a charset. + */ + public Charset charset() { + return charset != null ? Charset.forName(charset) : null; + } + + /** + * Returns the charset of this media type, or {@code defaultValue} if this + * media type doesn't specify a charset. + */ + public Charset charset(Charset defaultValue) { + return charset != null ? Charset.forName(charset) : defaultValue; + } + + /** + * Returns the encoded media type, like "text/plain; charset=utf-8", + * appropriate for use in a Content-Type header. + */ + @Override public String toString() { + return mediaType; + } + + @Override public boolean equals(Object o) { + return o instanceof MediaType && ((MediaType) o).mediaType.equals(mediaType); + } + + @Override public int hashCode() { + return mediaType.hashCode(); + } +} diff --git a/okhttp/src/test/java/com/squareup/okhttp/MediaTypeTest.java b/okhttp/src/test/java/com/squareup/okhttp/MediaTypeTest.java new file mode 100644 index 000000000..acbfdd5c1 --- /dev/null +++ b/okhttp/src/test/java/com/squareup/okhttp/MediaTypeTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2013 Square, Inc. + * Copyright (C) 2011 The Guava Authors + * + * 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 com.squareup.okhttp; + +import com.squareup.okhttp.internal.Util; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Test MediaType API and parsing. + * + *
This test includes tests from Guava's MediaTypeTest.
+ */
+public class MediaTypeTest {
+ @Test public void testParse() throws Exception {
+ MediaType mediaType = MediaType.parse("text/plain;boundary=foo;charset=utf-8");
+ assertEquals("text", mediaType.type());
+ assertEquals("plain", mediaType.subtype());
+ assertEquals("UTF-8", mediaType.charset().name());
+ assertEquals("text/plain;boundary=foo;charset=utf-8", mediaType.toString());
+ assertTrue(mediaType.equals(MediaType.parse("text/plain;boundary=foo;charset=utf-8")));
+ assertEquals(mediaType.hashCode(),
+ MediaType.parse("text/plain;boundary=foo;charset=utf-8").hashCode());
+ }
+
+ @Test public void testValidParse() throws Exception {
+ assertMediaType("text/plain");
+ assertMediaType("application/atom+xml; charset=utf-8");
+ assertMediaType("application/atom+xml; a=1; a=2; b=3");
+ assertMediaType("image/gif; foo=bar");
+ assertMediaType("text/plain; a=1");
+ assertMediaType("text/plain; a=1; a=2; b=3");
+ assertMediaType("text/plain; charset=utf-16");
+ assertMediaType("text/plain; \t \n \r a=b");
+ }
+
+ @Test public void testInvalidParse() throws Exception {
+ assertInvalid("");
+ assertInvalid("/");
+ assertInvalid("/");
+ assertInvalid("text");
+ assertInvalid("text/");
+ assertInvalid("te