/*
* Copyright 2008, Plutext Pty Ltd.
*
* This file is part of Docx4all.
Docx4all is free software: you can redistribute it and/or modify
it under the terms of version 3 of the GNU General Public License
as published by the Free Software Foundation.
Docx4all is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Docx4all. If not, see <http://www.gnu.org/licenses/>.
*/
package org.plutext.client.state;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.docx4all.swing.text.DocumentElement;
import org.docx4all.swing.text.WordMLDocument;
import org.docx4all.xml.DocumentML;
import org.docx4j.model.datastorage.CustomXmlDataStorageImpl;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.CustomXmlDataStoragePart;
import org.docx4j.openpackaging.parts.PartName;
import org.plutext.client.CustomProperties;
import org.plutext.client.Util;
import org.plutext.client.partWrapper.Part;
import org.plutext.client.partWrapper.SequencedPart;
import org.plutext.client.wrappedTransforms.TransformAbstract;
import org.plutext.client.wrappedTransforms.TransformComparator;
import org.w3c.dom.Document;
/* Represent the configuration of the
* document, and certain transient
* document-level state information.
*/
public class StateDocx {
private static Logger log = LoggerFactory.getLogger(StateDocx.class);
public StateDocx(WordMLDocument doc) {
init(doc);
}
WordprocessingMLPackage wordMLPackage = null;
private void init(WordMLDocument doc) {
String fileUri = (String) doc.getProperty(WordMLDocument.FILE_PATH_PROPERTY);
int idx = fileUri.indexOf("/alfresco/");
this.docID = fileUri.substring(idx);
DocumentElement root = (DocumentElement) doc.getDefaultRootElement();
this.wordMLPackage =
((DocumentML) root.getElementML()).getWordprocessingMLPackage();
if (Util.getCustomDocumentProperty(
wordMLPackage.getDocPropsCustomPart(),
CustomProperties.PROMPT_FOR_CHECKIN_MESSAGE).equals("true")) {
this.promptForCheckinMessage = Boolean.TRUE;
} else if (Util.getCustomDocumentProperty(
wordMLPackage.getDocPropsCustomPart(),
CustomProperties.PROMPT_FOR_CHECKIN_MESSAGE).equals("false")) {
this.promptForCheckinMessage = Boolean.FALSE;
} else {
log.warn(CustomProperties.PROMPT_FOR_CHECKIN_MESSAGE + "unknown");
this.promptForCheckinMessage = Boolean.FALSE;
}
// Expected values: EachBlock, Heading1
this.chunkingStrategy = Util.getCustomDocumentProperty(
wordMLPackage.getDocPropsCustomPart(),
CustomProperties.CHUNKING_STRATEGY);
// Find our version list CustomXml part
HashMap docx4jParts = wordMLPackage.getParts().getParts();
Iterator partsIterator = docx4jParts.entrySet().iterator();
PartName toBeRemovedPartName = null;
while (partsIterator.hasNext()) {
Map.Entry pairs = (Map.Entry)partsIterator.next();
if(pairs.getKey()==null) {
log.warn("Skipped null key");
pairs = (Map.Entry)partsIterator.next();
}
PartName partName = (PartName)pairs.getKey();
log.debug(partName.getName() );
if (pairs.getValue() instanceof CustomXmlDataStoragePart)
{
CustomXmlDataStoragePart docx4jPart
= (CustomXmlDataStoragePart)pairs.getValue();
// dom4j document :-(
//Document customXmlDoc = ((Dom4jCustomXmlDataStorage)docx4jPart.getData()).getDom4jDocument();
Document customXmlDoc = null;
try {
customXmlDoc = ((CustomXmlDataStorageImpl)docx4jPart.getData()).getDocument();
// ElementMLFactory could set in LoadFromVFSZipFile
} catch (Docx4JException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
log.debug(customXmlDoc.getDocumentElement().getLocalName() );
if (customXmlDoc.getDocumentElement().getLocalName().equals("parts")) {
// ? was = PartVersionList
partVersionList = new PartVersionList(customXmlDoc);
log.debug("set partVersionList");
// now delete this part from the package,
// including its entry in the doc's rels.
toBeRemovedPartName = partName;
}
}
}
if (toBeRemovedPartName!=null) {
wordMLPackage.getMainDocumentPart().getRelationshipsPart().removePart(toBeRemovedPartName);
// Ensure that if we create any more parts (eg a hyperlink), there isn't
// a gap in the sequence.
wordMLPackage.getMainDocumentPart().getRelationshipsPart().resetIdAllocator();
log.debug("Removed tmp part " + toBeRemovedPartName.getName() );
}
parts = Util.extractParts(doc);
sectPr = extractDocumentSectPr();
// For the parts we care about, record their version info.
// partVersionList.setVersions(parts);
partVersionList.setVersions();
this.transforms = new TransformsCollection();
this.stateChunks = Util.createStateChunks(doc);
}
private String sectPr;
public String getSectPr() {
return sectPr;
}
private PartVersionList partVersionList;
public PartVersionList getPartVersionList() {
return partVersionList;
}
private HashMap<String, Part> parts;
public HashMap<String, Part> getParts() {
return parts;
}
private HashMap<String, StateChunk> stateChunks;
public HashMap<String, StateChunk> getStateChunks() {
return stateChunks;
}
// Set from App_DocumentOpen
private String docID = null;
public String getDocID() {
return docID;
}
public void setDocID(String docID) {
this.docID = docID;
}
// These three variables are read from document properties
private Boolean promptForCheckinMessage;
public Boolean getPromptForCheckinMessage() {
return promptForCheckinMessage;
}
public void setPromptForCheckinMessage(Boolean promptForCheckinMessage) {
this.promptForCheckinMessage = promptForCheckinMessage;
}
private String chunkingStrategy;
public String getChunkingStrategy() {
return chunkingStrategy;
}
public void setChunkingStrategy(String chunkingStrategy) {
this.chunkingStrategy = chunkingStrategy;
}
private Boolean initialized = false;
public Boolean getInitialized() {
return initialized;
}
public void setInitialized(Boolean initialized) {
this.initialized = initialized;
}
// Styles stylemap = new Styles();
// public Styles getStylemap() {
// return stylemap;
// }
/**
* NB At present, docx4all has no notion of sectPr at the ML level,
* so this operates solely at the docx4j level.
* @param foreignSectPr
*/
public String extractDocumentSectPr() {
return Util.extractDocumentSectPr(this.wordMLPackage);
}
/* Maintain a collection of Transforms keyed by tSequenceNumber, so we
* can keep track of which ones have been applied. */
TransformsCollection transforms;
public TransformsCollection getTransforms() {
return transforms;
}
public class TransformsCollection
{
private int tSequenceNumberAtLoadTime = Integer.parseInt(Util
.getCustomDocumentProperty(wordMLPackage
.getDocPropsCustomPart(),
CustomProperties.DOCUMENT_TRANSFORM_SEQUENCENUMBER));
private int tSequenceNumberHighestFetched = this.tSequenceNumberAtLoadTime;
public int getTSequenceNumberHighestFetched() {
return tSequenceNumberHighestFetched;
}
public void setTSequenceNumberHighestFetched(int sequenceNumberHighestFetched) {
tSequenceNumberHighestFetched = sequenceNumberHighestFetched;
}
/* Maintain a collection of Transforms keyed by tSequenceNumber, so we
* can keep track of which ones have been applied.
*
Key is SequenceNumber, not t.ID, since TransformStyle type doesn't have an
underlying SDT. Besides, if 2 additions related to the same SDT, the
keys would collide.
*/
ArrayList<TransformAbstract> transformsBySeqNum = new ArrayList<TransformAbstract>();
public ArrayList<TransformAbstract> getTransformsBySeqNum() {
Collections.sort(transformsBySeqNum, new TransformComparator());
return transformsBySeqNum;
}
public void add(TransformAbstract t, Boolean updateHighestFetched)
{
// Check it is not already present before adding
// This list should be pretty short, so there is
// little harm in just iterating through it
for (TransformAbstract ta : transformsBySeqNum)
{
if (ta.getSequenceNumber() == t.getSequenceNumber())
{
log.debug(t.getSequenceNumber() + " already registered. Ignoring.");
return;
}
}
transformsBySeqNum.add(t);
if (updateHighestFetched && t.getSequenceNumber() > tSequenceNumberHighestFetched)
{
// Only update highest fetched if this addition is
// a result of fetch updates. (If it is a result
// of local transmits, there is a possibility that
// someone else may have generated an snum (ie while
// our transmitLocalChanges was running), and we
// wouldn't want to miss that
tSequenceNumberHighestFetched = Integer.parseInt(Long.toString(t.getSequenceNumber()));
}
}
public ArrayList<TransformAbstract> getTransformsBySdtId(String id, Boolean includeLocals)
{
ArrayList<TransformAbstract> list = new ArrayList<TransformAbstract>();
for (TransformAbstract ta : transformsBySeqNum)
{
if (ta.getPlutextId().equals(id))
{
if (!ta.isLocal() || includeLocals)
{
list.add(ta);
log.debug("Instance -- found a transform applicable to Sdt " + id);
}
}
}
return list;
}
} //TransformsCollection inner class
} //StateDocx class