avoid corruption of XSSFWorkbook after applying XSSFRichTextRun#applyFont, see Bugzilla 50258

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1036599 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2010-11-18 20:07:15 +00:00
parent 5f11a63d37
commit ba3f0ceaed
3 changed files with 225 additions and 70 deletions

View File

@ -34,6 +34,7 @@
<changes>
<release version="3.8-beta1" date="2010-??-??">
<action dev="poi-developers" type="fix">50258 - avoid corruption of XSSFWorkbook after applying XSSFRichTextRun#applyFont</action>
<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">49966 - Ensure that XSSFRow#removeCell cleares calculation chain entries </action>

View File

@ -17,7 +17,7 @@
package org.apache.poi.xssf.usermodel;
import java.util.ArrayList;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
@ -132,7 +132,6 @@ public class XSSFRichTextString implements RichTextString {
* @param endIndex The end index to apply to font to (exclusive)
* @param font The index of the font to use.
*/
@SuppressWarnings("deprecation") //YK: getXYZArray() array accessors are deprecated in xmlbeans with JDK 1.5 support
public void applyFont(int startIndex, int endIndex, Font font) {
if (startIndex > endIndex)
throw new IllegalArgumentException("Start index must be less than end index.");
@ -148,56 +147,15 @@ public class XSSFRichTextString implements RichTextString {
}
String text = getString();
XSSFFont xssfFont = (XSSFFont)font;
ArrayList<CTRElt> runs = new ArrayList<CTRElt>();
CTRElt[] r = st.getRArray();
int pos = 0;
for (int i = 0; i < r.length; i++) {
int rStart = pos;
String t = r[i].getT();
int rEnd = rStart + t.length();
TreeMap<Integer, CTRPrElt> formats = getFormatMap(st);
CTRPrElt fmt = CTRPrElt.Factory.newInstance();
setRunAttributes(xssfFont.getCTFont(), fmt);
applyFont(formats, startIndex, endIndex, fmt);
if(rEnd <= startIndex) {
runs.add(r[i]);
pos += r[i].getT().length();
}
else if (startIndex > rStart && startIndex < rEnd){
CTRElt c = (CTRElt)r[i].copy();
String txt = text.substring(rStart, startIndex);
c.setT(txt);
runs.add(c);
pos += txt.length();
} else {
break;
}
}
CTRElt rt = CTRElt.Factory.newInstance();
String txt = text.substring(startIndex, endIndex);
rt.setT(txt);
CTRPrElt pr = rt.addNewRPr();
setRunAttributes(xssfFont.getCTFont(), pr);
runs.add(rt);
pos += txt.length();
for (int i = 0; i < r.length; i++) {
int rStart = pos;
String t = r[i].getT();
int rEnd = Math.min(rStart + t.length(), text.length());
if (endIndex < rEnd){
CTRElt c = (CTRElt)r[i].copy();
txt = text.substring(rStart, rEnd);
c.setT(txt);
runs.add(c);
pos += txt.length();
preserveSpaces(c.xgetT());
}
}
st.setRArray(runs.toArray(new CTRElt[runs.size()]));
CTRst newSt = buildCTRst(text, formats);
st.set(newSt);
}
/**
@ -205,17 +163,8 @@ public class XSSFRichTextString implements RichTextString {
* @param font The font to use.
*/
public void applyFont(Font font) {
if(st.sizeOfRArray() == 0 && st.isSetT()) {
CTRElt r = st.addNewR();
r.setT(st.getT());
setRunAttributes(((XSSFFont)font).getCTFont(), r.addNewRPr());
st.unsetT();
} else {
CTRElt r = CTRElt.Factory.newInstance();
r.setT(getString());
setRunAttributes(((XSSFFont)font).getCTFont(), r.addNewRPr());
st.setRArray(new CTRElt[]{r});
}
String text = getString();
applyFont(0, text.length(), font);
}
/**
@ -231,7 +180,8 @@ public class XSSFRichTextString implements RichTextString {
} else {
font = styles.getFontAt(fontIndex);
}
applyFont(font);
String text = getString();
applyFont(0, text.length(), font);
}
/**
@ -295,9 +245,7 @@ public class XSSFRichTextString implements RichTextString {
*/
public void clearFormatting() {
String text = getString();
while (st.sizeOfRArray() > 0) {
st.removeR(st.sizeOfRArray()-1);
}
st.setRArray(null);
st.setT(text);
}
@ -531,4 +479,62 @@ public class XSSFRichTextString implements RichTextString {
buf.append(value.substring(idx));
return buf.toString();
}
void applyFont(TreeMap<Integer, CTRPrElt> formats, int startIndex, int endIndex, CTRPrElt fmt) {
// delete format runs that fit between startIndex and endIndex
// runs intersecting startIndex and endIndex remain
int runStartIdx = 0;
for (Iterator<Integer> it = formats.keySet().iterator(); it.hasNext();) {
int runEndIdx = it.next();
if (runStartIdx >= startIndex && runEndIdx < endIndex) {
it.remove();
}
runStartIdx = runEndIdx;
}
if(startIndex > 0 && !formats.containsKey(startIndex)) {
Map.Entry<Integer, CTRPrElt> he = formats.higherEntry(startIndex); //TODO TreeMap#higherEntry is JDK 1.6 only!
if(he != null) formats.put(startIndex, he.getValue());
}
formats.put(endIndex, fmt);
// assure that the range [startIndex, endIndex] consists if a single run
// there can be two or three runs depending whether startIndex or endIndex
// intersected existing format runs
SortedMap<Integer, CTRPrElt> sub = formats.subMap(startIndex, endIndex);
while(sub.size() > 1) sub.remove(sub.lastKey());
}
TreeMap<Integer, CTRPrElt> getFormatMap(CTRst entry){
int length = 0;
TreeMap<Integer, CTRPrElt> formats = new TreeMap<Integer, CTRPrElt>();
for (CTRElt r : entry.getRArray()) {
String txt = r.getT();
CTRPrElt fmt = r.getRPr();
length += txt.length();
formats.put(length, fmt);
}
return formats;
}
CTRst buildCTRst(String text, TreeMap<Integer, CTRPrElt> formats){
if(text.length() != formats.lastKey()) {
throw new IllegalArgumentException("Text length was " + text.length() +
" but the last format index was " + formats.lastKey());
}
CTRst st = CTRst.Factory.newInstance();
int runStartIdx = 0;
for (Iterator<Integer> it = formats.keySet().iterator(); it.hasNext();) {
int runEndIdx = it.next();
CTRElt run = st.addNewR();
String fragment = text.substring(runStartIdx, runEndIdx);
run.setT(fragment);
preserveSpaces(run.xgetT());
CTRPrElt fmt = formats.get(runEndIdx);
if(fmt != null) run.setRPr(fmt);
runStartIdx = runEndIdx;
}
return st;
}
}

