PropertiesProcessor.java

package org.docascode.api.core.office;

import org.apache.commons.io.FilenameUtils;
import org.docascode.api.core.errors.DocAsCodeException;
import org.docascode.api.event.Event;
import org.docascode.api.listener.APIEventListener;
import org.docx4j.docProps.core.dc.elements.SimpleLiteral;
import org.docx4j.docProps.custom.Properties;
import org.docx4j.model.fields.FieldUpdater;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.OpcPackage;
import org.docx4j.openpackaging.packages.SpreadsheetMLPackage;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.packages.PresentationMLPackage;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static java.util.Map.*;

public class PropertiesProcessor extends APIEventListener {
    private static Map<String,Map<String,String>> dicts = new HashMap<>();

    public Map<String,Map<String,String>> list(List<File> files) throws DocAsCodeException {
        for (File file : files){
            list(file);
        }
        List<String> selectedKeys = files.stream()
                .map(File::getAbsolutePath)
                .filter(key -> dicts.keySet().contains(key))
                .collect(Collectors.toList());
        return dicts.entrySet()
                .stream()
                .filter(entry -> selectedKeys.contains(entry.getKey()))
                .collect(Collectors.toMap(
                        Entry::getKey,
                        Entry::getValue));
    }

    public Map<String,String> list(File file) throws DocAsCodeException {
        if (file == null){
            return new HashMap<>();
        } else {
            if (!dicts.containsKey(file.getAbsolutePath())) {
                try {
                    HashMap<String, String> properties = new HashMap<>();
                    OpcPackage pkg = open(file);
                    if (pkg != null) {
                        String key;
                        String value;
                        List<org.docx4j.docProps.custom.Properties.Property> listCustomProperties = pkg.getDocPropsCustomPart().getContents().getProperty();
                        for (org.docx4j.docProps.custom.Properties.Property property : listCustomProperties) {
                            key = property.getName();
                            value = property.getLpwstr();
                            properties.put(key, value);
                        }
                        Map<String, List<String>> coreProperties = listCore(pkg);
                        for (HashMap.Entry<String, List<String>> entry : coreProperties.entrySet()) {
                            if (!entry.getValue().isEmpty()) {
                                value = entry.getValue().get(0);
                            } else {
                                value = "";
                            }
                            properties.put(entry.getKey(), value);
                        }
                    }
                    dicts.put(file.getAbsolutePath(), properties);
                } catch (Docx4JException e) {
                    throw new DocAsCodeException(
                            String.format(
                                    "Unable to list properties of file '%s'.",
                                    file),
                            e);
                }
            }
        }
        return dicts.get(file.getAbsolutePath());
    }

    private static OpcPackage open(File file) throws Docx4JException {
        String extension = FilenameUtils.getExtension(file.getAbsolutePath());
        OpcPackage openPackage;
        switch (extension) {
            case "docx":
            case "docm":
                openPackage = WordprocessingMLPackage.load(file);
                break;
            case "xlsx":
            case "xlsm":
                openPackage = SpreadsheetMLPackage.load(file);
                break;
            case "pptx":
            case "pptm":
                openPackage = PresentationMLPackage.load(file);
                break;
            default:
                openPackage = null;
        }
        return openPackage;
    }

    private Map<String, List<String>> listCore(OpcPackage document) throws Docx4JException {
        HashMap<String,List <String>> hashMap = new HashMap<>();
        try {
            hashMap.put("Title", document.getDocPropsCorePart().getContents().getTitle().getValue().getContent());
        } catch (NullPointerException e) {
            //Do nothing
        }
        return hashMap;
    }

    public void update(File file, Map<String,String> map) throws DocAsCodeException {
        if (file != null) {
            try {
                String extension = FilenameUtils.getExtension(file.getAbsolutePath());
                OpcPackage openPackage;
                switch (extension) {
                    case "docx":
                    case "docm":
                        openPackage = WordprocessingMLPackage.load(file);
                        break;
                    case "xlsx":
                    case "xlsm":
                        openPackage = SpreadsheetMLPackage.load(file);
                        break;
                    case "pptx":
                    case "pptm":
                        openPackage = PresentationMLPackage.load(file);
                        break;
                    default:
                        return;
                }
                org.docx4j.docProps.core.dc.elements.ObjectFactory dcElfactory = new org.docx4j.docProps.core.dc.elements.ObjectFactory();
                SimpleLiteral literal = dcElfactory.createSimpleLiteral();
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    switch (entry.getKey()){
                        case "Title":
                            literal.getContent().add(entry.getValue());
                            openPackage.addDocPropsCorePart();
                            openPackage.getDocPropsCorePart().getContents().setTitle(
                                    dcElfactory.createTitle(literal));
                            break;
                        case "Description":
                            break;
                        default:
                            openPackage.addDocPropsCustomPart();
                            if (openPackage.getDocPropsCustomPart().getContents().getProperty().stream()
                                    .map(Properties.Property::getName)
                                    .collect(Collectors.toList()).contains(entry.getKey())){
                                log(String.format("\tupdate %s: '%s'", entry.getKey(), entry.getValue()), Event.Level.INFO);
                            } else {
                                log(String.format("\tcreate %s: '%s'", entry.getKey(), entry.getValue()), Event.Level.INFO);
                            }
                            openPackage.getDocPropsCustomPart().setProperty(entry.getKey(),entry.getValue());
                    }
                }
                if (openPackage instanceof  WordprocessingMLPackage){
                    FieldUpdater fieldUpdater = new FieldUpdater((WordprocessingMLPackage)openPackage);
                    fieldUpdater.update(true);
                }
                openPackage.save(file);
            } catch (Docx4JException e) {
                throw new DocAsCodeException(
                        String.format("Unable to update properties of %s",
                                file.getAbsolutePath())
                        ,e);
            }
        }
    }

    public void delete(File file, List<String> delete) throws DocAsCodeException {
        if (file != null) {
            try {
                OpcPackage openPackage = open(file);
                if (openPackage!=null) {
                    List<org.docx4j.docProps.custom.Properties.Property> properties = openPackage.getDocPropsCustomPart().getContents().getProperty();
                    Iterator<org.docx4j.docProps.custom.Properties.Property> propertyIterator = properties.iterator();
                    while (propertyIterator.hasNext()) {
                        org.docx4j.docProps.custom.Properties.Property p = propertyIterator.next();
                        if (delete.contains(p.getName())) {
                            propertyIterator.remove();
                            log(String.format("\tdelete '%s'",p.getName()), Event.Level.INFO);
                        }
                    }
                }
            } catch (Docx4JException e) {
                throw new DocAsCodeException(
                        String.format("Unable to delete properties '%s' of '%s'",
                                delete,
                                file.getAbsolutePath())
                        , e);
            }
        }
    }

}