/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.io; import com.vividsolutions.jts.geom.*; import com.vividsolutions.jts.util.Assert; import com.vividsolutions.jump.feature.*; import java.io.BufferedWriter; import java.io.FileReader; import java.lang.reflect.Array; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; /** * GMLWriter is a {@link JUMPWriter} specialized to output GML. * * <p> * DataProperties for the JCSWriter write(featureSchema,DataProperties) interface:<br> * </p> * <p> * <table border='1' cellspacing='0' cellpadding='4'> * <tr> * <th>Parameter</th><th>Meaning</th> * </tr> * <tr> * <td>OutputFile or DefaultValue</td> * <td>File name for output .xml file</td> * </tr> * <tr> * <td>OutputTemplateFile</td> * <td>File name for GMLOutputTemplate file</td> * </tr> * </table><br> * </p> * NOTE: If OutputTemplateFile is unspecified, one will be auto-created (the JCS format). * <br> * <br> * Programmer details: <br> * <br> * This is usually called as follows:<br> * <pre> * gmlWriter= new GMLWriter(); * gmlWriter.write( DriverProperites); * </pre> * or: * <pre> * gmlWriter.setOutputTemplate( GMLOutputTemplate); * gmlWriter.write( <writer>, <stream name>); * </pre> * <br> * Also, the function "makeOutputTemplate()" is useful for * autogenerating the JCS outputtemplate. * <br> * <br> * * Output will be formed from the OutputTeplate like:<Br> * <br> * <pre> * headerText * ==== This section repeated for each feature * featureText[0] <evaluate codeText[0]> * featureText[1] <evaluate codeText[1]> * ... * featureTextFooter * ==== * footerText * </pre> * */ public class GMLWriter implements JUMPWriter { // Standard tags for the auto-generated outputTemplate. public static String standard_geom = "geometry"; public static String standard_feature = "feature"; public static String standard_featureCollection = "featureCollection"; private GMLOutputTemplate outputTemplate = null; private GMLGeometryWriter geometryWriter = new GMLGeometryWriter(); /** constructor**/ public GMLWriter() { geometryWriter.setLinePrefix(" "); } /** * Main entry function - write the GML file. * @param featureCollection features to write * @param dp specify the 'OuputFile' and 'OuputTemplateFile' */ public void write(FeatureCollection featureCollection, DriverProperties dp) throws IllegalParametersException, Exception { GMLOutputTemplate gmlTemplate; String outputFname; outputFname = dp.getProperty("File"); if (outputFname == null) { outputFname = dp.getProperty("DefaultValue"); } if (outputFname == null) { throw new IllegalParametersException( "call to GMLWRite.write() has DataProperties w/o a OutputFile specified"); } if (dp.getProperty("TemplateFile") == null) { //we're going create the output template gmlTemplate = GMLWriter.makeOutputTemplate(featureCollection.getFeatureSchema()); } else { // load the template java.io.Reader r; r = new FileReader(dp.getProperty("TemplateFile")); gmlTemplate = new GMLOutputTemplate(); gmlTemplate.load(r); r.close(); } //have a template and FC. Write it! setOutputTemplate(gmlTemplate); java.io.Writer w; w = new java.io.BufferedWriter(new java.io.FileWriter(outputFname)); this.write(featureCollection, w); w.close(); } /** * Actual evaluator/writer - you should have already called setOutputTemplate *@param featureCollection features to write *@param writer - where to send the output to */ public void write(FeatureCollection featureCollection, java.io.Writer writer) throws Exception { BufferedWriter buffWriter; Feature f; String pre; String token; if (outputTemplate == null) { throw new Exception( "attempt to write GML w/o specifying the output template"); } buffWriter = new BufferedWriter(writer); buffWriter.write(outputTemplate.headerText); for (Iterator t = featureCollection.iterator(); t.hasNext();) { f = (Feature) t.next(); for (int u = 0; u < outputTemplate.featureText.size(); u++) { String evaled; pre = (String) outputTemplate.featureText.get(u); token = (String) outputTemplate.codingText.get(u); buffWriter.write(pre); evaled = evaluateToken(f, token); if (evaled == null) { evaled = ""; } buffWriter.write(evaled); } buffWriter.write(outputTemplate.featureTextfooter); buffWriter.write("\n"); } buffWriter.write(outputTemplate.footerText); buffWriter.flush(); } /** *Convert an arbitary string into something that will not cause XML to gack. * Ie. convert "<" to "<" *@param s string to safe-ify */ public static String safeXML(String s) { StringBuffer sb = new StringBuffer(s); char c; for (int t = 0; t < sb.length(); t++) { c = sb.charAt(t); if (c == '<') { sb.replace(t, t + 1, "<"); } if (c == '>') { sb.replace(t, t + 1, ">"); } if (c == '&') { sb.replace(t, t + 1, "&"); } if (c == '\'') { sb.replace(t, t + 1, "'"); } if (c == '"') { sb.replace(t, t + 1, """); } } return sb.toString(); } /** * Attaches a GMLOuputTemplate */ public void setOutputTemplate(GMLOutputTemplate ot) { outputTemplate = ot; } /** *takes a token and replaces it with its value (ie. geometry or column) * @param f feature to take geometry or column value from *@token token to evaluate - "column","geometry" or "geometrytype" */ private String evaluateToken(Feature f, String token) throws Exception, ParseException { String column; String cmd; String result; int index; //token = token.toLowerCase(); token = token.trim(); if (!(token.startsWith("=")) || (token.length() < 7)) { throw new ParseException("couldn't understand token '" + token + "' in the output template"); } token = token.substring(1); token = token.trim(); index = token.indexOf(" "); if (index == -1) { cmd = token; } else { cmd = token.substring(0, token.indexOf(" ")); } if (cmd.equalsIgnoreCase("column")) { column = token.substring(6); column = column.trim(); // System.out.println("column = " + column); result = toString(f, column); //need to ensure that the output is XML okay result = safeXML(result); return result; } else if (cmd.equalsIgnoreCase("geometry")) { // MD - testing new GMLGeometryWriter geometryWriter.setMaximumCoordinatesPerLine(1); return geometryWriter.write(f.getGeometry()); //return Geometry2GML(f.getGeometry()); } else if (cmd.equalsIgnoreCase("geometrytype")) { return f.getGeometry().getGeometryType(); } else { throw new ParseException("couldn't understand token '" + token + "' in the output template"); } } protected String toString(Feature f, String column) { Assert.isTrue(f.getSchema().getAttributeType(column) != AttributeType.GEOMETRY); Object attribute = f.getAttribute(column); if (attribute == null) { return ""; } if (attribute instanceof Date) { return format((Date)attribute); } return attribute.toString(); } protected String format(Date date) { return dateFormatter.format(date); } private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd"); /** given a FEatureSchema, make an output template * in the JCS format * @param fcmd input featureSchema */ public static GMLOutputTemplate makeOutputTemplate(FeatureSchema fcmd) { GMLOutputTemplate result; String inputTemplate; int t; String colName; String colText = ""; String colCode = ""; String colHeader = ""; result = new GMLOutputTemplate(); inputTemplate = makeInputTemplate(fcmd); result.setHeaderText( "<?xml version='1.0' encoding='UTF-8'?>\n<JCSDataFile xmlns:gml=\"http://www.opengis.net/gml\" xmlns:xsi=\"http://www.w3.org/2000/10/XMLSchema-instance\" >\n" + inputTemplate + "<" + standard_featureCollection + ">\n"); colText = ""; colHeader = " <" + standard_feature + "> \n"; for (t = 0; t < fcmd.getAttributeCount(); t++) { colName = fcmd.getAttributeName(t); colText = ""; if (t != fcmd.getGeometryIndex()) { //not geometry colText = colHeader + " <property name=\"" + colName + "\">"; colCode = "=column " + colName; colHeader = "</property>\n"; } else { //geometry colText = colHeader + " <" + standard_geom + ">\n"; colCode = "=geometry"; colHeader = " </" + standard_geom + ">\n"; } result.addItem(colText, colCode); } result.setFeatureFooter(colHeader + " </" + standard_feature + ">\n"); result.setFooterText(" </" + standard_featureCollection + ">\n</JCSDataFile>\n"); return result; } /**given a FeatureSchema, make a chunk of XML that represents a valid * GMLInputTemplate for the JCS format. Used by makeOutputTemplate since the * output template includes an inputtemplate. * *@param fcmd the featureSchema to describe */ public static String makeInputTemplate(FeatureSchema fcmd) { String result; int t; result = "<JCSGMLInputTemplate>\n<CollectionElement>" + standard_featureCollection + "</CollectionElement> \n<FeatureElement>" + standard_feature + "</FeatureElement>\n<GeometryElement>" + standard_geom + "</GeometryElement>\n<ColumnDefinitions>\n"; //fill in each of the column defs for (t = 0; t < fcmd.getAttributeCount(); t++) { String colDef; String colName; AttributeType attributeType; colName = fcmd.getAttributeName(t); if (t != fcmd.getGeometryIndex()) { colDef = " <column>\n"; colDef = colDef + " <name>" + colName + "</name>\n"; attributeType = fcmd.getAttributeType(t); colDef = colDef + " <type>" + attributeType + "</type>\n"; colDef = colDef + " <valueElement elementName=\"property\" attributeName=\"name\" attributeValue=\"" + colName + "\"/>\n"; colDef = colDef + " <valueLocation position=\"body\"/>\n"; colDef = colDef + " </column>\n"; result = result + colDef; } } result = result + "</ColumnDefinitions>\n</JCSGMLInputTemplate>\n\n"; return result; } }