- Timestamp:
- 04/10/09 14:41:38 (11 months ago)
- Location:
- tools/routingservice/branches/wrs-2.0
- Files:
-
- 53 added
- 10 modified
-
data/resources.xml (modified) (2 diffs)
-
data/services.xml (modified) (1 diff)
-
data/templates/route/route_gml.st (modified) (2 diffs)
-
lib/acme.jar (added)
-
lib/antlr-2.7.7.jar (added)
-
lib/jdom.jar (added)
-
lib/jts-1.10.jar (added)
-
lib/org (added)
-
lib/org.restlet.jar (added)
-
lib/org/json (added)
-
lib/org/json/CDL.class (added)
-
lib/org/json/CDL.java (added)
-
lib/org/json/Cookie.class (added)
-
lib/org/json/Cookie.java (added)
-
lib/org/json/CookieList.class (added)
-
lib/org/json/CookieList.java (added)
-
lib/org/json/HTTP.class (added)
-
lib/org/json/HTTP.java (added)
-
lib/org/json/HTTPTokener.class (added)
-
lib/org/json/HTTPTokener.java (added)
-
lib/org/json/JSONArray.class (added)
-
lib/org/json/JSONArray.java (added)
-
lib/org/json/JSONException.class (added)
-
lib/org/json/JSONException.java (added)
-
lib/org/json/JSONML.class (added)
-
lib/org/json/JSONML.java (added)
-
lib/org/json/JSONObject$1.class (added)
-
lib/org/json/JSONObject$Null.class (added)
-
lib/org/json/JSONObject.class (added)
-
lib/org/json/JSONObject.java (added)
-
lib/org/json/JSONString.class (added)
-
lib/org/json/JSONString.java (added)
-
lib/org/json/JSONStringer.class (added)
-
lib/org/json/JSONStringer.java (added)
-
lib/org/json/JSONTokener.class (added)
-
lib/org/json/JSONTokener.java (added)
-
lib/org/json/JSONWriter.class (added)
-
lib/org/json/JSONWriter.java (added)
-
lib/org/json/Test$1Obj.class (added)
-
lib/org/json/Test.class (added)
-
lib/org/json/Test.java (added)
-
lib/org/json/XML.class (added)
-
lib/org/json/XML.java (added)
-
lib/org/json/XMLTokener.class (added)
-
lib/org/json/XMLTokener.java (added)
-
lib/postgresql-8.3-604.jdbc4.jar (added)
-
lib/stringtemplate.jar (added)
-
lib/xerces.jar (added)
-
src/WRS.java (modified) (12 diffs)
-
src/geometry (added)
-
src/geometry/Point.java (added)
-
src/handler/PgRoutingHandler.java (modified) (5 diffs)
-
src/handler/ResourceHandler.java (modified) (1 diff)
-
src/model/Resource.java (modified) (1 diff)
-
src/model/Service.java (modified) (4 diffs)
-
src/util/ObjectPool.java (modified) (3 diffs)
-
src/util/ServiceRequest.java (modified) (3 diffs)
-
src/util/format (added)
-
src/util/format/FormatParser.java (added)
-
src/util/format/GMLTemplateFiller.java (added)
-
src/util/format/GeoJSONParser.java (added)
-
src/util/format/JSONParser.java (added)
-
src/util/format/TemplateFiller.java (added)
Legend:
- Unmodified
- Added
- Removed
-
tools/routingservice/branches/wrs-2.0/data/resources.xml
r289 r290 9 9 <parameter name="srid" type="integer" key="srid" value="4301"/> 10 10 <parameter name="units" type="string" key="units" value="dd"/> 11 <parameter name="box" type="double" key="box" value="0.01"/> 12 <parameter name="cost" type="string" key="cost" value="length"/> 11 13 </parameters> 12 14 <services> … … 19 21 <password></password> 20 22 <query> 21 SELECT gid , AsText(the_geom) AS wktFROM22 $function$('_hcc_algo_shooting_pedestrian', $parameters$ )23 SELECT gid as id, length(the_geom) as cost, astext(the_geom) as line FROM 24 $function$('_hcc_algo_shooting_pedestrian', $parameters$, true, true) 23 25 </query> 24 26 </resource> -
tools/routingservice/branches/wrs-2.0/data/services.xml
r287 r290 7 7 <description>Returns a shortest path between points A and B</description> 8 8 <in> 9 <parameter name="startX" type="double" key="x1"/> 10 <parameter name="startY" type="double" key="y1"/> 11 <parameter name="endX" type="double" key="x2"/> 12 <parameter name="endY" type="double" key="y2"/> 13 <parameter name="cost" type="string" key="cost"/> 9 <parameter key="startX" type="double" name="x1"/> 10 <parameter key="startY" type="double" name="y1"/> 11 <parameter key="endX" type="double" name="x2"/> 12 <parameter key="endY" type="double" name="y2"/> 13 <parameter key="cost" type="string" name="cost"/> 14 <parameter key="bbox" type="double" name="box"/> 14 15 </in> 15 16 <out> 16 17 <parameter name="id" type="integer" key="id"/> 17 <parameter name="cost" type=" integer" key="cost"/>18 <parameter name="cost" type="double" key="cost"/> 18 19 <parameter name="line" type="geometry" key="the_geom"/> 19 <parameter name="description" type="string" key="desc"/>20 <parameter name="error" type="string" key="error"/>21 20 </out> 22 21 </service> -
tools/routingservice/branches/wrs-2.0/data/templates/route/route_gml.st
r284 r290 4 4 xsi:schemaLocation="http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd" 5 5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 6 $ edges:{ e|6 $parameters:{ p | 7 7 <gml:featureMember> 8 8 <feature:feature xmlns:feature="http://example.com/feature"> … … 10 10 <gml:LineString> 11 11 <gml:posList> 12 $ e.points:{ p |$p.x$ $p.y$};separator=" "$12 $p.line$ 13 13 </gml:posList> 14 14 </gml:LineString> 15 15 </feature:geometry> 16 <feature:id>$ e.id$</feature:id>16 <feature:id>$p.id$</feature:id> 17 17 </feature:feature> 18 18 </gml:featureMember> -
tools/routingservice/branches/wrs-2.0/src/WRS.java
r289 r290 5 5 import java.util.ArrayList; 6 6 import java.util.Enumeration; 7 import java.util.HashMap; 7 8 import java.util.Hashtable; 8 9 import java.util.Iterator; … … 18 19 import model.Template; 19 20 21 import org.antlr.stringtemplate.StringTemplate; 20 22 import org.restlet.Component; 21 23 import org.restlet.Restlet; … … 24 26 import util.IOHelper; 25 27 import util.Log; 28 import util.ObjectPool; 26 29 import util.Parameter; 27 30 import util.ServiceRequest; … … 29 32 import util.conf.Configuration; 30 33 import util.conf.Configuration.Includes.Include; 34 import util.format.FormatParser; 35 import util.format.GMLTemplateFiller; 36 import util.format.JSONParser; 37 import util.format.TemplateFiller; 31 38 import util.profile.Profiles; 32 39 import util.profile.Profiles.Profile.Resources.Rref; … … 64 71 TEMPLATES, SERVICES, PROFILES, RESOURCES; 65 72 } 73 74 public enum ParameterSources 75 { 76 URL, PROFILE, RESOURCE; 77 } 78 79 public enum Formats 80 { 81 JSON(JSONParser.class), GEOJSON(null), KML(null), GML(null), XLS(null), GEORSS( 82 null), GPX(null); 83 84 private Class<? extends FormatParser> parserType; 85 86 Formats(Class<? extends FormatParser> p) 87 { 88 this.parserType = p; 89 } 90 91 public FormatParser getInstance() throws InstantiationException, 92 IllegalAccessException 93 { 94 return parserType.newInstance(); 95 } 96 } 97 98 public enum Fillers 99 { 100 JSON(null), GEOJSON(null), KML(null), GML(GMLTemplateFiller.class), XLS(null), GEORSS( 101 null), GPX(null); 102 103 private Class<? extends TemplateFiller> fillerType; 104 105 Fillers(Class<? extends TemplateFiller> p) 106 { 107 this.fillerType = p; 108 } 109 110 public TemplateFiller getInstance() throws InstantiationException, 111 IllegalAccessException 112 { 113 return fillerType.newInstance(); 114 } 115 } 66 116 67 117 public WRS(String[] args) throws Exception … … 127 177 handler.setResource(resource); 128 178 129 Vector<Parameter> output = handler 130 .handle(serviceRequest); 179 ArrayList<HashMap<String, Parameter>> output = handler 180 .handle(serviceRequest, log); 181 182 String templateName = serviceRequest.getTemplate().getName(); 183 184 try 185 { 186 TemplateFiller filler = Fillers.valueOf(templateName.toUpperCase()) 187 .getInstance(); 188 result = filler.fillOutputTemplate(serviceRequest, output, 189 handler, log); 190 } 191 // TODO process the error more carefully 192 catch (InstantiationException e) 193 { 194 log.logger.warning("Wrong output format " + templateName); 195 } 196 catch (IllegalAccessException e) 197 { 198 log.logger.warning("Wrong output format " + templateName); 199 } 200 catch (NullPointerException e) 201 { 202 log.logger.warning("Output format " + templateName + " is not supported yet."); 203 } 204 131 205 } 132 206 // TODO process the error more carefully … … 183 257 } 184 258 259 185 260 public ServiceRequest parseURL(Request req) 186 261 throws ArrayIndexOutOfBoundsException, InvalidVersionException, … … 264 339 } 265 340 266 Vector<Parameter> parameters = new Vector<Parameter>();267 341 Iterator<String> ni = form.getNames().iterator(); 268 342 while (ni.hasNext()) … … 270 344 String name = ni.next(); 271 345 String value = form.getValuesMap().get(name); 272 Parameter param = new Parameter(); 273 param.setName(name); 274 param.setKey(name); 275 param.setValue(value); 276 parameters.add(param); 346 347 Parameter p = new Parameter(); 348 p.setSource("url"); 349 p.setName(name); 350 p.setValue(value); 351 request.getParameters().put(name, p); 352 353 try 354 { 355 FormatParser parser = Formats.valueOf(name.toUpperCase()) 356 .getInstance(); 357 Hashtable<String, Parameter> params = parser.parse(value); 358 addParameters(request.getParameters(), params); 359 } 360 // TODO process the error more carefully 361 catch (InstantiationException e) 362 { 363 log.logger.warning("Wrong format " + name); 364 } 365 catch (IllegalAccessException e) 366 { 367 log.logger.warning("Wrong format " + name); 368 } 369 catch (NullPointerException e) 370 { 371 log.logger.warning("Format " + name + " is not supported yet."); 372 } 277 373 } 278 374 279 375 return request; 376 } 377 378 private void addParameters(Hashtable<String, Parameter> parameters, 379 Hashtable<String, Parameter> params) 380 { 381 // TODO check for ability to override parameters 382 parameters.putAll(params); 280 383 } 281 384 … … 359 462 util.resource.Resources.Resource res = li.next(); 360 463 Resource resource = new Resource(res.getName(), res.getType(), 361 res.getUrl(), res.getUser(), res.getPassword()); 464 res.getUrl(), res.getUser(), res.getPassword(), res 465 .getQuery()); 362 466 363 467 ListIterator<Parameter> pi = res.getParameters().getParameter() … … 592 696 { 593 697 Parameter param = outpi.next(); 594 service.get In().put(param.getName(), param);698 service.getOut().put(param.getName(), param); 595 699 } 596 700 … … 644 748 { 645 749 resource = res; 750 addParameters(serviceRequest.getParameters(), res 751 .getParameters()); 646 752 break; 647 753 } -
tools/routingservice/branches/wrs-2.0/src/handler/PgRoutingHandler.java
r289 r290 1 1 package handler; 2 2 3 import geometry.Point; 4 5 import java.math.BigDecimal; 3 6 import java.sql.Connection; 7 import java.sql.ResultSet; 8 import java.sql.SQLException; 9 import java.sql.Statement; 10 import java.util.ArrayList; 11 import java.util.Arrays; 12 import java.util.Enumeration; 13 import java.util.HashMap; 4 14 import java.util.Hashtable; 15 import java.util.Iterator; 5 16 import java.util.Vector; 6 17 7 18 import model.Resource; 19 import model.Service; 20 import util.Log; 8 21 import util.ObjectPool; 9 22 import util.Parameter; … … 13 26 import org.antlr.stringtemplate.StringTemplate; 14 27 15 public class PgRoutingHandler implements ResourceHandler28 public class PgRoutingHandler extends ResourceHandler 16 29 { 17 30 private Resource resource; … … 21 34 public static final String UNITS = "units"; 22 35 36 public static final String FUNCTION = "function"; 37 public static final String PARAMETERS = "parameters"; 38 39 // Arrays in Services enum's elements specify an order of parameters 40 private enum Services 41 { 42 ROUTE 43 { 44 ArrayList getParameters() 45 { 46 String[] p = { "x1", "y1", "x2", "y2", "box", "cost" }; 47 return new ArrayList(Arrays.asList(p)); 48 } 49 }; 50 51 abstract ArrayList getParameters(); 52 } 53 23 54 public PgRoutingHandler() 24 55 { 56 // TODO implement reading functions mapping from a config file 25 57 this.functions = new Hashtable<String, String>(); 26 58 this.functions.put("route", "shootingstar_sp_smart"); … … 29 61 30 62 @Override 31 public Vector<Parameter> handle(ServiceRequest request) 32 { 33 Vector<Parameter> out = new Vector<Parameter>(); 63 public ArrayList<HashMap<String, Parameter>> handleRequest( 64 ServiceRequest request, Log log) 65 { 66 ArrayList<HashMap<String, Parameter>> out = new ArrayList<HashMap<String, Parameter>>(); 34 67 35 68 if (this.resource != null) 36 69 { 37 38 StringTemplate queryTemplate = new StringTemplate(this.resource 39 .getQuery()); 70 System.out.println("<<<<<<< " + this.resource.getQuery() + " from " 71 + this.resource.getName()); 72 73 String query = fillQueryTemplate(this.resource.getQuery(), request 74 .getService(), request.getParameters()); 75 76 System.out.println(">>>>>>> " + query); 77 40 78 ObjectPool<Connection> pool = this.resource.getPool(); 41 79 Connection con = pool.checkOut(); 42 43 //TODO send query and get a result 44 45 pool.checkIn(con); 80 81 if (con != null) 82 { 83 84 // TODO send query and get a result 85 try 86 { 87 Statement stmt = con.createStatement(); 88 ResultSet result = stmt.executeQuery(query); 89 90 while (result.next()) 91 { 92 Iterator<String> keys = request.getService().getOut() 93 .keySet().iterator(); 94 HashMap<String, Parameter> paraMap = new HashMap<String, Parameter>(); 95 while (keys.hasNext()) 96 { 97 String key = keys.next(); 98 Parameter op = new Parameter(); 99 op.setName(key); 100 op.setType(request.getService().getOut().get(key) 101 .getType()); 102 op.setValue(result.getString(key)); 103 104 paraMap.put(key, op); 105 } 106 out.add(paraMap); 107 } 108 109 } 110 catch (SQLException e) 111 { 112 // TODO Auto-generated catch block 113 e.printStackTrace(); 114 } 115 116 pool.checkIn(con); 117 } 118 else 119 { 120 // Connection pool is full 121 log.logger.severe("Connection to " + resource.getUrl() 122 + " failed."); 123 } 46 124 } 47 125 return out; … … 54 132 } 55 133 134 @Override 135 public String fillQueryTemplate(String template, Service service, 136 Hashtable<String, Parameter> parameters) 137 { 138 StringTemplate query = new StringTemplate(template); 139 String function = this.functions.get(service.getName()); 140 query.setAttribute(FUNCTION, function); 141 142 StringBuffer params = new StringBuffer(""); 143 144 // fill parameters 145 // Iterator<String> keys = service.getIn().keySet().iterator(); 146 Iterator<String> keys = Services.valueOf( 147 service.getName().toUpperCase()).getParameters().iterator(); 148 while (keys.hasNext()) 149 { 150 try 151 { 152 String key = keys.next(); 153 if (service.getIn().containsKey(key)) 154 { 155 Parameter p = parameters.get(key); 156 String value = p.getValue(); 157 if (service.getIn().get(key).getType().toLowerCase() 158 .equals(STRING)) 159 { 160 value = "'" + value + "'"; 161 } 162 params.append(value); 163 164 if (keys.hasNext()) 165 params.append(","); 166 } 167 else 168 { 169 // Service doesn't know about the parameter 170 // TODO do something smart here 171 } 172 } 173 catch (NullPointerException e) 174 { 175 // Required parameter is missing! 176 // TODO do something smart here 177 } 178 } 179 180 System.out.println(">>PARAMETERS>>>>> " + params.toString()); 181 query.setAttribute(PARAMETERS, params.toString()); 182 183 return query.toString(); 184 } 185 186 @Override 187 public boolean checkService(Service service) 188 { 189 return this.resource.getServices().containsKey(service.getName()); 190 } 191 192 @Override 193 public ArrayList<Point> parseGeometry(String geom) 194 { 195 ArrayList<Point> points = new ArrayList<Point>(); 196 String[] wkts = parseWKT(geom); 197 198 for (int i = 0; i < wkts.length; i++) 199 { 200 String wkt2[] = wkts[i].split(","); 201 for (int j = 0; j < wkt2.length; j++) 202 { 203 Point point = new Point(); 204 point.setX(new BigDecimal(wkt2[j].split(" ")[0])); 205 point.setY(new BigDecimal(wkt2[j].split(" ")[1])); 206 points.add(point); 207 } 208 209 } 210 return points; 211 } 212 213 /** 214 * Parses a WKT geometry string to an array 215 * 216 * @param resultSet 217 * @return 218 * @throws SQLException 219 */ 220 private String[] parseWKT(String wkt) 221 { 222 String wkts[] = null; 223 224 if (wkt.contains("MULTILINESTRING")) 225 { 226 wkt = wkt.split("MULTILINESTRING\\(\\(")[1].split("\\)\\)")[0]; 227 wkts = wkt.split("\\)\\("); 228 } 229 else if (wkt.contains("POINT")) 230 { 231 wkts = wkt.split("POINT\\(\\(")[1].split("\\)\\)"); 232 } 233 else if (wkt.contains("LINESTRING")) 234 { 235 wkts = wkt.split("LINESTRING\\(\\(")[1].split("\\)\\)"); 236 } 237 else if (wkt.contains("POLYGON")) 238 { 239 wkts = wkt.split("POLYGON\\(\\(")[1].split("\\)\\)"); 240 } 241 242 return wkts; 243 } 244 56 245 } -
tools/routingservice/branches/wrs-2.0/src/handler/ResourceHandler.java
r289 r290 1 1 package handler; 2 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.Hashtable; 3 6 import java.util.Vector; 4 7 8 import exception.InvalidServiceException; 9 import geometry.Point; 10 5 11 import model.Resource; 12 import model.Service; 13 import util.Log; 6 14 import util.Parameter; 7 15 import util.ServiceRequest; 8 16 9 public interfaceResourceHandler17 public abstract class ResourceHandler 10 18 { 11 19 12 public void setResource(Resource resource); 13 public Vector<Parameter> handle(ServiceRequest request); 20 public static final String STRING = "string"; 21 public static final String INTEGER = "integer"; 22 public static final String DOUBLE = "double"; 23 public static final String GEOMETRY = "geometry"; 24 25 public abstract void setResource(Resource resource); 26 27 public abstract boolean checkService(Service service); 28 29 public ArrayList<HashMap<String, Parameter>> handle(ServiceRequest request, 30 Log log) throws InvalidServiceException 31 { 32 if (checkService(request.getService())) 33 { 34 return handleRequest(request, log); 35 } 36 else 37 { 38 throw new InvalidServiceException(request.getService().getName()); 39 } 40 } 41 42 protected abstract ArrayList<HashMap<String, Parameter>> handleRequest( 43 ServiceRequest request, Log log); 44 45 public abstract String fillQueryTemplate(String template, Service service, 46 Hashtable<String, Parameter> parameters); 47 48 public abstract ArrayList<Point> parseGeometry(String geometry); 14 49 15 50 } -
tools/routingservice/branches/wrs-2.0/src/model/Resource.java
r289 r290 55 55 private ObjectPool pool; 56 56 57 public Resource(String name, String type, String url, String user, String password )57 public Resource(String name, String type, String url, String user, String password, String query) 58 58 { 59 59 super(); 60 60 this.name = name; 61 61 this.type = type; 62 this.url = url; 63 this.user = user; 64 this.password = password; 65 this.query = query; 62 66 63 67 try -
tools/routingservice/branches/wrs-2.0/src/model/Service.java
r287 r290 1 1 package model; 2 2 3 import java.util.ArrayList; 3 4 import java.util.Hashtable; 5 import java.util.HashMap; 4 6 5 7 import util.Parameter; … … 11 13 private String description; 12 14 13 private Hashtable<String, Parameter> in; 14 private Hashtable<String, Parameter> out; 15 15 private HashMap<String, Parameter> in; 16 private HashMap<String, Parameter> out; 16 17 17 18 … … 20 21 super(); 21 22 this.name = name; 22 this.in = new Hash table<String, Parameter>();23 this.out = new Hash table<String, Parameter>();23 this.in = new HashMap<String, Parameter>(); 24 this.out = new HashMap<String, Parameter>(); 24 25 } 25 26 … … 54 55 } 55 56 56 public Hash table<String, Parameter> getIn()57 public HashMap<String, Parameter> getIn() 57 58 { 58 59 return in; 59 60 } 60 61 61 public Hash table<String, Parameter> getOut()62 public HashMap<String, Parameter> getOut() 62 63 { 63 64 return out; -
tools/routingservice/branches/wrs-2.0/src/util/ObjectPool.java
r289 r290 12 12 private long expirationTime; 13 13 private Hashtable<T, Long> locked, unlocked; 14 private int capacity; 14 15 15 16 public ObjectPool(String url, String user, String password) … … 19 20 this.password = password; 20 21 21 expirationTime = 30000; // 30 seconds 22 expirationTime = 60000; // 1 minute 23 capacity = 10; 22 24 locked = new Hashtable<T, Long>(); 23 25 unlocked = new Hashtable<T, Long>(); … … 36 38 T t; 37 39 38 if ( unlocked.size() > 0)40 if (locked.size() + unlocked.size() < capacity) 39 41 { 40 Enumeration<T> e = unlocked.keys(); 42 if (unlocked.size() > 0) 43 { 44 Enumeration<T> e = unlocked.keys(); 41 45 42 while (e.hasMoreElements())43 {44 t = e.nextElement();46 while (e.hasMoreElements()) 47 { 48 t = e.nextElement(); 45 49 46 if ((now - unlocked.get(t)) > expirationTime) 47 { 48 // object has expired 49 unlocked.remove(t); 50 expire(t); 51 t = null; 52 } 53 else 54 { 55 if (validate(t)) 50 if ((now - unlocked.get(t)) > expirationTime) 56 51 { 57 unlocked.remove(t); 58 locked.put(t, now); 59 return (t); 60 } 61 else 62 { 63 // object failed validation 52 // object has expired 64 53 unlocked.remove(t); 65 54 expire(t); 66 55 t = null; 67 56 } 57 else 58 { 59 if (validate(t)) 60 { 61 unlocked.remove(t); 62 locked.put(t, now); 63 return (t); 64 } 65 else 66 { 67 // object failed validation 68 unlocked.remove(t); 69 expire(t); 70 t = null; 71 } 72 } 68 73 } 69 74 } 75 76 // no objects available, create a new one 77 t = create(); 78 locked.put(t, now); 79 return (t); 80 81 } 82 else 83 { 84 //TODO Process error 85 return null; 70 86 } 71 87 72 // no objects available, create a new one73 t = create();74 locked.put(t, now);75 return (t);76 88 } 77 89 -
tools/routingservice/branches/wrs-2.0/src/util/ServiceRequest.java
r287 r290 1 1 package util; 2 2 3 import java.util.Hashtable; 3 4 import java.util.Vector; 4 5 … … 12 13 private Service service; 13 14 private Template template; 14 private Vector<Parameter> parameters;15 private Hashtable<String, Parameter> parameters; 15 16 16 17 public ServiceRequest(Profile profile, Service service, Template template) … … 20 21 this.service = service; 21 22 this.template = template; 23 this.parameters = new Hashtable<String, Parameter>(); 22 24 } 23 25 24 public Vector<Parameter> getParameters()26 public Hashtable<String, Parameter> getParameters() 25 27 { 26 28 return parameters; 27 }28 29 public void setParameters(Vector<Parameter> parameters)30 {31 this.parameters = parameters;32 29 } 33 30
