001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.io;
033:
034: import com.vividsolutions.jts.geom.*;
035: import com.vividsolutions.jts.util.Assert;
036:
037: import com.vividsolutions.jump.feature.*;
038:
039: import java.io.BufferedWriter;
040: import java.io.FileReader;
041:
042: import java.lang.reflect.Array;
043:
044: import java.text.SimpleDateFormat;
045: import java.util.Date;
046: import java.util.Iterator;
047:
048: /**
049: * GMLWriter is a {@link JUMPWriter} specialized to output GML.
050: *
051: * <p>
052: * DataProperties for the JCSWriter write(featureSchema,DataProperties) interface:<br>
053: * </p>
054: * <p>
055: * <table border='1' cellspacing='0' cellpadding='4'>
056: * <tr>
057: * <th>Parameter</th><th>Meaning</th>
058: * </tr>
059: * <tr>
060: * <td>OutputFile or DefaultValue</td>
061: * <td>File name for output .xml file</td>
062: * </tr>
063: * <tr>
064: * <td>OutputTemplateFile</td>
065: * <td>File name for GMLOutputTemplate file</td>
066: * </tr>
067: * </table><br>
068: * </p>
069: * NOTE: If OutputTemplateFile is unspecified, one will be auto-created (the JCS format).
070: * <br>
071: * <br>
072: * Programmer details: <br>
073: * <br>
074: * This is usually called as follows:<br>
075: * <pre>
076: * gmlWriter= new GMLWriter();
077: * gmlWriter.write( DriverProperites);
078: * </pre>
079: * or:
080: * <pre>
081: * gmlWriter.setOutputTemplate( GMLOutputTemplate);
082: * gmlWriter.write( <writer>, <stream name>);
083: * </pre>
084: * <br>
085:
086: * Also, the function "makeOutputTemplate()" is useful for
087: * autogenerating the JCS outputtemplate.
088: * <br>
089: * <br>
090: *
091: * Output will be formed from the OutputTeplate like:<Br>
092: * <br>
093: * <pre>
094: * headerText
095: * ==== This section repeated for each feature
096: * featureText[0] <evaluate codeText[0]>
097: * featureText[1] <evaluate codeText[1]>
098: * ...
099: * featureTextFooter
100: * ====
101: * footerText
102: * </pre>
103: *
104: */
105: public class GMLWriter implements JUMPWriter {
106: // Standard tags for the auto-generated outputTemplate.
107: public static String standard_geom = "geometry";
108: public static String standard_feature = "feature";
109: public static String standard_featureCollection = "featureCollection";
110: private GMLOutputTemplate outputTemplate = null;
111: private GMLGeometryWriter geometryWriter = new GMLGeometryWriter();
112:
113: /** constructor**/
114: public GMLWriter() {
115: geometryWriter.setLinePrefix(" ");
116: }
117:
118: /**
119: * Main entry function - write the GML file.
120: * @param featureCollection features to write
121: * @param dp specify the 'OuputFile' and 'OuputTemplateFile'
122: */
123: public void write(FeatureCollection featureCollection,
124: DriverProperties dp) throws IllegalParametersException,
125: Exception {
126: GMLOutputTemplate gmlTemplate;
127: String outputFname;
128:
129: outputFname = dp.getProperty("File");
130:
131: if (outputFname == null) {
132: outputFname = dp.getProperty("DefaultValue");
133: }
134:
135: if (outputFname == null) {
136: throw new IllegalParametersException(
137: "call to GMLWRite.write() has DataProperties w/o a OutputFile specified");
138: }
139:
140: if (dp.getProperty("TemplateFile") == null) {
141: //we're going create the output template
142: gmlTemplate = GMLWriter
143: .makeOutputTemplate(featureCollection
144: .getFeatureSchema());
145: } else {
146: // load the template
147: java.io.Reader r;
148:
149: r = new FileReader(dp.getProperty("TemplateFile"));
150: gmlTemplate = new GMLOutputTemplate();
151: gmlTemplate.load(r);
152: r.close();
153: }
154:
155: //have a template and FC. Write it!
156: setOutputTemplate(gmlTemplate);
157:
158: java.io.Writer w;
159:
160: w = new java.io.BufferedWriter(new java.io.FileWriter(
161: outputFname));
162: this .write(featureCollection, w);
163: w.close();
164: }
165:
166: /**
167: * Actual evaluator/writer - you should have already called setOutputTemplate
168: *@param featureCollection features to write
169: *@param writer - where to send the output to
170: */
171: public void write(FeatureCollection featureCollection,
172: java.io.Writer writer) throws Exception {
173: BufferedWriter buffWriter;
174: Feature f;
175: String pre;
176: String token;
177:
178: if (outputTemplate == null) {
179: throw new Exception(
180: "attempt to write GML w/o specifying the output template");
181: }
182:
183: buffWriter = new BufferedWriter(writer);
184:
185: buffWriter.write(outputTemplate.headerText);
186:
187: for (Iterator t = featureCollection.iterator(); t.hasNext();) {
188: f = (Feature) t.next();
189:
190: for (int u = 0; u < outputTemplate.featureText.size(); u++) {
191: String evaled;
192: pre = (String) outputTemplate.featureText.get(u);
193: token = (String) outputTemplate.codingText.get(u);
194: buffWriter.write(pre);
195: evaled = evaluateToken(f, token);
196:
197: if (evaled == null) {
198: evaled = "";
199: }
200:
201: buffWriter.write(evaled);
202: }
203:
204: buffWriter.write(outputTemplate.featureTextfooter);
205: buffWriter.write("\n");
206: }
207:
208: buffWriter.write(outputTemplate.footerText);
209: buffWriter.flush();
210: }
211:
212: /**
213: *Convert an arbitary string into something that will not cause XML to gack.
214: * Ie. convert "<" to "<"
215: *@param s string to safe-ify
216: */
217: public static String safeXML(String s) {
218: StringBuffer sb = new StringBuffer(s);
219: char c;
220:
221: for (int t = 0; t < sb.length(); t++) {
222: c = sb.charAt(t);
223:
224: if (c == '<') {
225: sb.replace(t, t + 1, "<");
226: }
227:
228: if (c == '>') {
229: sb.replace(t, t + 1, ">");
230: }
231:
232: if (c == '&') {
233: sb.replace(t, t + 1, "&");
234: }
235:
236: if (c == '\'') {
237: sb.replace(t, t + 1, "'");
238: }
239:
240: if (c == '"') {
241: sb.replace(t, t + 1, """);
242: }
243: }
244:
245: return sb.toString();
246: }
247:
248: /**
249: * Attaches a GMLOuputTemplate
250: */
251: public void setOutputTemplate(GMLOutputTemplate ot) {
252: outputTemplate = ot;
253: }
254:
255: /**
256: *takes a token and replaces it with its value (ie. geometry or column)
257: * @param f feature to take geometry or column value from
258: *@token token to evaluate - "column","geometry" or "geometrytype"
259: */
260: private String evaluateToken(Feature f, String token)
261: throws Exception, ParseException {
262: String column;
263: String cmd;
264: String result;
265: int index;
266:
267: //token = token.toLowerCase();
268: token = token.trim();
269:
270: if (!(token.startsWith("=")) || (token.length() < 7)) {
271: throw new ParseException("couldn't understand token '"
272: + token + "' in the output template");
273: }
274:
275: token = token.substring(1);
276: token = token.trim();
277: index = token.indexOf(" ");
278:
279: if (index == -1) {
280: cmd = token;
281: } else {
282: cmd = token.substring(0, token.indexOf(" "));
283: }
284:
285: if (cmd.equalsIgnoreCase("column")) {
286: column = token.substring(6);
287: column = column.trim();
288:
289: // System.out.println("column = " + column);
290: result = toString(f, column);
291:
292: //need to ensure that the output is XML okay
293: result = safeXML(result);
294:
295: return result;
296: } else if (cmd.equalsIgnoreCase("geometry")) {
297: // MD - testing new GMLGeometryWriter
298: geometryWriter.setMaximumCoordinatesPerLine(1);
299:
300: return geometryWriter.write(f.getGeometry());
301:
302: //return Geometry2GML(f.getGeometry());
303: } else if (cmd.equalsIgnoreCase("geometrytype")) {
304: return f.getGeometry().getGeometryType();
305: } else {
306: throw new ParseException("couldn't understand token '"
307: + token + "' in the output template");
308: }
309: }
310:
311: protected String toString(Feature f, String column) {
312: Assert
313: .isTrue(f.getSchema().getAttributeType(column) != AttributeType.GEOMETRY);
314: Object attribute = f.getAttribute(column);
315: if (attribute == null) {
316: return "";
317: }
318: if (attribute instanceof Date) {
319: return format((Date) attribute);
320: }
321: return attribute.toString();
322: }
323:
324: protected String format(Date date) {
325: return dateFormatter.format(date);
326: }
327:
328: private SimpleDateFormat dateFormatter = new SimpleDateFormat(
329: "yyyy-MM-dd");
330:
331: /** given a FEatureSchema, make an output template
332: * in the JCS format
333: * @param fcmd input featureSchema
334: */
335: public static GMLOutputTemplate makeOutputTemplate(
336: FeatureSchema fcmd) {
337: GMLOutputTemplate result;
338: String inputTemplate;
339: int t;
340: String colName;
341: String colText = "";
342: String colCode = "";
343: String colHeader = "";
344:
345: result = new GMLOutputTemplate();
346:
347: inputTemplate = makeInputTemplate(fcmd);
348:
349: result
350: .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"
351: + inputTemplate
352: + "<"
353: + standard_featureCollection + ">\n");
354:
355: colText = "";
356: colHeader = " <" + standard_feature + "> \n";
357:
358: for (t = 0; t < fcmd.getAttributeCount(); t++) {
359: colName = fcmd.getAttributeName(t);
360: colText = "";
361:
362: if (t != fcmd.getGeometryIndex()) {
363: //not geometry
364: colText = colHeader + " <property name=\""
365: + colName + "\">";
366: colCode = "=column " + colName;
367: colHeader = "</property>\n";
368: } else {
369: //geometry
370: colText = colHeader + " <" + standard_geom
371: + ">\n";
372: colCode = "=geometry";
373: colHeader = " </" + standard_geom + ">\n";
374: }
375:
376: result.addItem(colText, colCode);
377: }
378:
379: result.setFeatureFooter(colHeader + " </"
380: + standard_feature + ">\n");
381: result.setFooterText(" </" + standard_featureCollection
382: + ">\n</JCSDataFile>\n");
383:
384: return result;
385: }
386:
387: /**given a FeatureSchema, make a chunk of XML that represents a valid
388: * GMLInputTemplate for the JCS format. Used by makeOutputTemplate since the
389: * output template includes an inputtemplate.
390: *
391: *@param fcmd the featureSchema to describe
392: */
393: public static String makeInputTemplate(FeatureSchema fcmd) {
394: String result;
395: int t;
396:
397: result = "<JCSGMLInputTemplate>\n<CollectionElement>"
398: + standard_featureCollection
399: + "</CollectionElement> \n<FeatureElement>"
400: + standard_feature
401: + "</FeatureElement>\n<GeometryElement>"
402: + standard_geom
403: + "</GeometryElement>\n<ColumnDefinitions>\n";
404:
405: //fill in each of the column defs
406: for (t = 0; t < fcmd.getAttributeCount(); t++) {
407: String colDef;
408: String colName;
409: AttributeType attributeType;
410:
411: colName = fcmd.getAttributeName(t);
412:
413: if (t != fcmd.getGeometryIndex()) {
414: colDef = " <column>\n";
415:
416: colDef = colDef + " <name>" + colName
417: + "</name>\n";
418: attributeType = fcmd.getAttributeType(t);
419: colDef = colDef + " <type>" + attributeType
420: + "</type>\n";
421:
422: colDef = colDef
423: + " <valueElement elementName=\"property\" attributeName=\"name\" attributeValue=\""
424: + colName + "\"/>\n";
425: colDef = colDef
426: + " <valueLocation position=\"body\"/>\n";
427:
428: colDef = colDef + " </column>\n";
429:
430: result = result + colDef;
431: }
432: }
433:
434: result = result
435: + "</ColumnDefinitions>\n</JCSGMLInputTemplate>\n\n";
436:
437: return result;
438: }
439: }
|