Browse Source

allow add and remove a HyperlinkRun or a FieldRun

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1875862 13f79535-47bb-0310-9956-ffa450edef68
tags/before_ooxml_3rd_edition
Alain Béarez 4 years ago
parent
commit
ce544b8550

+ 6
- 0
src/examples/src/org/apache/poi/xwpf/usermodel/examples/SimpleDocument.java View File

@@ -27,6 +27,7 @@ import org.apache.poi.xwpf.usermodel.TextAlignment;
import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
import org.apache.poi.xwpf.usermodel.VerticalAlign;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;

@@ -78,6 +79,11 @@ public class SimpleDocument {
r3.setFontSize(20);
r3.setSubscript(VerticalAlign.SUPERSCRIPT);

// hyperlink
XWPFHyperlinkRun hyperlink = p2.insertNewHyperlinkRun(0, "http://poi.apache.org/");
hyperlink.setUnderline(UnderlinePatterns.SINGLE);
hyperlink.setColor("0000ff");
hyperlink.setText("Apache POI");

XWPFParagraph p3 = doc.createParagraph();
p3.setWordWrapped(true);

+ 176
- 45
src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFParagraph.java View File

@@ -20,6 +20,7 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;

import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.util.Internal;
@@ -1478,50 +1479,144 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
}

/**
* insert a new Run in RunArray
* Appends a new field run to this paragraph
*
* @return a new field run
*/
public XWPFFieldRun createFieldRun() {
CTSimpleField ctSimpleField = paragraph.addNewFldSimple();
XWPFFieldRun newRun = new XWPFFieldRun(ctSimpleField, ctSimpleField.addNewR(), this);
runs.add(newRun);
iruns.add(newRun);
return newRun;
}

