org.docx4j.model.fields.FieldRef Maven / Gradle / Ivy
package org.docx4j.model.fields;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.docx4j.XmlUtils;
import org.docx4j.jaxb.Context;
import org.docx4j.wml.CTFFData;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.FldChar;
import org.docx4j.wml.R;
import org.docx4j.wml.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The objective of this class is to represent a complex field
* (containing nested fields, if any; nested fields are
* represented by FieldRef object of their own).
*
* TODO, consider whether to make this abstract, with
* differing concrete implementations for top level and nested fields.
*
* Background. There are simple fields:
*
4/12/2011
*
* and there are complex fields:
*
REF hi \h
Hello
* A simple field can also take the complex form:
*
DATE
4/12/2011
* A complex field can contain nested fields, in either
* its instruction part, or result part.
*
* we need to represent nest fields in the instructions
* part only (since nested fields in the results part
* get re-generated).
*
* An example of a nested field in the instructions part:
*
IF
4/12/2011
="4/12/2011" "it is 4/12" "not 4/12"
today
* An example of nested fields in the results part:
*
*
TOC \o "1-3" \h \z \ u
one
PAGEREF _Toc310757867 \h
1
oneone
PAGEREF _Toc310757868 \h
1
*
* In general, you can "canonicalise" the field representation
* to be
* (i) instructions, contained within a single run
* (ii) results, immediately following, though not nec just as following siblings
* (iii) the final
*
* Since the purpose of our field support is to update the field
* results, we can delete (ii) and (iii) before adding them in again.
*
* The document is preprocessed to put it into this form.
*
* @author jharrop
*
*/
public class FieldRef {
private static Logger log = LoggerFactory.getLogger(FieldRef.class);
public FieldRef(FldChar fldCharBegin) {
this.fldCharBegin = fldCharBegin;
}
private FldChar fldCharBegin;
protected String fldName = null;
/**
* The name of the (outer most) field, for example DATE, MERGEFIELD.
*
* Assume for now that this is contained in instructions.get(0).
*
* @see field syntax
* @return
*/
public String getFldName() {
Object o = XmlUtils.unwrap(instructions.get(0));
if (o instanceof Text) {
return FormattingSwitchHelper.getFldSimpleName( ((Text)o).getValue() );
} else {
log.error("TODO: extract field name from " + o.getClass().getName() );
if (o instanceof FieldRef) {
// contains a nested field?!
FieldRef nested = (FieldRef)o;
log.error("Nested field " + nested.getFldName() );
} else {
if(log.isErrorEnabled()) {
log.error(XmlUtils.marshaltoString(instructions.get(0), true, true));
}
}
return null;
}
}
private ContentAccessor parent;
public ContentAccessor getParent() {
return parent;
}
public void setParent(ContentAccessor parent) {
this.parent = parent;
}
/**
* The run
*
*
...
*/
private R beginRun;
public R getBeginRun() {
return beginRun;
}
public void setBeginRun(R beginRun) {
this.beginRun = beginRun;
}
private boolean seenSeparate=false;
public boolean haveSeenSeparate() {
return seenSeparate;
}
public void setSeenSeparate(boolean seenSeparate) {
this.seenSeparate = seenSeparate;
}
private void processFldBegin() {
formFieldProperties = fldCharBegin.getFfData();
customFieldData = fldCharBegin.getFldData();
dirty = fldCharBegin.isDirty();
lock = fldCharBegin.isFldLock();
}
private Boolean mergeFormat;
/**
* @return whether \* MERGEFORMAT is set
*/
public Boolean isMergeFormat() {
if (mergeFormat==null) {
//Work it out. Assume for now that this is contained in instructions.get(0).
mergeFormat = Boolean.FALSE;
Object o = XmlUtils.unwrap(instructions.get(0));
if (o instanceof Text) {
String instr = ((Text)o).getValue();
if (instr.contains("MERGEFORMAT")) {
mergeFormat = Boolean.TRUE;
}
} else {
if(log.isErrorEnabled()) {
log.error("TODO: extract field name from " + o.getClass().getName());
log.error(XmlUtils.marshaltoString(instructions.get(0), true, true));
}
}
}
return mergeFormat;
}
private boolean dirty;
/**
* Specifies that this field has been flagged by an application to indicate that its current results
* are invalid (stale) due to other modifications made to the document, and these contents should be
* updated before they are displayed.
* @return whether stale
* @see the spec
*/
public boolean isDirty() {
return dirty;
}
/**
* @param whether stale
* @see the spec
*/
public void setDirty(boolean dirty) {
this.dirty = dirty;
fldCharBegin.setDirty(dirty);
// Note that this doesn't set dirty on any nested fields. TODO: Consider whether it should.
}
private boolean lock;
/**
* @return the lock
* @see the spec
*/
public boolean isLock() {
return lock;
}
/**
* Specifies that the parent complex field shall not have its field result recalculated, even if
* an application attempts to recalculate the results of all fields in the document or a
* recalculation is explicitly requested.
*
* @param lock the lock to set
* @see the spec
*/
public void setLock(boolean lock) {
this.lock = lock;
fldCharBegin.setFldLock(lock);
}
private Text customFieldData;
/**
* application-specific data associated with this field.
*
* @return the customFieldData
* @see the spec
*/
public Text getCustomFieldData() {
return customFieldData;
}
/**
* @param customFieldData the customFieldData to set
* @see the spec
*/
public void setCustomFieldData(Text customFieldData) {
this.customFieldData = customFieldData;
fldCharBegin.setFldData(customFieldData);
}
private CTFFData formFieldProperties;
/**
* Properties specific to FORMCHECKBOX, FORMDROPDOWN, FORMTEXT
*
* @return the formFieldProperties
* @see the spec
*/
public CTFFData getFormFieldProperties() {
return formFieldProperties;
}
/**
* @param formFieldProperties the formFieldProperties to set
* @see the spec
*/
public void setFormFieldProperties(CTFFData formFieldProperties) {
this.formFieldProperties = formFieldProperties;
fldCharBegin.setFfData(formFieldProperties);
}
/**
* The run
*
Store a reference to it so we can delete it.
*/
private R endRun;
public R getEndRun() {
return endRun;
}
public void setEndRun(R endRun) {
this.endRun = endRun;
}
/**
* A list of the content between the outermost w:fldChar begin and separate elements;
* in the simplest case, this will be a single w:instrText object;
* in a more general case it will be a mixture of w:instrText and FieldRef objects
* (and possibly other things such as w:br).
*/
private List