code: /*******************************************************************************
   * Copyright (c) 2015 Voyager Search and MITRE
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Apache License, Version 2.0 which
   * accompanies this distribution and is available at
   *    http://www.apache.org/licenses/LICENSE-2.0.txt
   ******************************************************************************/
  
  package org.locationtech.spatial4j.io;
  
  import java.io.IOException;
  import java.io.Reader;
  import java.text.ParseException;
  import java.util.StringTokenizer;
  
  import org.locationtech.spatial4j.context.SpatialContext;
  import org.locationtech.spatial4j.context.SpatialContextFactory;
  import org.locationtech.spatial4j.exception.InvalidShapeException;
  import org.locationtech.spatial4j.shape.Point;
  import org.locationtech.spatial4j.shape.Shape;
  
  /**
   * Reads a shape from the old format.
   * <ul>
   *   <li>Point: X Y
   *   <br> 1.23 4.56
   *   </li>
   *   <li>Rect: XMin YMin XMax YMax
   *   <br> 1.23 4.56 7.87 4.56
   *   </li>
   *   <li>{CIRCLE} '(' {POINT} {DISTANCE} ')' <br>
   *   CIRCLE is "CIRCLE" or "Circle" (no other case), and POINT is "X Y" order pair of doubles, or
   *   "Y,X" (lat,lon) pair of doubles, and DISTANCE is "d=RADIUS" or "distance=RADIUS" where RADIUS
   *   is a double that is the distance radius in degrees.
   *   </li>
   * </ul>
   */
  @Deprecated
  public class LegacyShapeReader implements ShapeReader {
  
    final SpatialContext ctx;
  
    public LegacyShapeReader(SpatialContext ctx, SpatialContextFactory factory) {
      this.ctx = ctx;
    }
  
  
    /** Reads the shape specification as defined in the class javadocs. If the first character is
     * a letter but it doesn't complete out "Circle" or "CIRCLE" then this method returns null,
     * offering the caller the opportunity to potentially try additional parsing.
     * If the first character is not a letter then it's assumed to be a point or rectangle. If that
     * doesn't work out then an {@link org.locationtech.spatial4j.exception.InvalidShapeException} is thrown.
     */
    public static Shape readShapeOrNull(String str, SpatialContext ctx) throws InvalidShapeException {
      if (str == null || str.length() == 0) {
        throw new InvalidShapeException(str);
      }
  
      if (Character.isLetter(str.charAt(0))) {
        if (str.startsWith("Circle(") || str.startsWith("CIRCLE(")) {
          int idx = str.lastIndexOf(')');
          if (idx > 0) {
            String body = str.substring("Circle(".length(), idx);
            StringTokenizer st = new StringTokenizer(body, " ");
            String token = st.nextToken();
            Point pt;
            if (token.indexOf(',') != -1) {
              pt = readLatCommaLonPoint(token, ctx);
            } else {
              double x = Double.parseDouble(token);
              double y = Double.parseDouble(st.nextToken());
              pt = ctx.makePoint(x, y);
            }
            Double d = null;
  
            String arg = st.nextToken();
            idx = arg.indexOf('=');
            if (idx > 0) {
              String k = arg.substring(0, idx);
              if (k.equals("d") || k.equals("distance")) {
                d = Double.parseDouble(arg.substring(idx + 1));
              } else {
                throw new InvalidShapeException("unknown arg: " + k + " :: " + str);
              }
            } else {
              d = Double.parseDouble(arg);
            }
            if (st.hasMoreTokens()) {
              throw new InvalidShapeException("Extra arguments: " + st.nextToken() + " :: " + str);
            }
            if (d == null) {
              throw new InvalidShapeException("Missing Distance: " + str);
            }
            //NOTE: we are assuming the units of 'd' is the same as that of the spatial context.
            return ctx.makeCircle(pt, d);
          }
        }
        return null;//caller has opportunity to try other parsing
      }
  
      if (str.indexOf(',') != -1)
        return readLatCommaLonPoint(str, ctx);
      StringTokenizer st = new StringTokenizer(str, " ");
      double p0 = Double.parseDouble(st.nextToken());
      double p1 = Double.parseDouble(st.nextToken());
      if (st.hasMoreTokens()) {
        double p2 = Double.parseDouble(st.nextToken());
        double p3 = Double.parseDouble(st.nextToken());
        if (st.hasMoreTokens())
          throw new InvalidShapeException("Only 4 numbers supported (rect) but found more: " + str);
        return ctx.makeRectangle(p0, p2, p1, p3);
      }
      return ctx.makePoint(p0, p1);
    }
  
    /** Reads geospatial latitude then a comma then longitude. */
    private static Point readLatCommaLonPoint(String value, SpatialContext ctx) throws InvalidShapeException {
      double[] latLon = ParseUtils.parseLatitudeLongitude(value);
      return ctx.makePoint(latLon[1], latLon[0]);
    }
    
    //-------
  
    @Override
    public String getFormatName() {
      return ShapeIO.LEGACY;
    }
  
    @Override
    public Shape read(Object value) throws IOException, ParseException, InvalidShapeException {
      Shape shape = readShapeOrNull(value.toString(), ctx);
      if(shape==null) {
        throw new ParseException("unable to read shape: "+value, 0);
      }
      return readShapeOrNull(value.toString(), ctx);
    }
  
    @Override
    public Shape readIfSupported(Object value) throws InvalidShapeException {
      return readShapeOrNull(value.toString(), ctx);
    }
  
    @Override
    public Shape read(Reader reader) throws IOException, ParseException, InvalidShapeException {
      return read(WKTReader.readString(reader));
    }
  }
  