/**
* insert a new Run in all runs
*
* @param pos The position at which the new run should be added.
*
* @return the inserted run or null if the given pos is out of bounds.
*/
public XWPFRun insertNewRun(int pos) {
if (pos >= 0 && pos <= runs.size()) {
// calculate the correct pos as our run/irun list contains
// hyperlinks
// and fields so it is different to the paragraph R array.
int rPos = 0;
for (int i = 0; i < pos; i++) {
XWPFRun currRun = runs.get(i);
if (!(currRun instanceof XWPFHyperlinkRun
|| currRun instanceof XWPFFieldRun)) {
rPos++;
}
}
if (pos == runs.size()) {
return createRun();
}
return insertNewProvidedRun(pos, newCursor -> {
String uri = CTR.type.getName().getNamespaceURI();
String localPart = "r";
// creates a new run, cursor is positioned inside the new
// element
newCursor.beginElement(localPart, uri);
// move the cursor to the START token to the run just created
newCursor.toParent();
CTR r = (CTR) newCursor.getObject();
return new XWPFRun(r, (IRunBody)this);
});
}

/**
* insert a new hyperlink Run in all runs
*
* @param pos The position at which the new run should be added.
* @param uri hyperlink uri
*
* @return the inserted run or null if the given pos is out of bounds.
*/
public XWPFHyperlinkRun insertNewHyperlinkRun(int pos, String uri) {
if (pos == runs.size()) {
return createHyperlinkRun(uri);
}
XWPFHyperlinkRun newRun = insertNewProvidedRun(pos, newCursor -> {
String namespaceURI = CTHyperlink.type.getName().getNamespaceURI();
String localPart = "hyperlink";
newCursor.beginElement(localPart, namespaceURI);
// move the cursor to the START token to the hyperlink just created
newCursor.toParent();
CTHyperlink ctHyperLink = (CTHyperlink) newCursor.getObject();
return new XWPFHyperlinkRun(ctHyperLink, ctHyperLink.addNewR(), this);
});

CTR ctRun = paragraph.insertNewR(rPos);
XWPFRun newRun = new XWPFRun(ctRun, (IRunBody) this);
String rId = getPart().getPackagePart().addExternalRelationship(
uri, XWPFRelation.HYPERLINK.getRelation()
).getId();
newRun.getCTHyperlink().setId(rId);
return newRun;
}

/**
* insert a new field Run in all runs
*
* @param pos The position at which the new run should be added.
*
* @return the inserted run or null if the given pos is out of bounds.
*/
public XWPFFieldRun insertNewFieldRun(int pos) {
if (pos == runs.size()) {
return createFieldRun();
}
return insertNewProvidedRun(pos, newCursor -> {
String uri = CTSimpleField.type.getName().getNamespaceURI();
String localPart = "fldSimple";
newCursor.beginElement(localPart, uri);
// move the cursor to the START token to the field just created
newCursor.toParent();
CTSimpleField ctSimpleField = (CTSimpleField) newCursor.getObject();
return new XWPFFieldRun(ctSimpleField, ctSimpleField.addNewR(), this);
});
}

// To update the iruns, find where we're going
// in the normal runs, and go in there
int iPos = iruns.size();
if (pos < runs.size()) {
XWPFRun oldAtPos = runs.get(pos);
int oldAt = iruns.indexOf(oldAtPos);
/**
* insert a new run provided by in all runs
*
* @param <T> XWPFRun or XWPFHyperlinkRun or XWPFFieldRun
* @param pos The position at which the new run should be added.
* @param provider provide a new run at position of the given cursor.
* @return the inserted run or null if the given pos is out of bounds.
*/
private <T extends XWPFRun> T insertNewProvidedRun(int pos, Function<XmlCursor, T> provider) {
if (pos >= 0 && pos < runs.size()) {
XWPFRun run = runs.get(pos);
CTR ctr = run.getCTR();
XmlCursor newCursor = ctr.newCursor();
if (!isCursorInParagraph(newCursor)) {
// look up correct position for CTP -> XXX -> R array
newCursor.toParent();
}
if (isCursorInParagraph(newCursor)) {
// provide a new run
T newRun = provider.apply(newCursor);

// To update the iruns, find where we're going
// in the normal runs, and go in there
int iPos = iruns.size();
int oldAt = iruns.indexOf(run);
if (oldAt != -1) {
iPos = oldAt;
}
iruns.add(iPos, newRun);
// Runs itself is easy to update
runs.add(pos, newRun);
return newRun;
}
iruns.add(iPos, newRun);

// Runs itself is easy to update
runs.add(pos, newRun);

return newRun;
newCursor.dispose();
}

return null;
}
// TODO Add methods to allow adding a HyperlinkRun or a FieldRun

/**
* verifies that cursor is on the right position
*
* @param cursor
* @return
*/
private boolean isCursorInParagraph(XmlCursor cursor) {
XmlCursor verify = cursor.newCursor();
verify.toParent();
boolean result = (verify.getObject() == this.paragraph);
verify.dispose();
return result;
}

/**
* this methods parse the paragraph and search for the string searched.
@@ -1643,31 +1738,67 @@ public class XWPFParagraph implements IBodyElement, IRunBody, ISDTContents, Para
*/
public boolean removeRun(int pos) {
if (pos >= 0 && pos < runs.size()) {
// Remove the run from our high level lists
XWPFRun run = runs.get(pos);
if (run instanceof XWPFHyperlinkRun ||
run instanceof XWPFFieldRun) {
// TODO Add support for removing these kinds of nested runs,
// which aren't on the CTP -> R array, but CTP -> XXX -> R array
throw new IllegalArgumentException("Removing Field or Hyperlink runs not yet supported");
// CTP -> CTHyperlink -> R array
if (run instanceof XWPFHyperlinkRun
&& isTheOnlyCTHyperlinkInRuns((XWPFHyperlinkRun) run)) {
XmlCursor c = ((XWPFHyperlinkRun) run).getCTHyperlink()
.newCursor();
c.removeXml();
c.dispose();
runs.remove(pos);
iruns.remove(run);
return true;
}
// CTP -> CTField -> R array
if (run instanceof XWPFFieldRun
&& isTheOnlyCTFieldInRuns((XWPFFieldRun) run)) {
XmlCursor c = ((XWPFFieldRun) run).getCTField().newCursor();
c.removeXml();
c.dispose();
runs.remove(pos);
iruns.remove(run);
return true;
}
XmlCursor c = run.getCTR().newCursor();
c.removeXml();
c.dispose();
runs.remove(pos);
iruns.remove(run);
// Remove the run from the low-level XML
//calculate the correct pos as our run/irun list contains hyperlinks and fields so is different to the paragraph R array.
int rPos = 0;
for(int i=0;i<pos;i++) {
XWPFRun currRun = runs.get(i);
if(!(currRun instanceof XWPFHyperlinkRun || currRun instanceof XWPFFieldRun)) {
rPos++;
}
}
getCTP().removeR(rPos);
return true;
}
return false;
}

/**
* Is there only one ctHyperlink in all runs
*
* @param run
* hyperlink run
* @return
*/
private boolean isTheOnlyCTHyperlinkInRuns(XWPFHyperlinkRun run) {
CTHyperlink ctHyperlink = run.getCTHyperlink();
long count = runs.stream().filter(r -> (r instanceof XWPFHyperlinkRun
&& ctHyperlink == ((XWPFHyperlinkRun) r).getCTHyperlink()))
.count();
return count <= 1;
}

/**
* Is there only one ctField in all runs
*
* @param run
* field run
* @return
*/
private boolean isTheOnlyCTFieldInRuns(XWPFFieldRun run) {
CTSimpleField ctField = run.getCTField();
long count = runs.stream().filter(r -> (r instanceof XWPFFieldRun
&& ctField == ((XWPFFieldRun) r).getCTField())).count();
return count <= 1;
}

/**
* returns the type of the BodyElement Paragraph
*

+ 132
- 0
src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFParagraph.java View File

@@ -327,6 +327,138 @@ public final class TestXWPFParagraph {
}
}

@Test
public void testCreateNewRuns() throws IOException {
try (XWPFDocument doc = new XWPFDocument()) {

XWPFParagraph p = doc.createParagraph();
XWPFHyperlinkRun h = p.createHyperlinkRun("http://poi.apache.org");
XWPFFieldRun fieldRun = p.createFieldRun();
XWPFRun r = p.createRun();

assertEquals(3, p.getRuns().size());
assertEquals(0, p.getRuns().indexOf(h));
assertEquals(1, p.getRuns().indexOf(fieldRun));
assertEquals(2, p.getRuns().indexOf(r));

assertEquals(3, p.getIRuns().size());
assertEquals(0, p.getIRuns().indexOf(h));
assertEquals(1, p.getIRuns().indexOf(fieldRun));
assertEquals(2, p.getIRuns().indexOf(r));
}
}

@Test
public void testInsertNewRuns() throws IOException {
try (XWPFDocument doc = new XWPFDocument()) {

XWPFParagraph p = doc.createParagraph();
XWPFRun r = p.createRun();
assertEquals(1, p.getRuns().size());
assertEquals(0, p.getRuns().indexOf(r));

XWPFHyperlinkRun h = p.insertNewHyperlinkRun(0, "http://poi.apache.org");
assertEquals(2, p.getRuns().size());
assertEquals(0, p.getRuns().indexOf(h));
assertEquals(1, p.getRuns().indexOf(r));

XWPFFieldRun fieldRun2 = p.insertNewFieldRun(2);
assertEquals(3, p.getRuns().size());
assertEquals(2, p.getRuns().indexOf(fieldRun2));
}
}

@Test
public void testRemoveRuns() throws IOException {
try (XWPFDocument doc = new XWPFDocument()) {

XWPFParagraph p = doc.createParagraph();
XWPFRun r = p.createRun();
p.createRun();
XWPFHyperlinkRun hyperlinkRun = p
.createHyperlinkRun("http://poi.apache.org");
XWPFFieldRun fieldRun = p.createFieldRun();

assertEquals(4, p.getRuns().size());
assertEquals(2, p.getRuns().indexOf(hyperlinkRun));
assertEquals(3, p.getRuns().indexOf(fieldRun));

p.removeRun(2);
assertEquals(3, p.getRuns().size());
assertEquals(-1, p.getRuns().indexOf(hyperlinkRun));
assertEquals(2, p.getRuns().indexOf(fieldRun));

p.removeRun(0);
assertEquals(2, p.getRuns().size());
assertEquals(-1, p.getRuns().indexOf(r));
assertEquals(1, p.getRuns().indexOf(fieldRun));

p.removeRun(1);
assertEquals(1, p.getRuns().size());
assertEquals(-1, p.getRuns().indexOf(fieldRun));
}
}

@Test
public void testRemoveAndInsertRunsWithOtherIRunElement()
throws IOException {
XWPFDocument doc = new XWPFDocument();

XWPFParagraph p = doc.createParagraph();
p.createRun();
// add other run element
p.getCTP().addNewSdt();
// add two CTR in hyperlink
XWPFHyperlinkRun hyperlinkRun = p
.createHyperlinkRun("http://poi.apache.org");
hyperlinkRun.getCTHyperlink().addNewR();
p.createFieldRun();

XWPFDocument doc2 = XWPFTestDataSamples.writeOutAndReadBack(doc);
XWPFParagraph paragraph = doc2.getParagraphArray(0);

assertEquals(4, paragraph.getRuns().size());
assertEquals(5, paragraph.getIRuns().size());

assertTrue(paragraph.getRuns().get(1) instanceof XWPFHyperlinkRun);
assertTrue(paragraph.getRuns().get(2) instanceof XWPFHyperlinkRun);
assertTrue(paragraph.getRuns().get(3) instanceof XWPFFieldRun);

assertTrue(paragraph.getIRuns().get(1) instanceof XWPFSDT);
assertTrue(paragraph.getIRuns().get(2) instanceof XWPFHyperlinkRun);

paragraph.removeRun(1);
assertEquals(3, paragraph.getRuns().size());
assertTrue(paragraph.getRuns().get(1) instanceof XWPFHyperlinkRun);
assertTrue(paragraph.getRuns().get(2) instanceof XWPFFieldRun);

assertTrue(paragraph.getIRuns().get(1) instanceof XWPFSDT);
assertTrue(paragraph.getIRuns().get(2) instanceof XWPFHyperlinkRun);

paragraph.removeRun(1);
assertEquals(2, paragraph.getRuns().size());
assertTrue(paragraph.getRuns().get(1) instanceof XWPFFieldRun);

assertTrue(paragraph.getIRuns().get(1) instanceof XWPFSDT);
assertTrue(paragraph.getIRuns().get(2) instanceof XWPFFieldRun);

paragraph.removeRun(0);
assertEquals(1, paragraph.getRuns().size());
assertTrue(paragraph.getRuns().get(0) instanceof XWPFFieldRun);

assertTrue(paragraph.getIRuns().get(0) instanceof XWPFSDT);
assertTrue(paragraph.getIRuns().get(1) instanceof XWPFFieldRun);

XWPFRun newRun = paragraph.insertNewRun(0);
assertEquals(2, paragraph.getRuns().size());

assertEquals(3, paragraph.getIRuns().size());
assertEquals(0, paragraph.getRuns().indexOf(newRun));

doc.close();
doc2.close();
}

@Test
public void testPictures() throws IOException {
try (XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("VariousPictures.docx")) {

Loading…
Cancel
Save