argos/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java

1269 lines
70 KiB
Java

package eu.eudat.logic.utilities.documents.word;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.eudat.data.entities.DMP;
import eu.eudat.data.entities.Dataset;
import eu.eudat.data.entities.Organisation;
import eu.eudat.data.entities.Researcher;
import eu.eudat.logic.proxy.config.configloaders.ConfigLoader;
import eu.eudat.logic.services.forms.VisibilityRuleService;
import eu.eudat.logic.utilities.documents.types.ParagraphStyle;
import eu.eudat.logic.utilities.interfaces.ApplierWithValue;
import eu.eudat.logic.utilities.json.JavaToJson;
import eu.eudat.models.data.components.commons.datafield.*;
import eu.eudat.models.data.pid.PidLink;
import eu.eudat.models.data.pid.PidLinks;
import eu.eudat.models.data.user.components.datasetprofile.Field;
import eu.eudat.models.data.user.components.datasetprofile.FieldSet;
import eu.eudat.models.data.user.components.datasetprofile.Section;
import eu.eudat.models.data.user.composite.DatasetProfilePage;
import eu.eudat.models.data.user.composite.PagedDatasetProfile;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.json.JSONArray;
import org.json.JSONException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.NodeTraversor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.poi.xwpf.usermodel.Document.*;
public class WordBuilder {
private static final Logger logger = LoggerFactory.getLogger(WordBuilder.class);
private static final Map<String, Integer> IMAGE_TYPE_MAP = Stream.of(new Object[][] {
{"image/jpeg", PICTURE_TYPE_JPEG},
{"image/png", PICTURE_TYPE_PNG},
{"image/gif", PICTURE_TYPE_GIF},
{"image/tiff", PICTURE_TYPE_TIFF},
{"image/bmp", PICTURE_TYPE_BMP},
{"image/wmf", PICTURE_TYPE_WMF}
}
).collect(Collectors.toMap(objects -> (String)objects[0], o -> (Integer)o[1]));
private Map<ParagraphStyle, ApplierWithValue<XWPFDocument, Object, XWPFParagraph>> options = new HashMap<>();
private Map<ParagraphStyle, ApplierWithValue<XWPFTableCell, Object, XWPFParagraph>> optionsInTable = new HashMap<>();
private CTAbstractNum cTAbstractNum;
private BigInteger numId;
private Integer indent;
private final ObjectMapper mapper;
private Integer imageCount;
private ConfigLoader configLoader;
public WordBuilder(Environment environment, ConfigLoader configLoader) {
this.cTAbstractNum = CTAbstractNum.Factory.newInstance();
this.cTAbstractNum.setAbstractNumId(BigInteger.valueOf(1));
this.indent = 0;
this.imageCount = 0;
this.mapper = new ObjectMapper();
this.buildOptions(environment);
this.buildOptionsInTable(environment);
this.configLoader = configLoader;
}
private void buildOptionsInTable(Environment environment) {
this.optionsInTable.put(ParagraphStyle.TEXT, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.addParagraph();
XWPFRun run = paragraph.createRun();
if (item != null)
run.setText("" + item);
run.setFontSize(11);
return paragraph;
});
this.optionsInTable.put(ParagraphStyle.HTML, (mainDocumentPart, item) -> {
Document htmlDoc = Jsoup.parse(((String)item).replaceAll("\n", "<br>"));
HtmlToWorldBuilder htmlToWorldBuilder = HtmlToWorldBuilder.convertInTable(mainDocumentPart, htmlDoc, 0);
return htmlToWorldBuilder.getParagraph();
});
this.optionsInTable.put(ParagraphStyle.TITLE, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.addParagraph();
paragraph.setStyle("Title");
paragraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = paragraph.createRun();
run.setText((String)item);
run.setBold(true);
run.setFontSize(14);
return paragraph;
});
this.optionsInTable.put(ParagraphStyle.IMAGE, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.addParagraph();
XWPFRun run = paragraph.createRun();
if (item != null)
run.setText(((Map<String, String>)item).get("name"));
run.setFontSize(11);
run.setItalic(true);
return paragraph;
});
}
private void buildOptions(Environment environment) {
this.options.put(ParagraphStyle.TEXT, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
XWPFRun run = paragraph.createRun();
if (item != null)
run.setText("" + item);
run.setFontSize(11);
return paragraph;
});
this.options.put(ParagraphStyle.HTML, (mainDocumentPart, item) -> {
Document htmlDoc = Jsoup.parse(((String)item).replaceAll("\n", "<br>"));
HtmlToWorldBuilder htmlToWorldBuilder = HtmlToWorldBuilder.convert(mainDocumentPart, htmlDoc, this.indent);
return htmlToWorldBuilder.getParagraph();
});
this.options.put(ParagraphStyle.TITLE, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setStyle("Title");
paragraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = paragraph.createRun();
run.setText((String)item);
run.setBold(true);
run.setFontSize(14);
return paragraph;
});
this.options.put(ParagraphStyle.HEADER1, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setStyle("Heading1");
XWPFRun run = paragraph.createRun();
run.setText((String)item);
// run.setBold(true);
// run.setFontSize(12);
// run.setStyle("0");
return paragraph;
});
this.options.put(ParagraphStyle.HEADER2, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setStyle("Heading2");
XWPFRun run = paragraph.createRun();
run.setText("" + item);
// run.setBold(true);
// run.setFontSize(12);
return paragraph;
});
this.options.put(ParagraphStyle.HEADER3, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setStyle("Heading3");
XWPFRun run = paragraph.createRun();
run.setText("" + item);
// run.setBold(true);
// run.setFontSize(11);
return paragraph;
});
this.options.put(ParagraphStyle.HEADER4, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setStyle("Heading4");
XWPFRun run = paragraph.createRun();
run.setText((String)item);
return paragraph;
});
this.options.put(ParagraphStyle.HEADER5, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setStyle("Heading5");
XWPFRun run = paragraph.createRun();
run.setText("" + item);
return paragraph;
});
this.options.put(ParagraphStyle.HEADER6, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setStyle("Heading6");
XWPFRun run = paragraph.createRun();
run.setText("" + item);
return paragraph;
});
this.options.put(ParagraphStyle.FOOTER, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText((String)item);
return paragraph;
});
this.options.put(ParagraphStyle.COMMENT, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("" + item);
run.setItalic(true);
return paragraph;
});
this.options.put(ParagraphStyle.IMAGE, (mainDocumentPart, item) -> {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setPageBreak(true);
paragraph.setSpacingAfter(0);
paragraph.setAlignment(ParagraphAlignment.CENTER); //GK: Center the image if it is too small
XWPFRun run = paragraph.createRun();
String imageId = ((Map<String, String>)item).get("id");
String fileName = ((Map<String, String>)item).get("name");
String fileType = ((Map<String, String>)item).get("type");
int format;
format = IMAGE_TYPE_MAP.getOrDefault(fileType, 0);
try {
FileInputStream image = new FileInputStream(environment.getProperty("file.storage") + imageId);
ImageInputStream iis = ImageIO.createImageInputStream(new File(environment.getProperty("file.storage") + imageId));
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
if (readers.hasNext()) {
ImageReader reader = readers.next();
reader.setInput(iis);
int initialImageWidth = reader.getWidth(0);
int initialImageHeight = reader.getHeight(0);
float ratio = initialImageHeight / (float)initialImageWidth;
int marginLeftInDXA = mainDocumentPart.getDocument().getBody().getSectPr().getPgMar().getLeft().intValue();
int marginRightInDXA = mainDocumentPart.getDocument().getBody().getSectPr().getPgMar().getRight().intValue();
int pageWidthInDXA = mainDocumentPart.getDocument().getBody().getSectPr().getPgSz().getW().intValue();
int pageWidth = Math.round((pageWidthInDXA - marginLeftInDXA - marginRightInDXA) / (float)20); // /20 converts dxa to points
int imageWidth = Math.round(initialImageWidth*(float)0.75); // *0.75 converts pixels to points
int width = Math.min(imageWidth, pageWidth);
int marginTopInDXA = mainDocumentPart.getDocument().getBody().getSectPr().getPgMar().getTop().intValue();
int marginBottomInDXA = mainDocumentPart.getDocument().getBody().getSectPr().getPgMar().getBottom().intValue();
int pageHeightInDXA = mainDocumentPart.getDocument().getBody().getSectPr().getPgSz().getH().intValue();
int pageHeight = Math.round((pageHeightInDXA - marginTopInDXA - marginBottomInDXA) / (float)20); // /20 converts dxa to points
int imageHeight = Math.round(initialImageHeight * ((float)0.75)); // *0.75 converts pixels to points
int height = Math.round(width*ratio);
if(height > pageHeight) {
// height calculated with ratio is too large. Image may have Portrait (vertical) orientation. Recalculate image dimensions.
height = Math.min(imageHeight, pageHeight);
width = Math.round(height/ratio);
}
XWPFPicture picture = run.addPicture(image, format, fileName, Units.toEMU(width), Units.toEMU(height));
paragraph.setPageBreak(false);
imageCount++;
XWPFParagraph captionParagraph = mainDocumentPart.createParagraph();
captionParagraph.setAlignment(ParagraphAlignment.CENTER);
captionParagraph.setSpacingBefore(0);
captionParagraph.setStyle("Caption");
XWPFRun captionRun = captionParagraph.createRun();
captionRun.setText("Image " + imageCount);
}
} catch (IOException | InvalidFormatException e){
logger.error(e.getMessage(), e);
}
return paragraph;
});
}
public XWPFDocument build(XWPFDocument document, PagedDatasetProfile pagedDatasetProfile, VisibilityRuleService visibilityRuleService) throws IOException {
// createPages(pagedDatasetProfile.getPages(), document, true, visibilityRuleService);
// XWPFNumbering numbering = document.createNumbering();
// BigInteger tempNumId = BigInteger.ONE;
// boolean found = false;
// while (!found) {
// Object o = numbering.getAbstractNum(tempNumId);
// found = (o == null);
// if (!found) tempNumId = tempNumId.add(BigInteger.ONE);
// }
// cTAbstractNum.setAbstractNumId(tempNumId);
// XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum);
// BigInteger abstractNumID = numbering.addAbstractNum(abstractNum);
// this.numId = numbering.addNum(abstractNumID);
createPages(pagedDatasetProfile.getPages(), document, false, visibilityRuleService);
return document;
}
private void createPages(List<DatasetProfilePage> datasetProfilePages, XWPFDocument mainDocumentPart, Boolean createListing, VisibilityRuleService visibilityRuleService) {
datasetProfilePages.forEach(item -> {
try {
createSections(item.getSections(), mainDocumentPart, ParagraphStyle.HEADER5, 0, createListing, visibilityRuleService, item.getOrdinal() + 1, null);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
});
}
private void createSections(List<Section> sections, XWPFDocument mainDocumentPart, ParagraphStyle style, Integer indent, Boolean createListing, VisibilityRuleService visibilityRuleService, Integer page, String sectionString) {
if (createListing) this.addListing(mainDocumentPart, indent, false, true);
boolean hasValue = false;
for (Section section: sections) {
int paragraphPos = -1;
String tempSectionString = sectionString != null ? sectionString + "." + (section.getOrdinal() + 1) : "" + (section.getOrdinal() + 1);
if (visibilityRuleService.isElementVisible(section.getId())) {
if (!createListing) {
XWPFParagraph paragraph = addParagraphContent(page + "." + tempSectionString + " " + section.getTitle(), mainDocumentPart, style, numId, indent);
// CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl();
// number.setVal(BigInteger.valueOf(indent));
paragraphPos = mainDocumentPart.getPosOfParagraph(paragraph);
}
createSections(section.getSections(), mainDocumentPart, ParagraphStyle.HEADER5, indent+1, createListing, visibilityRuleService, page, tempSectionString);
hasValue = createCompositeFields(section.getCompositeFields(), mainDocumentPart, indent+1, createListing, visibilityRuleService, page, tempSectionString);
if (!hasValue && paragraphPos > -1) {
mainDocumentPart.removeBodyElement(paragraphPos);
}
}
}
}
private Boolean createCompositeFields(List<FieldSet> compositeFields, XWPFDocument mainDocumentPart, Integer indent, Boolean createListing, VisibilityRuleService visibilityRuleService, Integer page, String section) {
if (createListing) this.addListing(mainDocumentPart, indent, true, true);
boolean hasValue = false;
boolean returnedValue = false;
for (FieldSet compositeField: compositeFields) {
if (visibilityRuleService.isElementVisible(compositeField.getId()) && hasVisibleFields(compositeField, visibilityRuleService)) {
char c = 'a';
int multiplicityItems = 0;
boolean hasMultiplicityItems = false;
int paragraphPos = -1;
int paragraphPosInner = -1;
if (compositeField.getTitle() != null && !compositeField.getTitle().isEmpty() && !createListing) {
XWPFParagraph paragraph = addParagraphContent(page + "." + section + "." + (compositeField.getOrdinal() +1) + " " + compositeField.getTitle(), mainDocumentPart, ParagraphStyle.HEADER6, numId, indent);
// CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl();
// number.setVal(BigInteger.valueOf(indent));
paragraphPos = mainDocumentPart.getPosOfParagraph(paragraph);
if(!compositeField.getMultiplicity().getTableView() && compositeField.getMultiplicityItems() != null && !compositeField.getMultiplicityItems().isEmpty()){
XWPFParagraph paragraphInner = addParagraphContent(c + ". ", mainDocumentPart, ParagraphStyle.TEXT, numId, indent);
paragraphPosInner = mainDocumentPart.getPosOfParagraph(paragraphInner);
hasMultiplicityItems = true;
multiplicityItems++;
}
}
XWPFTable tbl = null;
XWPFTableRow row = null;
int numOfRows = 0;
if(compositeField.getMultiplicity().getTableView()) {
tbl = mainDocumentPart.createTable();
tbl.setTableAlignment(TableRowAlign.CENTER);
mainDocumentPart.createParagraph();
createHeadersInTable(compositeField.getFields(), tbl, visibilityRuleService);
numOfRows = tbl.getRows().size();
row = tbl.createRow();
}
if(compositeField.getMultiplicity().getTableView()) {
hasValue = createFieldsInTable(compositeField.getFields(), row, indent, createListing, visibilityRuleService, hasMultiplicityItems, numOfRows);
numOfRows++;
} else {
hasValue = createFields(compositeField.getFields(), mainDocumentPart, indent, createListing, visibilityRuleService, hasMultiplicityItems);
}
if(hasValue){
returnedValue = true;
} else if(paragraphPosInner > -1){
mainDocumentPart.removeBodyElement(paragraphPosInner);
c--;
multiplicityItems--;
}
if (compositeField.getMultiplicityItems() != null && !compositeField.getMultiplicityItems().isEmpty()) {
List<FieldSet> list = compositeField.getMultiplicityItems().stream().sorted(Comparator.comparingInt(FieldSet::getOrdinal)).collect(Collectors.toList());
for (FieldSet multiplicityFieldset : list) {
paragraphPosInner = -1;
if(!compositeField.getMultiplicity().getTableView() && !createListing){
c++;
// addParagraphContent(c + ". ", mainDocumentPart, ParagraphStyle.HEADER6, numId);
XWPFParagraph paragraphInner = addParagraphContent(c + ". ", mainDocumentPart, ParagraphStyle.TEXT, numId, indent);
paragraphPosInner = mainDocumentPart.getPosOfParagraph(paragraphInner);
hasMultiplicityItems = true;
multiplicityItems++;
}
// hasValue = createFields(multiplicityFieldset.getFields(), mainDocumentPart, 3, createListing, visibilityRuleService, hasMultiplicityItems);
boolean hasValueInner = false;
if(compositeField.getMultiplicity().getTableView()) {
row = tbl.createRow();
hasValueInner = createFieldsInTable(multiplicityFieldset.getFields(), row, indent, createListing, visibilityRuleService, hasMultiplicityItems, numOfRows);
numOfRows++;
} else {
hasValueInner = createFields(multiplicityFieldset.getFields(), mainDocumentPart, indent, createListing, visibilityRuleService, hasMultiplicityItems);
}
// if(hasValue){
if(hasValueInner){
hasValue = true;
returnedValue = true;
} else if(paragraphPosInner > -1){
mainDocumentPart.removeBodyElement(paragraphPosInner);
c--;
multiplicityItems--;
}
}
if(multiplicityItems == 1) {
String text = mainDocumentPart.getLastParagraph().getRuns().get(0).getText(0);
if(text.equals("a. ")) {
mainDocumentPart.getLastParagraph().removeRun(0);
}
}
}
if (hasValue && compositeField.getHasCommentField() && compositeField.getCommentFieldValue() != null && !compositeField.getCommentFieldValue().isEmpty() && !createListing) {
XWPFParagraph paragraph = addParagraphContent("<i>Comment:</i>\n"+compositeField.getCommentFieldValue(), mainDocumentPart, ParagraphStyle.HTML, numId, indent);
// CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl();
// number.setVal(BigInteger.valueOf(indent));
}
if (!hasValue && paragraphPos > -1) {
mainDocumentPart.removeBodyElement(paragraphPos);
}
}
}
return returnedValue;
}
private void createHeadersInTable(List<Field> fields, XWPFTable table, VisibilityRuleService visibilityRuleService) {
boolean atLeastOneHeader = false;
List<Field> tempFields = fields.stream().sorted(Comparator.comparingInt(Field::getOrdinal)).collect(Collectors.toList());
int index = 0;
XWPFTableRow row = table.getRow(0);
for (Field field: tempFields) {
if (visibilityRuleService.isElementVisible(field.getId()) && field.getExport()) {
XWPFTableCell cell;
if(index == 0) {
cell = row.getCell(0);
} else {
cell = row.createCell();
}
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.valueOf("CENTER"));
String label = ((FieldData) field.getData()).getLabel();
if(label != null && label != "") {
XWPFParagraph paragraph = cell.getParagraphs().get(0);
paragraph.setIndentationFirstLine(50);
XWPFRun run = paragraph.createRun();
run.setText(label);
run.setBold(true);
run.setFontSize(12);
paragraph.setAlignment(ParagraphAlignment.CENTER);
paragraph.setSpacingBefore(100);
atLeastOneHeader = true;
}
}
index++;
}
if(!atLeastOneHeader) {
table.removeRow(0);
}
}
private Boolean createFieldsInTable(List<Field> fields, XWPFTableRow mainDocumentPart, Integer indent, Boolean createListing, VisibilityRuleService visibilityRuleService, boolean hasMultiplicityItems, int numOfRows) {
int numOfCells = 0;
boolean hasValue = false;
List<Field> tempFields = fields.stream().sorted(Comparator.comparingInt(Field::getOrdinal)).collect(Collectors.toList());
for (Field field: tempFields) {
if (visibilityRuleService.isElementVisible(field.getId()) && field.getExport()) {
if (!createListing) {
try {
if(field.getViewStyle().getRenderStyle().equals("upload")){
boolean isImage = false;
for(UploadData.Option type: ((UploadData)field.getData()).getTypes()){
String fileFormat = type.getValue();
if(IMAGE_TYPE_MAP.containsKey(fileFormat)){
isImage = true;
break;
}
}
if(isImage){
if (field.getValue() != null && !field.getValue().toString().isEmpty()) {
XWPFParagraph paragraph = addCellContent(mapper.convertValue(field.getValue(), Map.class), mainDocumentPart, ParagraphStyle.IMAGE, numId, 0, numOfRows, numOfCells, 0);
if (paragraph != null) {
hasValue = true;
}
if(hasMultiplicityItems){
hasMultiplicityItems = false;
}
}
}
}
else if (field.getValue() != null && !field.getValue().toString().isEmpty()) {
this.indent = indent;
String format = this.formatter(field);
if (field.getViewStyle().getRenderStyle().equals("tags")) {
format = getCommaSeparatedFormatsFromJson(format, "name");
} else if (field.getViewStyle().getRenderStyle().equals("combobox") && field.getData() instanceof AutoCompleteData) {
format = getCommaSeparatedFormatsFromJson(format, "label");
}
boolean isResearcher = field.getViewStyle().getRenderStyle().equals("researchers");
if(format != null && !format.isEmpty()){
Object hasMultiAutoComplete = mapper.convertValue(field.getData(), Map.class).get("multiAutoComplete");
boolean isMultiAutoComplete = hasMultiAutoComplete != null && (boolean)hasMultiAutoComplete;
boolean arrayStringFormat = format.charAt(0) == '[';
if(arrayStringFormat || isMultiAutoComplete){
List<String> values = (arrayStringFormat) ? Arrays.asList(format.substring(1, format.length() - 1).split(",[ ]*")) : Arrays.asList(format.split(",[ ]*"));
if(values.size() > 1) {
boolean orcidResearcher;
int numOfValuesInCell = 0;
for (String val : values) {
orcidResearcher = false;
String orcId = null;
if(isResearcher && val.contains("orcid:")){
orcId = val.substring(val.indexOf(':') + 1, val.indexOf(')'));
val = val.substring(0, val.indexOf(':') + 1) + " ";
orcidResearcher = true;
}
format = "" + val;
if(hasMultiplicityItems){
XWPFParagraph paragraph = mainDocumentPart.getCell(mainDocumentPart.getTableCells().size()).addParagraph();
paragraph.createRun().setText(format);
if(orcidResearcher){
XWPFHyperlinkRun run = paragraph.createHyperlinkRun("https://orcid.org/" + orcId);
run.setText(orcId);
run.setUnderline(UnderlinePatterns.SINGLE);
run.setColor("0000FF");
paragraph.createRun().setText(")");
}
hasMultiplicityItems = false;
}
else{
XWPFParagraph paragraph = addCellContent(format, mainDocumentPart, field.getViewStyle().getRenderStyle().equals("richTextarea") ? ParagraphStyle.HTML : ParagraphStyle.TEXT, numId, indent, numOfRows, numOfCells, numOfValuesInCell);
numOfValuesInCell++;
if(orcidResearcher){
XWPFHyperlinkRun run = paragraph.createHyperlinkRun("https://orcid.org/" + orcId);
run.setText(orcId);
run.setUnderline(UnderlinePatterns.SINGLE);
run.setColor("0000FF");
paragraph.createRun().setText(")");
}
if (paragraph != null) {
hasValue = true;
}
}
format = null;
}
}
else if(values.size() == 1){
format = values.get(0);
}
}
}
if(hasMultiplicityItems && format != null){
XWPFParagraph paragraph = mainDocumentPart.getCell(mainDocumentPart.getTableCells().size()).addParagraph();
paragraph.createRun().setText(format);
hasMultiplicityItems = false;
hasValue = true;
}
else{
XWPFParagraph paragraph = addCellContent(format, mainDocumentPart, field.getViewStyle().getRenderStyle().equals("richTextarea") ? ParagraphStyle.HTML : ParagraphStyle.TEXT, numId, indent, numOfRows, numOfCells, 0);
if (paragraph != null) {
hasValue = true;
}
}
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
numOfCells++;
}
}
return hasValue;
}
private void createHypeLink(XWPFDocument mainDocumentPart, String format, String pidType, String pid, boolean hasMultiplicityItems, boolean isMultiAutoComplete){
PidLink pidLink = this.configLoader.getPidLinks().getPidLinks().stream().filter(pl -> pl.getPid().equals(pidType)).findFirst().orElse(null);
if (pidLink != null) {
if (!hasMultiplicityItems) {
XWPFParagraph paragraph = mainDocumentPart.createParagraph();
paragraph.setIndentFromLeft(400 * indent);
if (numId != null) {
paragraph.setNumID(numId);
}
}
if (isMultiAutoComplete) {
XWPFRun r = mainDocumentPart.getLastParagraph().createRun();
r.setText("");
}
XWPFHyperlinkRun run = mainDocumentPart.getLastParagraph().createHyperlinkRun(pidLink.getLink().replace("{pid}", pid));
run.setText(format);
run.setUnderline(UnderlinePatterns.SINGLE);
run.setColor("0000FF");
run.setFontSize(11);
}
else {
String newFormat = (isMultiAutoComplete) ? "" + format : format;
if (hasMultiplicityItems) {
mainDocumentPart.getLastParagraph().createRun().setText(newFormat);
}
else {
addParagraphContent(newFormat, mainDocumentPart, ParagraphStyle.TEXT, numId, indent);
}
}
}
private Boolean createFields(List<Field> fields, XWPFDocument mainDocumentPart, Integer indent, Boolean createListing, VisibilityRuleService visibilityRuleService, boolean hasMultiplicityItems) {
if (createListing) this.addListing(mainDocumentPart, indent, false, false);
boolean hasValue = false;
List<Field> tempFields = fields.stream().sorted(Comparator.comparingInt(Field::getOrdinal)).collect(Collectors.toList());
for (Field field: tempFields) {
if (visibilityRuleService.isElementVisible(field.getId()) && field.getExport()) {
if (!createListing) {
try {
if(field.getViewStyle().getRenderStyle().equals("upload")){
boolean isImage = false;
for(UploadData.Option type: ((UploadData)field.getData()).getTypes()){
String fileFormat = type.getValue();
if(IMAGE_TYPE_MAP.containsKey(fileFormat)){
isImage = true;
break;
}
}
if(isImage){
if (field.getValue() != null && !field.getValue().toString().isEmpty()) {
XWPFParagraph paragraph = addParagraphContent(mapper.convertValue(field.getValue(), Map.class), mainDocumentPart, ParagraphStyle.IMAGE, numId, 0);
if (paragraph != null) {
// CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl();
// number.setVal(BigInteger.valueOf(indent));
hasValue = true;
}
if(hasMultiplicityItems){
hasMultiplicityItems = false;
}
}
}
}
else if (field.getValue() != null && !field.getValue().toString().isEmpty()) {
this.indent = indent;
String format = this.formatter(field);
if (field.getViewStyle().getRenderStyle().equals("tags")) {
format = getCommaSeparatedFormatsFromJson(format, "name");
} else if (field.getViewStyle().getRenderStyle().equals("combobox") && field.getData() instanceof AutoCompleteData) {
format = getCommaSeparatedFormatsFromJson(format, "label");
}
switch (field.getViewStyle().getRenderStyle()) {
case "organizations":
case "externalDatasets":
case "publications":
if(format != null && !format.isEmpty()){
Object hasMultiAutoComplete = mapper.convertValue(field.getData(), Map.class).get("multiAutoComplete");
boolean isMultiAutoComplete = hasMultiAutoComplete != null && (boolean)hasMultiAutoComplete;
if(!isMultiAutoComplete){
Map<String, String> value = mapper.readValue((String)field.getValue(), Map.class);
if(hasMultiplicityItems){
createHypeLink(mainDocumentPart, format, value.get("pidTypeField"), value.get("pid"), true, false);
hasMultiplicityItems = false;
}
else{
createHypeLink(mainDocumentPart, format, value.get("pidTypeField"), value.get("pid"), false, false);
}
}
else{
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
List<Map<String, Object>> values = new ArrayList<>();
try {
values = Arrays.asList(mapper.readValue(field.getValue().toString(), HashMap[].class));
}
catch (Exception e) {
Map <String, Object> map = new HashMap<>();
map.put("label", field.getValue());
values.add(map);
}
if (values.size() > 1) {
for (Map<String, Object> value : values) {
if(hasMultiplicityItems){
createHypeLink(mainDocumentPart, (String) value.get("name"), (String) value.get("pidTypeField"), (String) value.get("pid"), true, true);
hasMultiplicityItems = false;
}
else{
createHypeLink(mainDocumentPart, (String) value.get("name"), (String) value.get("pidTypeField"), (String) value.get("pid"), false, true);
}
}
}
else if(values.size() == 1){
if(hasMultiplicityItems){
createHypeLink(mainDocumentPart, format, (String) values.get(0).get("pidTypeField"), (String) values.get(0).get("pid"), true, false);
hasMultiplicityItems = false;
}
else{
createHypeLink(mainDocumentPart, format, (String) values.get(0).get("pidTypeField"), (String) values.get(0).get("pid"), false, false);
}
}
}
hasValue = true;
}
break;
default:
boolean isResearcher = field.getViewStyle().getRenderStyle().equals("researchers");
if(format != null && !format.isEmpty()){
Object hasMultiAutoComplete = mapper.convertValue(field.getData(), Map.class).get("multiAutoComplete");
boolean isMultiAutoComplete = hasMultiAutoComplete != null && (boolean)hasMultiAutoComplete;
boolean arrayStringFormat = format.charAt(0) == '[';
if(arrayStringFormat || isMultiAutoComplete){
List<String> values = (arrayStringFormat) ? Arrays.asList(format.substring(1, format.length() - 1).split(",[ ]*")) : Arrays.asList(format.split(",[ ]*"));
if(values.size() > 1) {
boolean orcidResearcher;
for (String val : values) {
orcidResearcher = false;
String orcId = null;
if(isResearcher && val.contains("orcid:")){
orcId = val.substring(val.indexOf(':') + 1, val.indexOf(')'));
val = val.substring(0, val.indexOf(':') + 1) + " ";
orcidResearcher = true;
}
format = "" + val;
if(hasMultiplicityItems){
mainDocumentPart.getLastParagraph().createRun().setText(format);
if(orcidResearcher){
XWPFHyperlinkRun run = mainDocumentPart.getLastParagraph().createHyperlinkRun("https://orcid.org/" + orcId);
run.setText(orcId);
run.setUnderline(UnderlinePatterns.SINGLE);
run.setColor("0000FF");
mainDocumentPart.getLastParagraph().createRun().setText(")");
}
hasMultiplicityItems = false;
}
else{
XWPFParagraph paragraph = addParagraphContent(format, mainDocumentPart, field.getViewStyle().getRenderStyle().equals("richTextarea") ? ParagraphStyle.HTML : ParagraphStyle.TEXT, numId, indent);
if(orcidResearcher){
XWPFHyperlinkRun run = paragraph.createHyperlinkRun("https://orcid.org/" + orcId);
run.setText(orcId);
run.setUnderline(UnderlinePatterns.SINGLE);
run.setColor("0000FF");
paragraph.createRun().setText(")");
}
if (paragraph != null) {
// CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl();
// number.setVal(BigInteger.valueOf(indent));
hasValue = true;
}
}
format = null;
}
}
else if(values.size() == 1){
format = values.get(0);
}
}
}
if (format != null) {
if (hasMultiplicityItems) {
mainDocumentPart.getLastParagraph().createRun().setText(format);
hasMultiplicityItems = false;
hasValue = true;
}
else {
XWPFParagraph paragraph = addParagraphContent(format, mainDocumentPart, field.getViewStyle().getRenderStyle().equals("richTextarea") ? ParagraphStyle.HTML : ParagraphStyle.TEXT, numId, indent);
if (paragraph != null) {
// CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl();
// number.setVal(BigInteger.valueOf(indent));
hasValue = true;
}
}
}
}
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
}
return hasValue;
}
private String getCommaSeparatedFormatsFromJson(String format, String attribute){
if((format == null || format.isEmpty()) || (attribute == null || attribute.isEmpty())){
return null;
}
try {
JSONArray array = new JSONArray(JavaToJson.objectStringToJson(format));
StringBuilder multipleFormats = new StringBuilder();
for (int i = 0; i < array.length(); i++) {
multipleFormats.append(array.getJSONObject(i).getString(attribute)).append(", ");
}
if (multipleFormats.length() > 0) {
multipleFormats.setLength(multipleFormats.length() - 2);
}
return multipleFormats.toString();
} catch (JSONException e) {
return format;
}
}
public XWPFParagraph addCellContent(Object content, XWPFTableRow mainDocumentPart, ParagraphStyle style, BigInteger numId, int indent, int numOfRows, int numOfCells, int numOfValuesInCell) {
if (content != null) {
if (content instanceof String && ((String)content).isEmpty()) {
return null;
}
this.indent = indent;
XWPFTableCell cell;
if(numOfRows > 0 || numOfValuesInCell > 0) {
cell = mainDocumentPart.getCell(numOfCells);
} else {
cell = mainDocumentPart.createCell();
}
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.valueOf("CENTER"));
if(numOfValuesInCell == 0) {
cell.removeParagraph(0);
}
XWPFParagraph paragraph = this.optionsInTable.get(style).apply(cell, content);
if (paragraph != null) {
paragraph.setAlignment(ParagraphAlignment.CENTER);
paragraph.setSpacingBefore(100);
if (numId != null) {
paragraph.setNumID(numId);
}
return paragraph;
}
}
return null;
}
public XWPFParagraph addParagraphContent(Object content, XWPFDocument mainDocumentPart, ParagraphStyle style, BigInteger numId, int indent) {
// this.indent = 0;
if (content != null) {
if (content instanceof String && ((String)content).isEmpty()) {
return null;
}
this.indent = indent;
XWPFParagraph paragraph = this.options.get(style).apply(mainDocumentPart, content);
if (paragraph != null) {
paragraph.setIndentFromLeft(400*indent);
if (numId != null) {
paragraph.setNumID(numId);
}
return paragraph;
}
}
return null;
}
private void addListing(XWPFDocument document, int indent, Boolean question, Boolean hasIndication) {
CTLvl cTLvl = this.cTAbstractNum.addNewLvl();
String textLevel = "";
for (int i = 0; i <= indent; i++) {
textLevel += "%" + (i + 1) + ".";
}
if (question) {
cTLvl.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
// cTLvl.addNewLvlText().setVal("");
cTLvl.setIlvl(BigInteger.valueOf(indent));
} else if (!question && hasIndication) {
cTLvl.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
// cTLvl.addNewLvlText().setVal("");
cTLvl.setIlvl(BigInteger.valueOf(indent));
}
if (!question && !hasIndication) {
cTLvl.addNewNumFmt().setVal(STNumberFormat.NONE);
// cTLvl.addNewLvlText().setVal("");
cTLvl.setIlvl(BigInteger.valueOf(indent));
}
}
private String formatter(Field field) throws IOException {
String comboboxType = null;
if (field.getValue() == null) {
return null;
}
switch (field.getViewStyle().getRenderStyle()) {
case "researchers":
case "projects":
case "organizations":
case "externalDatasets":
case "dataRepositories":
case "pubRepositories":
case "journalRepositories":
case "taxonomies":
case "licenses":
case "publications":
case "registries":
case "services":
case "tags":
case "currency":
comboboxType = "autocomplete";
case "combobox": {
if (comboboxType == null) {
comboboxType = ((ComboBoxData) field.getData()).getType();
}
if (comboboxType.equals("autocomplete")) {
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
if (field.getValue() == null) return null;
List<Map<String, Object>> mapList = new ArrayList<>();
if (!field.getValue().equals("") && field.getValue().toString() != null) {
try {
mapList = Arrays.asList(mapper.readValue(field.getValue().toString(), HashMap[].class));
}catch (Exception e) {
// logger.warn(e.getMessage(), e);
// logger.info("Moving to fallback parsing");
Map <String, Object> map = new HashMap<>();
map.put("label", field.getValue().toString());
mapList.add(map);
}
}
StringBuilder sb = new StringBuilder();
int index = 0;
for (Map<String, Object> map: mapList) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() != null && (entry.getKey().equals("label") || entry.getKey().equals("description") || entry.getKey().equals("name"))) {
sb.append(entry.getValue());
break;
}
}
if (index != mapList.size() - 1) sb.append(", ");
index++;
}
return sb.toString();
} else if (comboboxType.equals("wordlist")) {
WordListData wordListData = (WordListData) field.getData();
if (field.getValue() != null){
ComboBoxData.Option selectedOption = null;
if (!wordListData.getOptions().isEmpty()) {
for (ComboBoxData.Option option : wordListData.getOptions()) {
if (option.getValue().equals(field.getValue())) {
selectedOption = option;
}
}
}
return selectedOption != null ? selectedOption.getLabel() : field.getValue().toString();
}
return "";
}
}
case "booleanDecision":
if (field.getValue() != null && field.getValue().equals("true")) return "Yes";
if (field.getValue() != null && field.getValue().equals("false")) return "No";
return null;
case "radiobox":
return field.getValue() != null ? field.getValue().toString() : null;
case "checkBox":
CheckBoxData data = (CheckBoxData) field.getData();
if (field.getValue() == null || field.getValue().equals("false")) return null;
return data.getLabel();
case "datepicker":
case "datePicker":{
Instant instant;
if (!((String)field.getValue()).isEmpty()) {
try {
instant = Instant.parse((String) field.getValue());
} catch (DateTimeParseException ex) {
instant = LocalDate.parse((String) field.getValue()).atStartOfDay().toInstant(ZoneOffset.UTC);
}
return field.getValue() != null ? DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault()).format(instant) : "";
}
return (String) field.getValue();
}
case "freetext":
case "textarea":
case "richTextarea":
return field.getValue() != null ? field.getValue().toString(): "";
case "datasetIdentifier":
case "validation":
if (field.getValue() != null && !field.getValue().toString().isEmpty()) {
Map<String, String> identifierData;
try {
identifierData = mapper.readValue(field.getValue().toString(), HashMap.class);
} catch (Exception ex) {
// logger.warn(ex.getLocalizedMessage(), ex);
// logger.info("Reverting to custom parsing");
identifierData = customParse(field.getValue().toString());
}
return "id: " + identifierData.get("identifier") + ", Type: " + identifierData.get("type");
}
return "";
}
return null;
}
private boolean hasVisibleFields(FieldSet compositeFields, VisibilityRuleService visibilityRuleService) {
return compositeFields.getFields().stream().anyMatch(field -> visibilityRuleService.isElementVisible(field.getId()) && field.getExport());
}
private Map<String, String> customParse(String value) {
Map<String, String> result = new LinkedHashMap<>();
String parsedValue = value.replaceAll("[^a-zA-Z0-9\\s:=,]", "");
StringTokenizer commaTokens = new StringTokenizer(parsedValue, ", ");
String delimeter = parsedValue.contains("=") ? "=" : ":";
while (commaTokens.hasMoreTokens()) {
String token = commaTokens.nextToken();
StringTokenizer delimiterTokens = new StringTokenizer(token, delimeter);
result.put(delimiterTokens.nextToken(), delimiterTokens.nextToken());
}
return result;
}
public int findPosOfPoweredBy(XWPFDocument document) {
for (XWPFParagraph p : document.getParagraphs()) {
List<XWPFRun> runs = p.getRuns();
if (runs != null) {
for (XWPFRun r : runs) {
String text = r.getText(0);
if (text != null) {
if (text.equals("Powered by")) {
return document.getPosOfParagraph(p) - 1;
}
}
}
}
}
return -1;
}
public void fillFirstPage(DMP dmpEntity, Dataset datasetEntity, XWPFDocument document, boolean isDataset) {
int parPos = 0;
int descrParPos = -1;
XWPFParagraph descrPar = null;
for(XWPFParagraph p: document.getParagraphs()){
if( dmpEntity != null) {
this.replaceTextSegment(p, "'{ARGOS.DMP.TITLE}'", dmpEntity.getLabel());
this.replaceTextSegment(p, "'{ARGOS.DMP.VERSION}'", "Version " + dmpEntity.getVersion());
}
if( datasetEntity != null) {
this.replaceTextSegment(p, "'{ARGOS.DATASET.TITLE}'", datasetEntity.getLabel());
}
String researchersNames = "";
Set<Researcher> researchers = dmpEntity.getResearchers();
int i = 0;
for(Researcher researcher : researchers){
i++;
researchersNames += researcher.getLabel() + (i < researchers.size() ? ", " : "");
}
this.replaceTextSegment(p, "'{ARGOS.DMP.RESEARCHERS}'", researchersNames, 15);
String organisationsNames = "";
Set<Organisation> organisations = dmpEntity.getOrganisations();
i = 0;
for(Organisation organisation : organisations){
i++;
organisationsNames += organisation.getLabel() + (i < organisations.size() ? ", " : "");
}
this.replaceTextSegment(p, "'{ARGOS.DMP.ORGANIZATIONS}'", organisationsNames, 15);
if(this.textSegmentExists(p,"'{ARGOS.DMP.DESCRIPTION}'")) {
descrParPos = parPos;
descrPar = p;
this.replaceTextSegment(p, "'{ARGOS.DMP.DESCRIPTION}'", "");
}
if(this.textSegmentExists(p,"'{ARGOS.DATASET.DESCRIPTION}'")) {
descrParPos = parPos;
descrPar = p;
this.replaceTextSegment(p, "'{ARGOS.DATASET.DESCRIPTION}'", "");
}
}
if((descrParPos != -1) && (dmpEntity!=null) && (dmpEntity.getDescription() != null) && !isDataset) {
XmlCursor cursor = descrPar.getCTP().newCursor();
cursor.toNextSibling();
Document htmlDoc = Jsoup.parse(((String)dmpEntity.getDescription()).replaceAll("\n", "<br>"));
HtmlToWorldBuilder htmlToWorldBuilder = new HtmlToWorldBuilder(descrPar, 0, cursor);
NodeTraversor.traverse(htmlToWorldBuilder, htmlDoc);
}
if((descrParPos != -1) && (datasetEntity != null) && (datasetEntity.getDescription() != null) && isDataset) {
XmlCursor cursor = descrPar.getCTP().newCursor();
cursor.toNextSibling();
Document htmlDoc = Jsoup.parse(((String)datasetEntity.getDescription()).replaceAll("\n", "<br>"));
HtmlToWorldBuilder htmlToWorldBuilder = new HtmlToWorldBuilder(descrPar, 0, cursor);
NodeTraversor.traverse(htmlToWorldBuilder, htmlDoc);
}
XWPFTable tbl = document.getTables().get(0);
Iterator<XWPFTableRow> it = tbl.getRows().iterator();
it.next(); // skip first row
if(it.hasNext() && dmpEntity.getGrant() != null){
XWPFParagraph p = it.next().getCell(0).getParagraphs().get(0);
XWPFRun run = p.createRun();
run.setText(dmpEntity.getGrant().getFunder().getLabel());
run.setFontSize(15);
p.setAlignment(ParagraphAlignment.CENTER);
}
it = tbl.getRows().iterator();
it.next();
if(it.hasNext() && dmpEntity.getGrant() != null){
XWPFParagraph p = it.next().getCell(1).getParagraphs().get(0);
XWPFRun run = p.createRun();
String text = dmpEntity.getGrant().getLabel();
String reference = dmpEntity.getGrant().getReference();
if(reference != null) {
String[] parts = reference.split("::");
text += parts.length > 1 ? "/ No "+parts[parts.length - 1] : "";
}
run.setText(text);
run.setFontSize(15);
p.setAlignment(ParagraphAlignment.CENTER);
}
}
public void fillFooter(DMP dmpEntity, Dataset datasetEntity, XWPFDocument document, boolean isDataset) {
document.getFooterList().forEach(xwpfFooter -> {
List<XWPFRun> runs = xwpfFooter.getParagraphs().get(0).getRuns();
for(XWPFParagraph p : xwpfFooter.getParagraphs()){
if(p != null){
if( dmpEntity != null) {
this.replaceTextSegment(p, "'{ARGOS.DMP.TITLE}'", dmpEntity.getLabel());
}
if( datasetEntity != null) {
this.replaceTextSegment(p, "'{ARGOS.DATASET.TITLE}'", datasetEntity.getLabel());
}
Map<String, String> license = null;
try {
license = ((Map<String, String>) mapper.readValue(dmpEntity.getExtraProperties(), Map.class).get("license"));
if (license != null && license.get("pid") != null) {
this.replaceTextSegment(p, "'{ARGOS.DMP.LICENSE}'", license.get("pid"));
} else {
this.replaceTextSegment(p, "'{ARGOS.DMP.LICENSE}'", "License: -");
}
} catch (JsonProcessingException e) {
this.replaceTextSegment(p, "'{ARGOS.DMP.LICENSE}'", "License: -");
}
if(dmpEntity.getDois() != null && !dmpEntity.getDois().isEmpty()) {
this.replaceTextSegment(p, "'{ARGOS.DMP.DOI}'", dmpEntity.getDois().iterator().next().getDoi());
} else {
this.replaceTextSegment(p, "'{ARGOS.DMP.DOI}'", "-");
}
DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
this.replaceTextSegment(p, "'{ARGOS.DMP.LAST_MODIFIED}'", formatter.format(dmpEntity.getModified()));
}
}
});
}
private boolean textSegmentExists(XWPFParagraph paragraph, String textToFind) {
TextSegment foundTextSegment = null;
PositionInParagraph startPos = new PositionInParagraph(0, 0, 0);
while((foundTextSegment = this.searchText(paragraph, textToFind, startPos)) != null) {
return true;
}
return false;
}
private void replaceTextSegment(XWPFParagraph paragraph, String textToFind, String replacement) {
this.replaceTextSegment(paragraph, textToFind, replacement, null);
}
private void replaceTextSegment(XWPFParagraph paragraph, String textToFind, String replacement, Integer fontSize) {
TextSegment foundTextSegment = null;
PositionInParagraph startPos = new PositionInParagraph(0, 0, 0);
while((foundTextSegment = this.searchText(paragraph, textToFind, startPos)) != null) { // search all text segments having text to find
System.out.println(foundTextSegment.getBeginRun()+":"+foundTextSegment.getBeginText()+":"+foundTextSegment.getBeginChar());
System.out.println(foundTextSegment.getEndRun()+":"+foundTextSegment.getEndText()+":"+foundTextSegment.getEndChar());
// maybe there is text before textToFind in begin run
XWPFRun beginRun = paragraph.getRuns().get(foundTextSegment.getBeginRun());
String textInBeginRun = beginRun.getText(foundTextSegment.getBeginText());
String textBefore = textInBeginRun.substring(0, foundTextSegment.getBeginChar()); // we only need the text before
// maybe there is text after textToFind in end run
XWPFRun endRun = paragraph.getRuns().get(foundTextSegment.getEndRun());
String textInEndRun = endRun.getText(foundTextSegment.getEndText());
String textAfter = textInEndRun.substring(foundTextSegment.getEndChar() + 1); // we only need the text after
if (foundTextSegment.getEndRun() == foundTextSegment.getBeginRun()) {
textInBeginRun = textBefore + replacement + textAfter; // if we have only one run, we need the text before, then the replacement, then the text after in that run
} else {
textInBeginRun = textBefore + replacement; // else we need the text before followed by the replacement in begin run
endRun.setText(textAfter, foundTextSegment.getEndText()); // and the text after in end run
}
beginRun.setText(textInBeginRun, foundTextSegment.getBeginText());
if (fontSize != null) {
beginRun.setFontSize(fontSize);
}
// runs between begin run and end run needs to be removed
for (int runBetween = foundTextSegment.getEndRun() - 1; runBetween > foundTextSegment.getBeginRun(); runBetween--) {
paragraph.removeRun(runBetween); // remove not needed runs
}
}
}
/**
* this methods parse the paragraph and search for the string searched.
* If it finds the string, it will return true and the position of the String
* will be saved in the parameter startPos.
*
* @param searched
* @param startPos
*/
private TextSegment searchText(XWPFParagraph paragraph, String searched, PositionInParagraph startPos) {
int startRun = startPos.getRun(),
startText = startPos.getText(),
startChar = startPos.getChar();
int beginRunPos = 0, candCharPos = 0;
boolean newList = false;
//CTR[] rArray = paragraph.getRArray(); //This does not contain all runs. It lacks hyperlink runs for ex.
java.util.List<XWPFRun> runs = paragraph.getRuns();
int beginTextPos = 0, beginCharPos = 0; //must be outside the for loop
//for (int runPos = startRun; runPos < rArray.length; runPos++) {
for (int runPos = startRun; runPos < runs.size(); runPos++) {
//int beginTextPos = 0, beginCharPos = 0, textPos = 0, charPos; //int beginTextPos = 0, beginCharPos = 0 must be outside the for loop
int textPos = 0, charPos;
//CTR ctRun = rArray[runPos];
CTR ctRun = runs.get(runPos).getCTR();
XmlCursor c = ctRun.newCursor();
c.selectPath("./*");
try {
while (c.toNextSelection()) {
XmlObject o = c.getObject();
if (o instanceof CTText) {
if (textPos >= startText) {
String candidate = ((CTText) o).getStringValue();
if (runPos == startRun) {
charPos = startChar;
} else {
charPos = 0;
}
for (; charPos < candidate.length(); charPos++) {
if ((candidate.charAt(charPos) == searched.charAt(0)) && (candCharPos == 0)) {
beginTextPos = textPos;
beginCharPos = charPos;
beginRunPos = runPos;
newList = true;
}
if (candidate.charAt(charPos) == searched.charAt(candCharPos)) {
if (candCharPos + 1 < searched.length()) {
candCharPos++;
} else if (newList) {
TextSegment segment = new TextSegment();
segment.setBeginRun(beginRunPos);
segment.setBeginText(beginTextPos);
segment.setBeginChar(beginCharPos);
segment.setEndRun(runPos);
segment.setEndText(textPos);
segment.setEndChar(charPos);
return segment;
}
} else {
candCharPos = 0;
}
}
}
textPos++;
} else if (o instanceof CTProofErr) {
c.removeXml();
} else if (o instanceof CTRPr) {
//do nothing
} else {
candCharPos = 0;
}
}
} finally {
c.dispose();
}
}
return null;
}
}