]> source.dussan.org Git - poi.git/commitdiff
avoid corruption of XSSFWorkbook after applying XSSFRichTextRun#applyFont, see Bugzil...
authorYegor Kozlov <yegor@apache.org>
Thu, 18 Nov 2010 20:07:15 +0000 (20:07 +0000)
committerYegor Kozlov <yegor@apache.org>
Thu, 18 Nov 2010 20:07:15 +0000 (20:07 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1036599 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/status.xml
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRichTextString.java
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRichTextString.java

index be82f7f1429db995b6887c03bd72c9ee42875628..f401b0308c6b73e487d9d51dd472ba19402a1c7e 100644 (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>
index 91afa49803c5310f3e78fc8b44014c07733650a6..d58da0e092a298ef6a89101cb209fbc0daa9e9ff 100644 (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();
-
-            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 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;
+    }
 }
index 5251e06bb29d69c59e5878ac7f1d29cbcb23d4a2..e769663415455e2b623aab199a058f7dcb530dfb 100644 (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());
+    }
 }