View File

@ -21,6 +21,9 @@ import junit.framework.TestCase;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STXstring;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRPrElt;
import java.util.TreeMap;
/**
* Tests functionality of the XSSFRichTextRun object
@ -53,23 +56,27 @@ public final class TestXSSFRichTextString extends TestCase {
rt.append("4567");
rt.append("89");
assertEquals("123456789", rt.getString());
XSSFFont font1 = new XSSFFont();
font1.setBold(true);
rt.applyFont(2, 5, font1);
assertEquals(5, rt.numFormattingRuns());
assertEquals(4, rt.numFormattingRuns());
assertEquals(0, rt.getIndexOfFormattingRun(0));
assertEquals(2, rt.getLengthOfFormattingRun(0));
assertEquals("12", rt.getCTRst().getRArray(0).getT());
assertEquals(2, rt.getIndexOfFormattingRun(1));
assertEquals(3, rt.getLengthOfFormattingRun(1));
assertEquals("345", rt.getCTRst().getRArray(1).getT());
assertEquals(5, rt.getIndexOfFormattingRun(2));
assertEquals(3, rt.getLengthOfFormattingRun(2));
assertEquals(2, rt.getLengthOfFormattingRun(2));
assertEquals("67", rt.getCTRst().getRArray(2).getT());
assertEquals(8, rt.getIndexOfFormattingRun(3));
assertEquals(1, rt.getLengthOfFormattingRun(3));
assertEquals(7, rt.getIndexOfFormattingRun(3));
assertEquals(2, rt.getLengthOfFormattingRun(3));
assertEquals("89", rt.getCTRst().getRArray(3).getT());
}
public void testClearFormatting() {
@ -142,4 +149,145 @@ public final class TestXSSFRichTextString extends TestCase {
assertEquals("abc\r2ef\r", rt.getString());
}
public void testApplyFont_lowlevel(){
CTRst st = CTRst.Factory.newInstance();
String text = "Apache Software Foundation";
XSSFRichTextString str = new XSSFRichTextString(text);
assertEquals(26, text.length());
st.addNewR().setT(text);
TreeMap<Integer, CTRPrElt> formats = str.getFormatMap(st);
assertEquals(1, formats.size());
assertEquals(26, (int)formats.firstEntry().getKey());
assertNull(formats.firstEntry().getValue());
CTRPrElt fmt1 = CTRPrElt.Factory.newInstance();
str.applyFont(formats, 0, 6, fmt1);
assertEquals(2, formats.size());
assertEquals("[6, 26]", formats.keySet().toString());
Object[] runs1 = formats.values().toArray();
assertSame(fmt1, runs1[0]);
assertSame(null, runs1[1]);
CTRPrElt fmt2 = CTRPrElt.Factory.newInstance();
str.applyFont(formats, 7, 15, fmt2);
assertEquals(4, formats.size());
assertEquals("[6, 7, 15, 26]", formats.keySet().toString());
Object[] runs2 = formats.values().toArray();
assertSame(fmt1, runs2[0]);
assertSame(null, runs2[1]);
assertSame(fmt2, runs2[2]);
assertSame(null, runs2[3]);
CTRPrElt fmt3 = CTRPrElt.Factory.newInstance();
str.applyFont(formats, 6, 7, fmt3);
assertEquals(4, formats.size());
assertEquals("[6, 7, 15, 26]", formats.keySet().toString());
Object[] runs3 = formats.values().toArray();
assertSame(fmt1, runs3[0]);
assertSame(fmt3, runs3[1]);
assertSame(fmt2, runs3[2]);
assertSame(null, runs3[3]);
CTRPrElt fmt4 = CTRPrElt.Factory.newInstance();
str.applyFont(formats, 0, 7, fmt4);
assertEquals(3, formats.size());
assertEquals("[7, 15, 26]", formats.keySet().toString());
Object[] runs4 = formats.values().toArray();
assertSame(fmt4, runs4[0]);
assertSame(fmt2, runs4[1]);
assertSame(null, runs4[2]);
CTRPrElt fmt5 = CTRPrElt.Factory.newInstance();
str.applyFont(formats, 0, 26, fmt5);
assertEquals(1, formats.size());
assertEquals("[26]", formats.keySet().toString());
Object[] runs5 = formats.values().toArray();
assertSame(fmt5, runs5[0]);
CTRPrElt fmt6 = CTRPrElt.Factory.newInstance();
str.applyFont(formats, 15, 26, fmt6);
assertEquals(2, formats.size());
assertEquals("[15, 26]", formats.keySet().toString());
Object[] runs6 = formats.values().toArray();
assertSame(fmt5, runs6[0]);
assertSame(fmt6, runs6[1]);
str.applyFont(formats, 0, 26, null);
assertEquals(1, formats.size());
assertEquals("[26]", formats.keySet().toString());
Object[] runs7 = formats.values().toArray();
assertSame(null, runs7[0]);
str.applyFont(formats, 15, 26, fmt6);
assertEquals(2, formats.size());
assertEquals("[15, 26]", formats.keySet().toString());
Object[] runs8 = formats.values().toArray();
assertSame(null, runs8[0]);
assertSame(fmt6, runs8[1]);
str.applyFont(formats, 15, 26, fmt5);
assertEquals(2, formats.size());
assertEquals("[15, 26]", formats.keySet().toString());
Object[] runs9 = formats.values().toArray();
assertSame(null, runs9[0]);
assertSame(fmt5, runs9[1]);
str.applyFont(formats, 2, 20, fmt6);
assertEquals(3, formats.size());
assertEquals("[2, 20, 26]", formats.keySet().toString());
Object[] runs10 = formats.values().toArray();
assertSame(null, runs10[0]);
assertSame(fmt6, runs10[1]);
assertSame(fmt5, runs10[2]);
str.applyFont(formats, 22, 24, fmt4);
assertEquals(5, formats.size());
assertEquals("[2, 20, 22, 24, 26]", formats.keySet().toString());
Object[] runs11 = formats.values().toArray();
assertSame(null, runs11[0]);
assertSame(fmt6, runs11[1]);
assertSame(fmt5, runs11[2]);
assertSame(fmt4, runs11[3]);
assertSame(fmt5, runs11[4]);
str.applyFont(formats, 0, 10, fmt1);
assertEquals(5, formats.size());
assertEquals("[10, 20, 22, 24, 26]", formats.keySet().toString());
Object[] runs12 = formats.values().toArray();
assertSame(fmt1, runs12[0]);
assertSame(fmt6, runs12[1]);
assertSame(fmt5, runs12[2]);
assertSame(fmt4, runs12[3]);
assertSame(fmt5, runs12[4]);
}
public void testApplyFont_usermodel(){
String text = "Apache Software Foundation";
XSSFRichTextString str = new XSSFRichTextString(text);
XSSFFont font1 = new XSSFFont();
XSSFFont font2 = new XSSFFont();
XSSFFont font3 = new XSSFFont();
str.applyFont(font1);
assertEquals(1, str.numFormattingRuns());
str.applyFont(0, 6, font1);
str.applyFont(6, text.length(), font2);
assertEquals(2, str.numFormattingRuns());
assertEquals("Apache", str.getCTRst().getRArray(0).getT());
assertEquals(" Software Foundation", str.getCTRst().getRArray(1).getT());
str.applyFont(15, 26, font3);
assertEquals(3, str.numFormattingRuns());
assertEquals("Apache", str.getCTRst().getRArray(0).getT());
assertEquals(" Software", str.getCTRst().getRArray(1).getT());
assertEquals(" Foundation", str.getCTRst().getRArray(2).getT());
str.applyFont(6, text.length(), font2);
assertEquals(2, str.numFormattingRuns());
assertEquals("Apache", str.getCTRst().getRArray(0).getT());
assertEquals(" Software Foundation", str.getCTRst().getRArray(1).getT());
}
}