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:
033: package com.vividsolutions.jump.io;
034:
035: import java.io.BufferedReader;
036: import java.util.ArrayList;
037:
038: /**
039: * This class provides a parser for reading GMLOuputTemplates. This does NOT contain an evaluator for <br>
040: * taking the parsed GMLOutputTemplate and a featureCollection and producing properly <bR>
041: * formatted output GML. Thats the {@link GMLWriter} class.<br>
042: *<br>
043: *<br>
044: * Most people will just use the load() function, but there is support for directly <br>
045: *constructing/modifying one. Your best bet for hand creating one is to make a String that <br>
046: *contains a GMLOutputTemplate then make a java.io.StringReader and passing it to the load()<br>
047: *method. <br>
048: *The valuators (things that look like "<%") are:<br>
049: * <%=GEOMETRY> - replace this with a GML represention of the geometry<br>
050: * <%=COLUMN <column name>> - replace this with the corresponding JCS column value <br>
051: * <%FEATURE> - this marks the start of a feature in the OutputTemplate <Br>
052: * <%ENDFEATURE> - This marks the end of a feature in the OutputTempalte <br>
053: *<br>
054: *<br>
055: *Here's an example of how a simple GMLOutputTemplate is parsed by this class:
056: *for input like:
057: *<br>
058: *<?xml version='1.0' encoding='UTF-8'?> <br>
059: *<dataset...> <br>
060: *<schemaFeatures> <br>
061: *<gml:featureMember> <br>
062: *<% FEATURE %> <br>
063: *<Feature> <br>
064: *<featureType> <%=COLUMN featureType %> </featureType> <br>
065: *<property fme:name="ID"> <%=COLUMN ID %> </property> <br>
066: *<property fme:name="SITE"> <%=COLUMN SITE %> </property> <br>
067: *<property fme:name="gml2_coordsys"></property> <br>
068: *<gml:pointProperty> <br>
069: * <%=GEOMETRY %> <br>
070: *</gml:pointProperty> <br>
071: *</Feature> <br>
072: *<% ENDFEATURE %> <br>
073: *</gml:featureMember> <br>
074: *</dataFeatures> <br>
075: *</dataset> <br>
076: * <br>
077: * <br>
078: * headeText = <br>
079: * <br>
080: *<?xml version='1.0' encoding='UTF-8'?> <br>
081: *<dataset...> <br>
082: *<schemaFeatures> <br>
083: *<gml:featureMember> <br>
084: * <br>
085: * footerText = <br>
086: *</gml:featureMember> <br>
087: *</dataFeatures> <br>
088: *</dataset> <br>
089: * <br>
090: * <br>
091: *featureText[0] = <br>
092: *<Feature> <br>
093: *<featureType> <br>
094: * <br>
095: * codingText[0] = "=COLUMN featureTtype" <br>
096: * <br>
097: *featureText[1] = <br>
098: *</property> <br>
099: *<property fme:name="SITE"> <br>
100: * <br>
101: *codingText[1] = "=COLUMN ID" <br>
102: **/
103: public class GMLOutputTemplate {
104: String headerText;
105: String footerText;
106: String AllFeatureText;
107: ArrayList featureText = new ArrayList();
108: ArrayList codingText = new ArrayList();
109: String featureTextfooter;
110: int lineNumber = 0;
111: String streamName;
112:
113: /** constructor**/
114: public GMLOutputTemplate() {
115: }
116:
117: /** sets the HeaderText for the Outputtemplate */
118: public void setHeaderText(String text) {
119: headerText = text;
120: }
121:
122: /** sets the FooterText for the Outputtemplate */
123: public void setFooterText(String text) {
124: footerText = text;
125: }
126:
127: /** sets the Footer text for the bottom of the feature*/
128: public void setFeatureFooter(String text) {
129: featureTextfooter = text;
130: }
131:
132: /**
133: *for input like : <br>
134: * <feature> <PROPERTY type=name> <%=NAME></property> <PROPERTY type=address> <%=ADDRESS> </property><br>
135: *<br>
136: * use addItem("<feature> <PROPERTY type=name>","=NAME")<br>
137: * addItem("</property> <PROPERTY type=address>", "%=ADDRESS")<br>
138: *
139: **/
140: public void addItem(String header, String coding) {
141: featureText.add(header);
142: codingText.add(coding);
143: }
144:
145: /**
146: * Calls the main load() method with the stream name as "Unknown Stream"
147: */
148: public void load(java.io.Reader r) throws Exception {
149: load(r, "Unknown Stream");
150: }
151:
152: /**
153: * Gets a line from the input Stream
154: */
155: private String getLine(BufferedReader br) throws Exception {
156: lineNumber++;
157:
158: return br.readLine();
159: }
160:
161: /**
162: *Main function - parse a GMLOuputTemplate.
163: *@param r actual reader to read from
164: *@param readerName name of the stream (for error reporting)
165: */
166: public void load(java.io.Reader r, String readerName)
167: throws Exception {
168: int index;
169: int index2;
170: String line;
171: boolean keepgoing;
172: String token;
173: String textAccum;
174: boolean justFoundTag = false;
175: BufferedReader buffRead = new BufferedReader(r);
176:
177: streamName = readerName;
178:
179: // find the header
180: headerText = "";
181: keepgoing = true;
182: line = "";
183: index = 0;
184:
185: while (keepgoing && ((line = getLine(buffRead)) != null)) {
186: if ((index = line.indexOf("<%")) != -1) {
187: //found a "<%" tag, look for the "%>" tag
188: index2 = line.indexOf("%>", index);
189:
190: if (index2 == -1) {
191: throw new ParseException(
192: "While trying to find the GML output header, found a <%, but no %>",
193: streamName, lineNumber, index);
194: }
195:
196: token = line.substring(index + 2, index2);
197: token = token.trim();
198:
199: if (!(token.equalsIgnoreCase("FEATURE"))) {
200: throw new ParseException(
201: "While trying to find the GML output header, found a <%..%> that isnt a <%FEATURE%>",
202: streamName, lineNumber, index);
203: }
204:
205: keepgoing = false;
206: headerText = headerText + line.substring(0, index);
207: line = line.substring(index2 + 2);
208: } else {
209: headerText = headerText + line + "\n";
210: }
211: }
212:
213: if (line == null) {
214: throw new ParseException(
215: "Unexpected EOF while looking for header",
216: streamName, lineNumber, index);
217: }
218:
219: //find the feature text
220: AllFeatureText = "";
221: keepgoing = true;
222: textAccum = "";
223:
224: while (keepgoing) {
225: if ((index = line.indexOf("<%")) != -1) {
226: //found a "<%" tag, look for the "%>" tag
227: index2 = line.indexOf("%>", index);
228:
229: if (index2 == -1) {
230: throw new ParseException(
231: "While looking at the GML feature text, found a <%, but no %>",
232: streamName, lineNumber, index);
233: }
234:
235: token = line.substring(index + 2, index2).trim();
236:
237: if (token.equalsIgnoreCase("ENDFEATURE")) {
238: keepgoing = false;
239: AllFeatureText = AllFeatureText
240: + line.substring(0, index);
241: featureTextfooter = textAccum
242: + line.substring(0, index);
243: line = line.substring(index2 + 2);
244: } else {
245: //handle a part of the feature spec\
246: if (!(validop(token))) {
247: throw new ParseException(
248: "invalid token in <%..%> :" + token,
249: streamName, lineNumber, index);
250: }
251:
252: justFoundTag = true;
253:
254: String pre = textAccum + line.substring(0, index);
255: textAccum = line.substring(index2 + 2);
256: featureText.add(pre);
257: codingText.add(token);
258: }
259: }
260:
261: if (keepgoing) {
262: AllFeatureText = AllFeatureText + line + "\n";
263:
264: if (!(justFoundTag)) {
265: textAccum = textAccum + line + "\n";
266: } else {
267: justFoundTag = false; //textAccum already has a portion of line in it.
268: textAccum = textAccum + "\n";
269: }
270:
271: line = getLine(buffRead);
272:
273: if (line == null) {
274: throw new ParseException(
275: "Unexpected EOF while looking for feature",
276: streamName, lineNumber, index);
277: }
278: }
279: }
280:
281: // grab the footer
282: footerText = line;
283:
284: while ((line = getLine(buffRead)) != null) {
285: footerText = footerText + line + "\n";
286: }
287:
288: //System.out.println(this.asString());
289: }
290:
291: /**
292: * For debugging - return a human readable version of the parsed outputtemplate
293: */
294: public String asString() {
295: String result;
296: int t;
297:
298: result = headerText
299: + "\n--------------------------------------\n";
300:
301: for (t = 0; t < featureText.size(); t++) {
302: result = result + featureText.get(t) + "<%"
303: + codingText.get(t) + "%>";
304: }
305:
306: result = result + featureTextfooter;
307: result = result + "\n--------------------------------------\n";
308: result = result + footerText;
309:
310: return result;
311: }
312:
313: /**
314: * returns true if the intput string is a valid op (for error checking)
315: *@param op test string that should contain an op
316: */
317: private boolean validop(String op) {
318: String op2;
319:
320: op2 = new String(op);
321: op2 = op2.trim();
322: op2 = op2.toLowerCase();
323:
324: if (!(op2.startsWith("=")) || (op2.length() < 2)) {
325: return false;
326: }
327:
328: op2 = op2.substring(1);
329: op2 = op2.trim();
330:
331: return (op2.startsWith("column") || op2.startsWith("geometry"));
332: }
333: }
|