package jeql.command.io.xml;
import java.io.IOException;
import java.io.Writer;
import jeql.api.row.Row;
import jeql.api.row.RowIterator;
import jeql.api.row.RowSchema;
import jeql.api.table.Table;
import jeql.command.io.IOConstants;
import jeql.command.io.TableFileWriterCmd;
import jeql.engine.Scope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.gml2.GMLWriter;
// TODO: allow output of nested column values (using _ as tag sep)
/**
* Outputs XML with defined tags for table and row tags.
* Column tags are defined by column names.
* Also allows outputing custom XML attributes for table element.
* Geometries are written in GML2 format (to write them as WKT, convert them to a WKT string in JEQL)
*
* @author Martin Davis
*
*/
public class XMLWriterCommand
extends TableFileWriterCmd
{
public static String[] splitTags(String tags, String tagSep)
{
String[] tag = tags.split(tagSep);
return tag;
}
private static final int GEOM_FORMAT_WKT = 0;
private static final int GEOM_FORMAT_GML2 = 1;
private String comment = null;
private String tableAttr = null;
private String tableTag = "table";
private String rowTag = "row";
private int geometryFormat = GEOM_FORMAT_WKT;
private RowSchema schema = null;
private XmlDataWriter xmlWriter;
private GMLWriter gmlWriter = new GMLWriter();
public XMLWriterCommand() {
super();
}
/**
* Sets the name to use as the table tag name.
* Name string may contain multiple tags separated by TBL_ROW_TAGS_SEPARATOR.
* Multiple tags will be written as nested elements.
*
* @param tableTag
*/
public void setTableTag(String tableTag)
{
this.tableTag = tableTag;
}
/**
* Sets the name to use as the row tag name.
* Name string may contain multiple tags separated by TBL_ROW_TAGS_SEPARATOR.
* Multiple tags will be written as nested elements.
*
* @param rowTag
*/
public void setRowTag(String rowTag)
{
this.rowTag = rowTag;
}
/**
* Sets comment text to write at start of document
* @param comment
*/
public void setComment(String comment)
{
this.comment = comment;
}
/**
* Attributes string to write in table tag
* @param tableAttr
*/
public void setTableAttr(String tableAttr)
{
this.tableAttr = tableAttr;
}
/**
* Sets whether to write geometry as GML-style XML
*
* @param asGML
*/
public void setGml(boolean asGML)
{
if (asGML)
geometryFormat = GEOM_FORMAT_GML2;
}
public void execute(Scope scope) throws Exception {
writer = getWriter();
xmlWriter = new XmlDataWriter(writer);
xmlWriter.prolog();
if (comment != null)
xmlWriter.comment(comment);
// write table
int n = writeStartElements(tableTag, tableAttr);
write(tbl);
xmlWriter.elementEnd(n);
writer.close();
}
private int writeStartElements(String tag, String attr1)
throws IOException
{
String[] tags = splitTags(tag, IOConstants.XML_NESTED_TAGS_SEPARATOR);
for (int i = 0; i < tags.length; i++) {
if (i == 0)
xmlWriter.elementStart(tags[0], attr1);
else
xmlWriter.elementStart(tags[i]);
}
return tags.length;
}
protected void write(Table tbl) throws Exception
{
schema = tbl.getRows().getSchema();
RowIterator rs = tbl.getRows().iterator();
while (true) {
Row row = rs.next();
if (row == null)
break;
int n = writeStartElements(rowTag, null);
writeRow(writer, row);
xmlWriter.elementEnd(n);
}
}
private void writeRow(Writer writer, Row row) throws IOException {
for (int i = 0; i < row.size(); i++) {
String itemTag = schema.getName(i);
Object val = row.getValue(i);
if (val instanceof String) {
xmlWriter.elementWithData(itemTag, (String) val);
}
else if (val instanceof Geometry) {
// indent geometry output
xmlWriter.elementStart(itemTag);
xmlWriter.markup(geometryRep((Geometry) val), true);
xmlWriter.elementEnd();
// xmlWriter.elementWithDataRaw(itemTag, geometryRep((Geometry) val)));
}
else {
// no need to encode non-string values -> faster
xmlWriter.elementWithDataRaw(itemTag, val.toString());
}
// TODO: better formatting for data items (eg Geometry, dates) ??
}
}
private String geometryRep(Geometry g)
{
if (geometryFormat == GEOM_FORMAT_GML2)
return gmlWriter.write(g);
return g.toString() + "\n";
}
}