import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
+import com.vaadin.util.EncodeUtil;
+
/**
* Downloadable stream.
* <p>
* @return A value for inclusion in a Content-Disposition header
*/
public static String getContentDispositionFilename(String filename) {
- try {
- String encodedFilename = URLEncoder.encode(filename, "UTF-8");
- return String.format("filename=\"%s\"; filename*=utf-8''%s",
- encodedFilename, encodedFilename);
- } catch (UnsupportedEncodingException e) {
- return null;
- }
+ String encodedFilename = EncodeUtil.rfc5987Encode(filename);
+
+ return String.format("filename=\"%s\"; filename*=utf-8''%s",
+ encodedFilename, encodedFilename);
}
/**
--- /dev/null
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * 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.vaadin.util;
+
+import java.nio.charset.Charset;
+
+/**
+ * Utilities related to various encoding schemes.
+ *
+ * @author Vaadin Ltd
+ * @since
+ */
+public class EncodeUtil {
+ private static final Charset utf8 = Charset.forName("UTF-8");
+
+ private EncodeUtil() {
+ // Static utils only
+ }
+
+ /**
+ * Encodes the given string to UTF-8 <code>value-chars</code> as defined in
+ * RFC5987 for use in e.g. the <code>Content-Disposition</code> HTTP header.
+ *
+ * @param value
+ * the string to encode, not <code>null</code>
+ * @return the encoded string
+ */
+ public static String rfc5987Encode(String value) {
+ StringBuilder builder = new StringBuilder();
+
+ for (int i = 0; i < value.length();) {
+ int cp = value.codePointAt(i);
+ if (cp < 127 && (Character.isLetterOrDigit(cp) || cp == '.')) {
+ builder.append((char) cp);
+ } else {
+ // Create string from a single code point
+ String cpAsString = new String(new int[] { cp }, 0, 1);
+
+ appendHexBytes(builder, cpAsString.getBytes(utf8));
+ }
+
+ // Advance to the next code point
+ i += Character.charCount(cp);
+ }
+
+ return builder.toString();
+ }
+
+ private static void appendHexBytes(StringBuilder builder, byte[] bytes) {
+ for (byte byteValue : bytes) {
+ // mask with 0xFF to compensate for "negative" values
+ int intValue = byteValue & 0xFF;
+ String hexCode = Integer.toString(intValue, 16);
+ builder.append('%').append(hexCode);
+ }
+ }
+
+}
import java.io.IOException;
import java.io.InputStream;
-import java.net.URLEncoder;
import org.junit.Before;
import org.junit.Test;
public class DownloadStreamTest {
- private String filename = "日本語.png";
+ private String filename = "A å日.png";
+ private String encodedFileName = "A" + "%20" // space
+ + "%c3%a5" // å
+ + "%e6%97%a5" // 日
+ + ".png";
private DownloadStream stream;
@Before
stream.writeResponse(mock(VaadinRequest.class), response);
- String encodedFileName = URLEncoder.encode(filename, "utf-8");
verify(response).setHeader(eq(DownloadStream.CONTENT_DISPOSITION),
contains(String.format("filename=\"%s\";", encodedFileName)));
- verify(response).setHeader(eq(DownloadStream.CONTENT_DISPOSITION),
- contains(
- String.format("filename*=utf-8''%s", encodedFileName)));
+ verify(response)
+ .setHeader(
+ eq(DownloadStream.CONTENT_DISPOSITION),
+ contains(String.format("filename*=utf-8''%s",
+ encodedFileName)));
}
}
private static String[] BASE_PACKAGES = { "com.vaadin" };
- private static String[] EXCLUDED_PATTERNS = { "com\\.vaadin\\.demo\\..*", //
+ private static String[] EXCLUDED_PATTERNS = {
+ "com\\.vaadin\\.demo\\..*", //
"com\\.vaadin\\.external\\.org\\.apache\\.commons\\.fileupload\\..*", //
"com\\.vaadin\\.launcher\\..*", //
"com\\.vaadin\\.client\\..*", //
// interfaces
"com\\.vaadin\\.server\\.LegacyCommunicationManager.*", //
"com\\.vaadin\\.buildhelpers.*", //
+ "com\\.vaadin\\.util\\.EncodeUtil.*", //
"com\\.vaadin\\.util\\.ReflectTools.*", //
"com\\.vaadin\\.data\\.util\\.ReflectTools.*", //
"com\\.vaadin\\.data\\.util.BeanItemContainerGenerator.*",
nonSerializableString += ")";
}
}
- Assert.fail(
- "Serializable not implemented by the following classes and interfaces: "
- + nonSerializableString);
+ Assert.fail("Serializable not implemented by the following classes and interfaces: "
+ + nonSerializableString);
}
}
while (e.hasMoreElements()) {
JarEntry entry = e.nextElement();
if (entry.getName().endsWith(".class")) {
- String nameWithoutExtension = entry.getName()
- .replaceAll("\\.class", "");
+ String nameWithoutExtension = entry.getName().replaceAll(
+ "\\.class", "");
String className = nameWithoutExtension.replace('/', '.');
classes.add(className);
}
--- /dev/null
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.vaadin.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class EncodeUtilTest {
+ @Test
+ public void rfc5987Encode() {
+ Assert.assertEquals("A", EncodeUtil.rfc5987Encode("A"));
+ Assert.assertEquals("%20", EncodeUtil.rfc5987Encode(" "));
+ Assert.assertEquals("%c3%a5", EncodeUtil.rfc5987Encode("å"));
+ Assert.assertEquals("%e6%97%a5", EncodeUtil.rfc5987Encode("日"));
+
+ Assert.assertEquals("A" + "%20" + "%c3%a5" + "%e6%97%a5",
+ EncodeUtil.rfc5987Encode("A å日"));
+ }
+}
addComponents("Class resource pdf", resource, components);
Button downloadUtf8File = new Button("Download UTF-8 named file");
- FileDownloader fd = new FileDownloader(
- new ClassResource(new EmbeddedPdf().getClass(), "åäö-日本語.pdf"));
+ FileDownloader fd = new FileDownloader(new ClassResource(
+ new EmbeddedPdf().getClass(), "File åäö-日本語.pdf"));
fd.setOverrideContentType(false);
fd.extend(downloadUtf8File);
addComponent(downloadUtf8File);