--- src/ooxml/java/org/apache/poi/POIXMLDocument.java (revision 1180716) +++ src/ooxml/java/org/apache/poi/POIXMLDocument.java (working copy) @@ -22,6 +22,7 @@ import java.io.PushbackInputStream; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -34,6 +35,9 @@ import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; import org.apache.poi.poifs.common.POIFSConstants; import org.apache.poi.util.IOUtils; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; +import org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperty; public abstract class POIXMLDocument extends POIXMLDocumentPart{ public static final String DOCUMENT_CREATOR = "Apache POI"; @@ -51,6 +55,9 @@ * The properties of the OPC package, opened as needed */ private POIXMLProperties properties; + + /** For our own logging use */ + private final static POILogger LOGGER = POILogFactory.getLogger(POIXMLDocument.class); protected POIXMLDocument(OPCPackage pkg) { super(pkg); @@ -178,4 +185,86 @@ getPackage().save(stream); } + + + /** + * Update the document custom properties. If a property already exists, the value + * is replaced. If the property is new then it will be added. + * + * @param propsMap - the properties in the form of an HashMap(propertyName,value) + * + * @exception IOException if anything can't be written. + */ + public void populateCustomProperties(HashMap propsMap) { + + POIXMLProperties summaryInfo = null; + Boolean foundProperty = false; + Boolean isFirstIteration = true; + Boolean initialPropsIsEmpty = false; + + try { + + this.getProperties(); + summaryInfo = properties; + + List propList = summaryInfo.getCustomProperties() + .getUnderlyingProperties().getPropertyList(); + + Iterator iterator = propsMap.keySet().iterator(); + int i = 1; + int delta = 0; + while (iterator.hasNext()) { + String key = iterator.next().toString(); + String value = propsMap.get(key).toString(); + + LOGGER.log(POILogger.INFO, "--> Processing property key {0}, with value {1}", new Object[]{key, value}); + + foundProperty = false; + + Iterator propIter = propList.iterator(); + + if (propList.isEmpty() + && isFirstIteration.booleanValue() == Boolean.TRUE) { + initialPropsIsEmpty = Boolean.TRUE; + isFirstIteration = Boolean.FALSE; + } else { + delta = delta + propList.size(); + } + + if (initialPropsIsEmpty.booleanValue() == Boolean.FALSE) { + while (propIter.hasNext()) { + CTProperty property = propIter.next(); + if (property.getName().equalsIgnoreCase(key)) { + property.setLpwstr(value); + LOGGER.log(POILogger.INFO, "--> Replaced property name {0}, with value {1}", new Object[]{property.getName(), property.getLpwstr()}); + + foundProperty = true; + } + } + } + + if (!foundProperty) { + + CTProperty prp = summaryInfo.getCustomProperties() + .getUnderlyingProperties().insertNewProperty(i); + prp.setFmtid("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"); + prp.setPid(i + delta + 1); + prp.setName(key); + prp.setLpwstr(value); + + i++; + + LOGGER.log(POILogger.INFO, "--> Added property key {0}, with value {1}", new Object[]{key, value}); + + } + + } + + + } catch (Exception e) { + LOGGER.log(POILogger.ERROR ,e.toString()); + } + + } + } --- src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java (revision 1180716) +++ src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java (working copy) @@ -49,12 +49,15 @@ import org.apache.poi.util.IOUtils; import org.apache.poi.util.IdentifierManager; import org.apache.poi.util.Internal; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; import org.apache.poi.util.PackageHelper; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; +import org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperty; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTComment; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFtnEdn; @@ -85,6 +88,10 @@ */ public class XWPFDocument extends POIXMLDocument implements Document, IBody { + /** For our own logging use */ + private final static POILogger LOGGER = POILogFactory.getLogger(XWPFDocument.class); + private final static String EMPTYSPACE = " "; + private CTDocument1 ctDocument; private XWPFSettings settings; /** @@ -1307,6 +1314,165 @@ return tableRow.getTableCell(cell); } + + + /** + * Propagate the document custom properties. If the document (Word) + * has custom properties defined, those will be matched across the text + * and if there are differences those will be updated. + * + * @exception IOException if anything can't be written. + */ + public void propagateXwpfCustomProperties() { + + LOGGER.log(POILogger.INFO,"--> method propagateDocxProperties started!"); + + + POIXMLProperties summaryInfo = null; + + Boolean go4NextWt = false; + + Boolean processed = false; + + Boolean catchNextWt = false; + + + ArrayList toBeprocessedItems = new ArrayList(); + HashMap hashmp = new HashMap(); + Integer i = 0, j = 0; + Integer selectedItem = 0; + + summaryInfo = this.getProperties(); + + List propList = summaryInfo.getCustomProperties() + .getUnderlyingProperties().getPropertyList(); + + Iterator propsIterator = propList.iterator(); + + while (propsIterator.hasNext()) { + CTProperty prop = propsIterator.next(); + LOGGER.log(POILogger.INFO, "--> Grouping property name {0} with value= {1}", new Object[]{prop.getName(), prop.getLpwstr()}); + toBeprocessedItems.add(prop.getName()); + hashmp.put(prop.getName(), prop.getLpwstr()); + } + + List paragraphList = this.getParagraphs(); + Iterator paragraphIterator = paragraphList.iterator(); + + // Iterate over each paragraph: + + processed = false; + paragraphIterator = paragraphList.iterator(); + + while (paragraphIterator.hasNext() && !processed) { + + XWPFParagraph element = paragraphIterator.next(); + + LOGGER.log(POILogger.INFO, "--> Processing paragraph: {0}", element.getParagraphText()); + CTP ctp = element.getCTP(); + + XmlObject o; + + XmlCursor c = ctp.newCursor(); + + for (c.toFirstContentToken(); c.hasNextToken(); c.toNextToken()) { + + o = c.getObject(); + + /* + * Here we look for Customized properties that are + * defined/declared in the XML Text areas. E.g. DOCPROPERTY dummyProperty \* + * MERGEFORMAT + */ + if (c.isText() && c.getTextValue().contains("DOCPROPERTY")) { + + for (i = 0; i < toBeprocessedItems.size(); i++) { + if (c.getTextValue() != null + && c.getTextValue().contains( + EMPTYSPACE + toBeprocessedItems.get(i) + + EMPTYSPACE)) { + + selectedItem = i; + + LOGGER.log(POILogger.INFO,"-->FOUND TEXT PROPERTY DEFINITION VALUE --> " + + " with i = {0} and text : {1}", new Object[]{i, c.getTextValue()}); + } + } + + String tempText = c.getTextValue(); + int len1 = tempText.length(); + int len2 = tempText.lastIndexOf("MERGEFORMAT") + + "MERGEFORMAT".length() + 1; + if (len1 == len2) { + catchNextWt = true; + tempText = tempText.substring(0, len2); + } else { + catchNextWt = false; + } + + } + if (o != null) { + String tagName1 = o.getDomNode().getNodeName(); + + if (catchNextWt) { + if ("w:t".equals(tagName1)) { + catchNextWt = false; + c.getTextValue(); + c.setTextValue(hashmp.get(toBeprocessedItems + .get(selectedItem))); + + LOGGER.log(POILogger.INFO, "--> CHANGED value--> {0}", c.getTextValue()); + } + } + + } + + o = c.getObject(); + + /* + * Here we look for Customized properties that are + * defined/declared in the XML Tag Value areas. E.g. + * + */ + if (o != null && o.getDomNode() != null) { + + String tagName = o.getDomNode().getNodeName(); + String tagValue = o.getDomNode().getNodeValue(); + + for (j = 0; j < toBeprocessedItems.size(); j++) { + if (tagValue != null + && tagValue.contains("DOCPROPERTY") + && tagValue.contains(EMPTYSPACE + + toBeprocessedItems.get(j) + + EMPTYSPACE)) { + go4NextWt = true; + selectedItem = j; + + LOGGER.log(POILogger.INFO,"--> FOUND TAG PROPERTY DEFINITION VALUE --> " + " {0}", tagValue); + + } + } + + if (tagName.contains("w:t") + && go4NextWt.booleanValue() == true) { + + c.setTextValue(hashmp.get(toBeprocessedItems + .get(selectedItem))); + LOGGER.log(POILogger.INFO, "--> CHANGED value--> {0}", c.getTextValue()); + + go4NextWt = false; + } + + processed = true; + } + } + + } + + } + public XWPFDocument getXWPFDocument() { return this; } + true --- src/testcases/org/apache/poi/properties/OfficePropertiesTest.java (revision 0) +++ src/testcases/org/apache/poi/properties/OfficePropertiesTest.java (revision 0) @@ -0,0 +1,107 @@ +package org.apache.poi.properties; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; + + +import org.apache.poi.POIDataSamples; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xwpf.usermodel.XWPFDocument; +import org.junit.BeforeClass; +import org.junit.Test; + + +public class OfficePropertiesTest { + + static OfficePropertiesTest instance = new OfficePropertiesTest(); + + /** For our own logging use */ + private final static POILogger LOGGER = POILogFactory.getLogger(OfficePropertiesTest.class); + + + @BeforeClass + public static void init() throws IOException, ClassNotFoundException { + + } + + @Test + public void executeTestStrategy() throws Exception { + + + HashMap myTestMap = new HashMap(); + myTestMap.put("xpto", "New_XPTO"); + myTestMap.put("prop1", "New_prop1"); + myTestMap.put("prop2", "New_prop2"); + myTestMap.put("aaaaaaa", "Nova_aaaaaaa"); + FileInputStream fIsDocx = null; + FileInputStream fIsXlsx = null; + FileOutputStream fIsDocxOutput = null; + FileOutputStream fIsXlsxOutput = null; + XSSFWorkbook workbook = null; + + XWPFDocument document = null; + + System.setProperty("POI.testdata.path","D://Projects//ApachePOI_WriteAccess//trunk//test-data//"); + + + File fl = POIDataSamples.getDocumentInstance().getFile("FldComplex.docx"); + + File outputFileDocx = new File("D:/Temp/temp.docx"); + + File outputFileXlsx = new File("D:/Temp/temp.xlsx"); + + + try { + fIsDocx = new FileInputStream(fl); + } catch (IOException e) { + LOGGER.log(POILogger.WARN,"-> IOException! Check file system permissions or Input file does not exist."); + } + + + try { + fIsDocxOutput = new FileOutputStream(outputFileDocx); + } catch (IOException e) { + LOGGER.log(POILogger.WARN,"-> IOException! Check file system permissions or Output file does not exist."); + } + + try { + document = new XWPFDocument(fIsDocx); + document.populateCustomProperties(myTestMap); + document.propagateXwpfCustomProperties(); + document.write(fIsDocxOutput); + } catch (IOException e) { + LOGGER.log(POILogger.WARN,"-> IOException! "); + } + + fl = POIDataSamples.getSpreadSheetInstance().getFile("FldSimple.xlsx"); + + + try { + fIsXlsx = new FileInputStream(fl); + } catch (IOException e) { + LOGGER.log(POILogger.WARN,"-> IOException! Check file system permissions or Input file does not exist."); + } + + try { + fIsXlsxOutput = new FileOutputStream(outputFileXlsx); + } catch (IOException e) { + LOGGER.log(POILogger.WARN,"-> IOException! Check file system permissions or Output file does not exist."); + } + + try { + workbook = new XSSFWorkbook(fIsXlsx); + workbook.populateCustomProperties(myTestMap); + workbook.write(fIsXlsxOutput); + } catch (IOException e) { + LOGGER.log(POILogger.WARN,"-> IOException! "); + } + + + } + +}