package org.rr.jeborker.metadata;
import static org.rr.commons.utils.StringUtil.EMPTY;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map.Entry;
import org.rr.commons.collection.TransformValueList;
import org.rr.commons.log.LoggerFactory;
import org.rr.commons.utils.DateConversionUtils;
import org.rr.commons.utils.ReflectionUtils;
import org.rr.commons.utils.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
class PDFMetadataProperty extends MetadataProperty {
private Hashtable<String, String> attributes;
private Class<?> propertyClass = null;
/**
* The list Element if the value is not located at the textContent of the tag.
* For example rdf:Seq or rdf:Alt
*/
private String listElementName;
/**
* If there is no simple value but a list of them, it is represented by a list
* of {@link PDFMetadataProperty}.
*/
private List<PDFMetadataProperty> childs;
private Object value;
/**
* The namespace like xap pdf or dc
*/
private String namespace = EMPTY;
PDFMetadataProperty(final String tagName, final Object value, final String listElementName) {
super(tagName, value);
this.listElementName = listElementName;
if(value instanceof String) {
this.value = StringUtil.trim((String)value);
} else {
this.value = value;
}
final int namespaceSeparatorIndex = tagName.indexOf(':');
if(namespaceSeparatorIndex!=-1) {
this.namespace = tagName.substring(0, namespaceSeparatorIndex);
}
}
/**
* Gets the name of the property. The name did not contain the namespace.
*/
@Override
public String getName() {
final String name = super.getName();
try {
return name.substring(namespace.length()+1);
} catch (Exception e) {
return name;
}
}
@Override
public List<Object> getValues() {
boolean isDate = ReflectionUtils.equals(getPropertyClass(), Date.class);
if (childs != null && childs.size() > 0) {
final ArrayList<Object> result = new ArrayList<>(childs.size());
for (PDFMetadataProperty child : childs) {
Object childValue = child.getValues().get(0);
if(isDate && !(childValue instanceof Date)) {
Date date = DateConversionUtils.toDate(StringUtil.toString(childValue));
result.add(date);
} else {
result.add(childValue);
}
}
return result;
} else {
List<Object> values = super.getValues();
if(isDate) {
return new TransformValueList<Object, Object>(values) {
@Override
public Object transform(Object source) {
Date d = DateConversionUtils.toDate(StringUtil.toString(source));
if(d != null) {
return d;
}
return source;
}
};
}
return values;
}
}
/**
* Gets an attribute by it's name.
* @param name The name of the attribute to be searched.
* @return The desired attribute.
*/
public String getAttributeValueByName(String name) {
if(attributes!=null) {
return attributes.get(name);
}
return null;
}
public Class<?> getPropertyClass() {
if(propertyClass==null) {
if(this.name.endsWith("Date")) {
propertyClass = Date.class;
} else if(values.get(0) instanceof Date) {
propertyClass = Date.class;
}
} else {
return propertyClass;
}
return String.class;
}
/**
* Adds a key/value pair for attributes.
* @param name The name of the attribute.
* @param value The value of the attribute.
*/
public void addAttribute(String name, String value) {
if(this.attributes==null) {
this.attributes = new Hashtable<String, String>();
}
this.attributes.put(name, value);
}
/**
* Adds a child property. This property could be one of several
* ones and holds the value of the parent here.
* @param property The property to be added.
*/
public void addChild(PDFMetadataProperty property) {
if(this.childs==null) {
//mostly there is only one entry.
this.childs = new ArrayList<>(1);
}
this.childs.add(property);
}
/**
* Creates an {@link Element} for this {@link EpubMetadataProperty} instance.
* @param document Needed for creating the right element for the given document.
* @return The desired {@link Element}
*/
public Element createElement(final Document document) {
Element resultElement = document.createElement(super.getName());
if(attributes!=null) {
for (Entry<String, String> entry : attributes.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
resultElement.setAttribute(name, value);
}
}
if(listElementName==null && super.getValues().size()==1) {
Object value = super.getValues().get(0);
if(value instanceof Date) {
final String dateString = DateConversionUtils.toString((Date)value, DateConversionUtils.DATE_FORMATS.W3C_SECOND);
resultElement.setTextContent(dateString);
} else {
resultElement.setTextContent(StringUtil.toString(value));
}
} else if(listElementName!=null && childs!=null && childs.size() > 0) {
final Element listElement = document.createElement(listElementName);
for (PDFMetadataProperty child : childs) {
Element childElement = child.createElement(document);
listElement.appendChild(childElement);
}
resultElement.appendChild(listElement);
}
return resultElement;
}
@Override
public void setValue(Object value, int idx) {
final boolean isDate = ReflectionUtils.equals(getPropertyClass(), Date.class);
if(isDate) {
final String dateValue = DateConversionUtils.toString(DateConversionUtils.toDate(value != null ? String.valueOf(value) : null), DateConversionUtils.DATE_FORMATS.W3C_SECOND);
setValueToList(dateValue, idx);
} else {
setValueToList(value, idx);
}
}
private void setValueToList(final Object value, final int idx) {
if(childs==null && idx==0) {
super.setValue(value, idx);
} else {
try {
PDFMetadataProperty pdfMetadataProperty = childs.get(idx);
pdfMetadataProperty.setValue(value, idx);
} catch(ArrayIndexOutOfBoundsException e) {
LoggerFactory.logWarning(this, "could not set value " + value + " to " + idx, e);
}
}
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
/**
* Creates a new {@link MultiMetadataProperty} instance with the data of this {@link MultiMetadataProperty}.
*/
@Override
public MetadataProperty clone() {
PDFMetadataProperty newMetadataProperty = new PDFMetadataProperty(this.name, this.value, this.listElementName);
newMetadataProperty.attributes = this.attributes;
newMetadataProperty.propertyClass = this.propertyClass;
newMetadataProperty.childs = this.childs;
newMetadataProperty.namespace = this.namespace;
newMetadataProperty.hints = this.hints;
return newMetadataProperty;
}
}