import java.io.InputStream; | import java.io.InputStream; | ||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.io.Serializable; | import java.io.Serializable; | ||||
import java.io.UnsupportedEncodingException; | |||||
import java.net.URLEncoder; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.Map; | import java.util.Map; | ||||
import javax.servlet.http.HttpServletResponse; | import javax.servlet.http.HttpServletResponse; | ||||
import com.vaadin.util.EncodeUtil; | |||||
/** | /** | ||||
* Downloadable stream. | * Downloadable stream. | ||||
* <p> | * <p> | ||||
* @return A value for inclusion in a Content-Disposition header | * @return A value for inclusion in a Content-Disposition header | ||||
*/ | */ | ||||
public static String getContentDispositionFilename(String filename) { | 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); | |||||
} | } | ||||
/** | /** |
/* | |||||
* 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.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.net.URLEncoder; | |||||
import org.junit.Before; | import org.junit.Before; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
public class DownloadStreamTest { | 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; | private DownloadStream stream; | ||||
@Before | @Before | ||||
stream.writeResponse(mock(VaadinRequest.class), response); | stream.writeResponse(mock(VaadinRequest.class), response); | ||||
String encodedFileName = URLEncoder.encode(filename, "utf-8"); | |||||
verify(response).setHeader(eq(DownloadStream.CONTENT_DISPOSITION), | verify(response).setHeader(eq(DownloadStream.CONTENT_DISPOSITION), | ||||
contains(String.format("filename=\"%s\";", encodedFileName))); | 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[] 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\\.external\\.org\\.apache\\.commons\\.fileupload\\..*", // | ||||
"com\\.vaadin\\.launcher\\..*", // | "com\\.vaadin\\.launcher\\..*", // | ||||
"com\\.vaadin\\.client\\..*", // | "com\\.vaadin\\.client\\..*", // | ||||
// interfaces | // interfaces | ||||
"com\\.vaadin\\.server\\.LegacyCommunicationManager.*", // | "com\\.vaadin\\.server\\.LegacyCommunicationManager.*", // | ||||
"com\\.vaadin\\.buildhelpers.*", // | "com\\.vaadin\\.buildhelpers.*", // | ||||
"com\\.vaadin\\.util\\.EncodeUtil.*", // | |||||
"com\\.vaadin\\.util\\.ReflectTools.*", // | "com\\.vaadin\\.util\\.ReflectTools.*", // | ||||
"com\\.vaadin\\.data\\.util\\.ReflectTools.*", // | "com\\.vaadin\\.data\\.util\\.ReflectTools.*", // | ||||
"com\\.vaadin\\.data\\.util.BeanItemContainerGenerator.*", | "com\\.vaadin\\.data\\.util.BeanItemContainerGenerator.*", | ||||
nonSerializableString += ")"; | 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()) { | while (e.hasMoreElements()) { | ||||
JarEntry entry = e.nextElement(); | JarEntry entry = e.nextElement(); | ||||
if (entry.getName().endsWith(".class")) { | if (entry.getName().endsWith(".class")) { | ||||
String nameWithoutExtension = entry.getName() | |||||
.replaceAll("\\.class", ""); | |||||
String nameWithoutExtension = entry.getName().replaceAll( | |||||
"\\.class", ""); | |||||
String className = nameWithoutExtension.replace('/', '.'); | String className = nameWithoutExtension.replace('/', '.'); | ||||
classes.add(className); | classes.add(className); | ||||
} | } |
/* | |||||
* 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); | addComponents("Class resource pdf", resource, components); | ||||
Button downloadUtf8File = new Button("Download UTF-8 named file"); | 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.setOverrideContentType(false); | ||||
fd.extend(downloadUtf8File); | fd.extend(downloadUtf8File); | ||||
addComponent(downloadUtf8File); | addComponent(downloadUtf8File); |