package er.rest.format;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.util.Date;
import java.util.Map;
import com.webobjects.foundation.NSTimestamp;
import er.extensions.foundation.ERXProperties;
import er.extensions.foundation.ERXStringUtilities;
import er.rest.ERXRestContext;
import er.rest.ERXRestRequestNode;
import er.rest.ERXRestUtils;
/**
*
* @property ERXRest.suppressTypeAttributesForSimpleTypes (default "false") If set to true, primitive types, like type = "datetime", won't be added to the output
* @property <code>ERXRest.includeNullValues</code> Boolean property to enable null values in return. Defaults
* to true.
*/
public class ERXXmlRestWriter extends ERXRestWriter {
@Override
public void appendToResponse(ERXRestRequestNode node, IERXRestResponse response, ERXRestFormat.Delegate delegate, ERXRestContext context) {
appendHeadersToResponse(node, response, context);
response.setContentEncoding(contentEncoding());
response.appendContentString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
appendNodeToResponse(node, response, 0, delegate, context);
}
protected String coerceValueToString(Object value, ERXRestContext context) {
return ERXRestUtils.coerceValueToString(value, context);
}
protected void appendNodeToResponse(ERXRestRequestNode node, IERXRestResponse response, int indent, ERXRestFormat.Delegate delegate, ERXRestContext context) {
if (delegate != null) {
delegate.nodeWillWrite(node);
}
if (node.value() != null || node.isNull()) {
appendValueToResponse(node, response, indent, context);
}
else if (node.isArray()) {
appendArrayToResponse(node, response, indent, delegate, context);
}
else {
appendDictionaryToResponse(node, response, indent, delegate, context);
}
}
protected void appendValueToResponse(ERXRestRequestNode node, IERXRestResponse response, int indent, ERXRestContext context) {
String name = node.name();
Object value = node.value();
String formattedValue = coerceValueToString(value, context);
if (formattedValue == null) {
if(ERXProperties.booleanForKeyWithDefault("ERXRest.includeNullValues", true)) {
indent(response, indent);
response.appendContentString("<");
response.appendContentString(name);
appendAttributesToResponse(node, response, context);
appendTypeToResponse(value, response);
response.appendContentString("/>");
response.appendContentString("\n");
}
} else {
indent(response, indent);
response.appendContentString("<");
response.appendContentString(name);
appendAttributesToResponse(node, response, context);
appendTypeToResponse(value, response);
response.appendContentString(">");
response.appendContentString(ERXStringUtilities.escapeNonXMLChars(formattedValue));
response.appendContentString("</");
response.appendContentString(name);
response.appendContentString(">");
response.appendContentString("\n");
}
}
protected void appendAttributesToResponse(ERXRestRequestNode node, IERXRestResponse response, ERXRestContext context) {
for (Map.Entry<String, Object> attribute : node.attributes().entrySet()) {
String key = attribute.getKey();
String formattedValue = coerceValueToString(attribute.getValue(), context);
if (formattedValue != null) {
response.appendContentString(" ");
response.appendContentString(key);
response.appendContentString("=\"");
response.appendContentString(ERXStringUtilities.escapeNonXMLChars(formattedValue));
response.appendContentString("\"");
}
}
}
protected void appendDictionaryToResponse(ERXRestRequestNode node, IERXRestResponse response, int indent, ERXRestFormat.Delegate delegate, ERXRestContext context) {
String objectName = node.name();
if (objectName == null) {
objectName = node.type();
}
indent(response, indent);
response.appendContentString("<");
response.appendContentString(objectName);
appendAttributesToResponse(node, response, context);
if (node.children().size() == 0) {
response.appendContentString("/>");
response.appendContentString("\n");
}
else {
response.appendContentString(">");
response.appendContentString("\n");
for (ERXRestRequestNode child : node.children()) {
appendNodeToResponse(child, response, indent + 1, delegate, context);
}
indent(response, indent);
response.appendContentString("</");
response.appendContentString(objectName);
response.appendContentString(">");
response.appendContentString("\n");
}
}
protected void appendTypeToResponse(Object value, IERXRestResponse response) {
if (value instanceof String) {
// do nothing
}
else if (!ERXProperties.booleanForKeyWithDefault("ERXRest.suppressTypeAttributesForSimpleTypes", false)) {
if (value instanceof NSTimestamp) {
response.appendContentString(" type = \"datetime\"");
}
else if (value instanceof Date) {
response.appendContentString(" type = \"datetime\"");
}
else if (value instanceof LocalDate) {
response.appendContentString(" type = \"date\"");
}
else if (value instanceof LocalDateTime) {
response.appendContentString(" type = \"datetime2\"");
}
else if (value instanceof LocalTime) {
response.appendContentString(" type = \"time\"");
}
else if (value instanceof OffsetDateTime) {
response.appendContentString(" type = \"datetime2\"");
}
else if (value instanceof Integer) {
response.appendContentString(" type = \"integer\"");
}
else if (value instanceof Long) {
response.appendContentString(" type = \"long\"");
}
else if (value instanceof Short) {
response.appendContentString(" type = \"short\"");
}
else if (value instanceof Double) {
response.appendContentString(" type = \"double\"");
}
else if (value instanceof Float) {
response.appendContentString(" type = \"float\"");
}
else if (value instanceof Boolean) {
response.appendContentString(" type = \"boolean\"");
}
else if (value instanceof BigDecimal) {
response.appendContentString(" type = \"bigint\"");
}
else if (value instanceof Enum) {
response.appendContentString(" type = \"enum\"");
}
}
}
protected void appendArrayToResponse(ERXRestRequestNode node, IERXRestResponse response, int indent, ERXRestFormat.Delegate delegate, ERXRestContext context) {
indent(response, indent);
String arrayName = node.name();
response.appendContentString("<");
response.appendContentString(arrayName);
appendAttributesToResponse(node, response, context);
response.appendContentString(">");
response.appendContentString("\n");
for (ERXRestRequestNode child : node.children()) {
appendNodeToResponse(child, response, indent + 1, delegate, context);
}
indent(response, indent);
response.appendContentString("</");
response.appendContentString(arrayName);
response.appendContentString(">");
response.appendContentString("\n");
}
protected void indent(IERXRestResponse response, int indent) {
for (int i = 0; i < indent; i++) {
response.appendContentString(" ");
}
}
@Override
public String contentType() {
return "text/xml";
}
}