Author: yegor
Date: Thu Nov 18 20:07:15 2010
New Revision: 1036599
URL: http://svn.apache.org/viewvc?rev=1036599&view=rev
Log:
avoid corruption of XSSFWorkbook after applying XSSFRichTextRun#applyFont, see
Bugzilla 50258
Modified:
poi/trunk/src/documentation/content/xdocs/status.xml
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRichTextString.java
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRichTextString.java
Modified: poi/trunk/src/documentation/content/xdocs/status.xml
URL:
http://svn.apache.org/viewvc/poi/trunk/src/documentation/content/xdocs/status.xml?rev=1036599&r1=1036598&r2=1036599&view=diff
==============================================================================
--- poi/trunk/src/documentation/content/xdocs/status.xml (original)
+++ poi/trunk/src/documentation/content/xdocs/status.xml Thu Nov 18 20:07:15
2010
@@ -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>
Modified:
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRichTextString.java
URL:
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRichTextString.java?rev=1036599&r1=1036598&r2=1036599&view=diff
==============================================================================
---
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRichTextString.java
(original)
+++
poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRichTextString.java
Thu Nov 18 20:07:15 2010
@@ -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 implemen
* @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 implemen
}
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();
-
- 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());
- }
- }
+ TreeMap<Integer, CTRPrElt> formats = getFormatMap(st);
+ CTRPrElt fmt = CTRPrElt.Factory.newInstance();
+ setRunAttributes(xssfFont.getCTFont(), fmt);
+ applyFont(formats, startIndex, endIndex, fmt);
-
- st.setRArray(runs.toArray(new CTRElt[runs.size()]));
+ CTRst newSt = buildCTRst(text, formats);
+ st.set(newSt);
}
/**
@@ -205,17 +163,8 @@ public class XSSFRichTextString implemen
* @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 implemen
} else {
font = styles.getFontAt(fontIndex);
}
- applyFont(font);
+ String text = getString();
+ applyFont(0, text.length(), font);
}
/**
@@ -295,9 +245,7 @@ public class XSSFRichTextString implemen
*/
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 implemen
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;
+ }
}
Modified:
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRichTextString.java
URL:
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRichTextString.java?rev=1036599&r1=1036598&r2=1036599&view=diff
==============================================================================
---
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRichTextString.java
(original)
+++
poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRichTextString.java
Thu Nov 18 20:07:15 2010
@@ -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 TestXSSFRichTextStrin
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 TestXSSFRichTextStrin
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());
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]