git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1036215 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_8_BETA1
<changes> | <changes> | ||||
<release version="3.8-beta1" date="2010-??-??"> | <release version="3.8-beta1" date="2010-??-??"> | ||||
<action dev="poi-developers" type="fix">50154 - Allow white spaces and unicode in OPC relationship targets </action> | |||||
<action dev="poi-developers" type="fix">50113 - Remove cell from Calculation Chain after setting cell type to blank </action> | <action dev="poi-developers" type="fix">50113 - Remove cell from Calculation Chain after setting cell type to blank </action> | ||||
<action dev="poi-developers" type="fix">49966 - Ensure that XSSFRow#removeCell cleares calculation chain entries </action> | <action dev="poi-developers" type="fix">49966 - Ensure that XSSFRow#removeCell cleares calculation chain entries </action> | ||||
<action dev="poi-developers" type="fix">50096 - Fixed evaluation of cell references with column index greater than 255 </action> | <action dev="poi-developers" type="fix">50096 - Fixed evaluation of cell references with column index greater than 255 </action> |
PackageRelationship.TARGET_ATTRIBUTE_NAME) | PackageRelationship.TARGET_ATTRIBUTE_NAME) | ||||
.getValue(); | .getValue(); | ||||
if (value.indexOf("\\") != -1) { | |||||
logger | |||||
.log(POILogger.INFO, "target contains \\ therefore not a valid URI" | |||||
+ value + " replaced by /"); | |||||
value = value.replaceAll("\\\\", "/"); | |||||
// word can save external relationship with a \ instead | |||||
// of / | |||||
} | |||||
target = new URI(value); | |||||
target = PackagingURIHelper.toURI(value); | |||||
} catch (URISyntaxException e) { | } catch (URISyntaxException e) { | ||||
logger.log(POILogger.ERROR, "Cannot convert " + value | logger.log(POILogger.ERROR, "Cannot convert " + value | ||||
+ " in a valid relationship URI-> ignored", e); | + " in a valid relationship URI-> ignored", e); |
*/ | */ | ||||
String IMAGE_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"; | String IMAGE_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"; | ||||
/** | |||||
* Hyperlink type. | |||||
*/ | |||||
String HYPERLINK_PART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"; | |||||
/** | /** | ||||
* Style type. | * Style type. | ||||
*/ | */ |
import java.net.URI; | import java.net.URI; | ||||
import java.net.URISyntaxException; | import java.net.URISyntaxException; | ||||
import java.nio.ByteBuffer; | |||||
import java.io.UnsupportedEncodingException; | |||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | ||||
import org.apache.poi.openxml4j.exceptions.InvalidOperationException; | import org.apache.poi.openxml4j.exceptions.InvalidOperationException; | ||||
// form must actually be an absolute URI | // form must actually be an absolute URI | ||||
if(sourceURI.toString().equals("/")) { | if(sourceURI.toString().equals("/")) { | ||||
String path = targetURI.getPath(); | String path = targetURI.getPath(); | ||||
if(msCompatible && path.charAt(0) == '/') { | |||||
if(msCompatible && path.length() > 0 && path.charAt(0) == '/') { | |||||
try { | try { | ||||
targetURI = new URI(path.substring(1)); | targetURI = new URI(path.substring(1)); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
} | } | ||||
} | } | ||||
// if the target had a fragment then append it to the result | |||||
String fragment = targetURI.getRawFragment(); | |||||
if (fragment != null) { | |||||
retVal.append("#").append(fragment); | |||||
} | |||||
try { | try { | ||||
return new URI(retVal.toString()); | return new URI(retVal.toString()); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
* Get URI from a string path. | * Get URI from a string path. | ||||
*/ | */ | ||||
public static URI getURIFromPath(String path) { | public static URI getURIFromPath(String path) { | ||||
URI retUri = null; | |||||
URI retUri; | |||||
try { | try { | ||||
retUri = new URI(path); | |||||
retUri = toURI(path); | |||||
} catch (URISyntaxException e) { | } catch (URISyntaxException e) { | ||||
throw new IllegalArgumentException("path"); | throw new IllegalArgumentException("path"); | ||||
} | } | ||||
throws InvalidFormatException { | throws InvalidFormatException { | ||||
URI partNameURI; | URI partNameURI; | ||||
try { | try { | ||||
partNameURI = new URI(resolvePartName(partName)); | |||||
partNameURI = toURI(partName); | |||||
} catch (URISyntaxException e) { | } catch (URISyntaxException e) { | ||||
throw new InvalidFormatException(e.getMessage()); | throw new InvalidFormatException(e.getMessage()); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* If part name is not a valid URI, it is resolved as follows: | |||||
* Convert a string to {@link java.net.URI} | |||||
* | |||||
* If part name is not a valid URI, it is resolved as follows: | |||||
* <p> | * <p> | ||||
* 1. Percent-encode each open bracket ([) and close bracket (]).</li> | * 1. Percent-encode each open bracket ([) and close bracket (]).</li> | ||||
* 2. Percent-encode each percent (%) character that is not followed by a hexadecimal notation of an octet value.</li> | * 2. Percent-encode each percent (%) character that is not followed by a hexadecimal notation of an octet value.</li> | ||||
* in ?5.2 of RFC 3986. The path component of the resulting absolute URI is the part name. | * in ?5.2 of RFC 3986. The path component of the resulting absolute URI is the part name. | ||||
*</p> | *</p> | ||||
* | * | ||||
* @param partName the name to resolve | |||||
* @param value the string to be parsed into a URI | |||||
* @return the resolved part name that should be OK to construct a URI | * @return the resolved part name that should be OK to construct a URI | ||||
* | * | ||||
* TODO YK: for now this method does only (5). Finish the rest. | * TODO YK: for now this method does only (5). Finish the rest. | ||||
*/ | */ | ||||
public static String resolvePartName(String partName){ | |||||
return partName.replace('\\', '/'); | |||||
public static URI toURI(String value) throws URISyntaxException { | |||||
//5. Convert all back slashes to forward slashes | |||||
if (value.indexOf("\\") != -1) { | |||||
value = value.replace('\\', '/'); | |||||
} | |||||
// URI fragemnts (those starting with '#') are not encoded | |||||
// and may contain white spaces and raw unicode characters | |||||
int fragmentIdx = value.indexOf('#'); | |||||
if(fragmentIdx != -1){ | |||||
String path = value.substring(0, fragmentIdx); | |||||
String fragment = value.substring(fragmentIdx + 1); | |||||
value = path + "#" + encode(fragment); | |||||
} | |||||
return new URI(value); | |||||
} | |||||
/** | |||||
* percent-encode white spaces and characters above 0x80. | |||||
* <p> | |||||
* Examples: | |||||
* 'Apache POI' --> 'Apache%20POI' | |||||
* 'Apache\u0410POI' --> 'Apache%04%10POI' | |||||
* | |||||
* @param s the string to encode | |||||
* @return the encoded string | |||||
*/ | |||||
public static String encode(String s) { | |||||
int n = s.length(); | |||||
if (n == 0) return s; | |||||
ByteBuffer bb; | |||||
try { | |||||
bb = ByteBuffer.wrap(s.getBytes("UTF-8")); | |||||
} catch (UnsupportedEncodingException e){ | |||||
// should not happen | |||||
throw new RuntimeException(e); | |||||
} | |||||
StringBuilder sb = new StringBuilder(); | |||||
while (bb.hasRemaining()) { | |||||
int b = bb.get() & 0xff; | |||||
if (isUnsafe(b)) { | |||||
sb.append('%'); | |||||
sb.append(hexDigits[(b >> 4) & 0x0F]); | |||||
sb.append(hexDigits[(b >> 0) & 0x0F]); | |||||
} else { | |||||
sb.append((char)b); | |||||
} | |||||
} | |||||
return sb.toString(); | |||||
} | } | ||||
private final static char[] hexDigits = { | |||||
'0', '1', '2', '3', '4', '5', '6', '7', | |||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' | |||||
}; | |||||
private static boolean isUnsafe(int ch) { | |||||
return ch > 0x80 || " ".indexOf(ch) >= 0; | |||||
} | |||||
} | } |
} else { | } else { | ||||
URI targetURI = rel.getTargetURI(); | URI targetURI = rel.getTargetURI(); | ||||
targetValue = PackagingURIHelper.relativizeURI( | targetValue = PackagingURIHelper.relativizeURI( | ||||
sourcePartURI, targetURI, true).getPath(); | |||||
if (targetURI.getRawFragment() != null) { | |||||
targetValue += "#" + targetURI.getRawFragment(); | |||||
} | |||||
sourcePartURI, targetURI, true).toString(); | |||||
} | } | ||||
relElem.addAttribute(PackageRelationship.TARGET_ATTRIBUTE_NAME, | relElem.addAttribute(PackageRelationship.TARGET_ATTRIBUTE_NAME, | ||||
targetValue); | targetValue); |
package org.apache.poi.openxml4j.opc; | package org.apache.poi.openxml4j.opc; | ||||
import java.net.URI; | import java.net.URI; | ||||
import java.net.URISyntaxException; | |||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
public void testRelativizeURI() throws Exception { | public void testRelativizeURI() throws Exception { | ||||
URI uri1 = new URI("/word/document.xml"); | URI uri1 = new URI("/word/document.xml"); | ||||
URI uri2 = new URI("/word/media/image1.gif"); | URI uri2 = new URI("/word/media/image1.gif"); | ||||
URI uri3 = new URI("/word/media/image1.gif#Sheet1!A1"); | |||||
URI uri4 = new URI("#'My%20Sheet1'!A1"); | |||||
// Document to image is down a directory | // Document to image is down a directory | ||||
URI retURI1to2 = PackagingURIHelper.relativizeURI(uri1, uri2); | URI retURI1to2 = PackagingURIHelper.relativizeURI(uri1, uri2); | ||||
assertEquals("media/image1.gif", retURI1to2.getPath()); | assertEquals("media/image1.gif", retURI1to2.getPath()); | ||||
//URI compatible with MS Office and OpenOffice: leading slash is removed | //URI compatible with MS Office and OpenOffice: leading slash is removed | ||||
uriRes = PackagingURIHelper.relativizeURI(root, uri1, true); | uriRes = PackagingURIHelper.relativizeURI(root, uri1, true); | ||||
assertEquals("word/document.xml", uriRes.toString()); | assertEquals("word/document.xml", uriRes.toString()); | ||||
//preserve URI fragments | |||||
uriRes = PackagingURIHelper.relativizeURI(uri1, uri3, true); | |||||
assertEquals("media/image1.gif#Sheet1!A1", uriRes.toString()); | |||||
uriRes = PackagingURIHelper.relativizeURI(root, uri4, true); | |||||
assertEquals("#'My%20Sheet1'!A1", uriRes.toString()); | |||||
} | } | ||||
/** | /** | ||||
.equals(relativeName)); | .equals(relativeName)); | ||||
pkg.revert(); | pkg.revert(); | ||||
} | } | ||||
public void testCreateURIFromString() throws Exception { | |||||
String[] href = { | |||||
"..\\\\\\cygwin\\home\\yegor\\.vim\\filetype.vim", | |||||
"..\\Program%20Files\\AGEIA%20Technologies\\v2.3.3\\NxCooking.dll", | |||||
"file:///D:\\seva\\1981\\r810102ns.mp3", | |||||
"..\\cygwin\\home\\yegor\\dinom\\%5baccess%5d.2010-10-26.log", | |||||
"#'Instructions (Text)'!B21" | |||||
}; | |||||
for(String s : href){ | |||||
try { | |||||
URI uri = PackagingURIHelper.toURI(s); | |||||
} catch (URISyntaxException e){ | |||||
fail("Failed to create URI from " + s); | |||||
} | |||||
} | |||||
} | |||||
} | } |
package org.apache.poi.openxml4j.opc; | package org.apache.poi.openxml4j.opc; | ||||
import java.io.*; | import java.io.*; | ||||
import java.net.URI; | |||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
pkg.getRelationshipsByType("http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties").getRelationship(0).getTargetURI().toString()); | pkg.getRelationshipsByType("http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties").getRelationship(0).getTargetURI().toString()); | ||||
} | } | ||||
public void testTargetWithSpecialChars() throws Exception{ | |||||
OPCPackage pkg; | |||||
String filepath = OpenXML4JTestDataSamples.getSampleFileName("50154.xlsx"); | |||||
pkg = OPCPackage.open(filepath); | |||||
assert_50154(pkg); | |||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||||
pkg.save(baos); | |||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); | |||||
pkg = OPCPackage.open(bais); | |||||
assert_50154(pkg); | |||||
} | |||||
public void assert_50154(OPCPackage pkg) throws Exception { | |||||
URI drawingURI = new URI("/xl/drawings/drawing1.xml"); | |||||
PackagePart drawingPart = pkg.getPart(PackagingURIHelper.createPartName(drawingURI)); | |||||
PackageRelationshipCollection drawingRels = drawingPart.getRelationships(); | |||||
assertEquals(6, drawingRels.size()); | |||||
// expected one image | |||||
assertEquals(1, drawingPart.getRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image").size()); | |||||
// and three hyperlinks | |||||
assertEquals(5, drawingPart.getRelationshipsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink").size()); | |||||
PackageRelationship rId1 = drawingPart.getRelationship("rId1"); | |||||
URI parent = drawingPart.getPartName().getURI(); | |||||
URI rel1 = parent.relativize(rId1.getTargetURI()); | |||||
URI rel11 = PackagingURIHelper.relativizeURI(drawingPart.getPartName().getURI(), rId1.getTargetURI()); | |||||
assertEquals("'Another Sheet'!A1", rel1.getFragment()); | |||||
PackageRelationship rId2 = drawingPart.getRelationship("rId2"); | |||||
URI rel2 = PackagingURIHelper.relativizeURI(drawingPart.getPartName().getURI(), rId2.getTargetURI()); | |||||
assertEquals("../media/image1.png", rel2.getPath()); | |||||
PackageRelationship rId3 = drawingPart.getRelationship("rId3"); | |||||
URI rel3 = parent.relativize(rId3.getTargetURI()); | |||||
assertEquals("ThirdSheet!A1", rel3.getFragment()); | |||||
PackageRelationship rId4 = drawingPart.getRelationship("rId4"); | |||||
URI rel4 = parent.relativize(rId4.getTargetURI()); | |||||
assertEquals("'\u0410\u043F\u0430\u0447\u0435 \u041F\u041E\u0418'!A1", rel4.getFragment()); | |||||
PackageRelationship rId5 = drawingPart.getRelationship("rId5"); | |||||
URI rel5 = parent.relativize(rId5.getTargetURI()); | |||||
// back slashed have been replaced with forward | |||||
assertEquals("file:///D:/chan-chan.mp3", rel5.toString()); | |||||
PackageRelationship rId6 = drawingPart.getRelationship("rId6"); | |||||
URI rel6 = parent.relativize(rId6.getTargetURI()); | |||||
assertEquals("../../../../../../../cygwin/home/yegor/dinom/&&&[access].2010-10-26.log", rel6.getPath()); | |||||
assertEquals("'\u0410\u043F\u0430\u0447\u0435 \u041F\u041E\u0418'!A5", rel6.getFragment()); | |||||
} | |||||
} | } |