<commit_before>package btools.server.request;

import btools.router.OsmNodeNamed;
import btools.router.OsmNogoPolygon;
import btools.router.OsmTrack;
import btools.router.RoutingContext;
import btools.server.ServiceContext;
import java.io.BufferedWriter;
import java.io.File;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * URL query parameter handler for web and standalone server. Supports all
 * BRouter features without restrictions.
 *
 * Parameters:
 *
 * lonlats = lon,lat|... (unlimited list of lon,lat waypoints separated by |)
 * nogos = lon,lat,radius|... (optional, radius in meters)
 * profile = profile file name without .brf
 * alternativeidx = [0|1|2|3] (optional, default 0)
 * format = [kml|gpx|geojson] (optional, default gpx)
 *
 * Example URLs:
 * {@code http://localhost:17777/brouter?lonlats=8.799297,49.565883|8.811764,49.563606&nogos=&profile=trekking&alternativeidx=0&format=gpx}
 * {@code http://localhost:17777/brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml}
 *
 */
public class ServerHandler extends RequestHandler {

  private RoutingContext rc;

  public ServerHandler( ServiceContext serviceContext, HashMap<String, String> params )
  {
    super( serviceContext, params );
  }

  @Override
  public RoutingContext readRoutingContext()
  {
    rc = new RoutingContext();
    rc.memoryclass = 128;

    String profile = params.get( "profile" );
    // when custom profile replace prefix with directory path
    if ( profile.startsWith( ProfileUploadHandler.CUSTOM_PREFIX ) )
    {
      String customProfile = profile.substring( ProfileUploadHandler.CUSTOM_PREFIX.length() );
      profile = new File( serviceContext.customProfileDir, customProfile ).getPath();
    }
    else if ( profile.startsWith( ProfileUploadHandler.SHARED_PREFIX ) )
    {
      String customProfile = profile.substring( ProfileUploadHandler.SHARED_PREFIX.length() );
      profile = new File( serviceContext.sharedProfileDir, customProfile ).getPath();
    }
    rc.localFunction = profile;

    rc.setAlternativeIdx(Integer.parseInt(params.get( "alternativeidx" )));

    List<OsmNodeNamed> nogoList = readNogoList();
    List<OsmNodeNamed> nogoPolygonsList = readNogoPolygons();

    if ( nogoList != null )
    {
      RoutingContext.prepareNogoPoints( nogoList );
      rc.nogopoints = nogoList;
    }

    if (rc.nogopoints == null)
    {
      rc.nogopoints = nogoPolygonsList;
    }
    else if ( nogoPolygonsList != null )
    {
      rc.nogopoints.addAll(nogoPolygonsList);
    }

    return rc;
  }

  @Override
  public List<OsmNodeNamed> readWayPointList()
  {
    // lon,lat|...
    String lonLats = params.get( "lonlats" );
    if (lonLats == null) throw new IllegalArgumentException( "lonlats parameter not set" );

    String[] coords = lonLats.split("\\|");
    if (coords.length < 2)
      throw new IllegalArgumentException( "we need two lat/lon points at least!" );

    List<OsmNodeNamed> wplist = new ArrayList<OsmNodeNamed>();
    for (int i = 0; i < coords.length; i++)
    {
      String[] lonLat = coords[i].split(",");
      if (lonLat.length < 2)
        throw new IllegalArgumentException( "we need two lat/lon points at least!" );
      wplist.add( readPosition( lonLat[0], lonLat[1], "via" + i ) );
    }

    wplist.get(0).name = "from";
    wplist.get(wplist.size()-1).name = "to";

    return wplist;
  }

  @Override
  public String formatTrack(OsmTrack track)
  {
    String result;
    // optional, may be null
    String format = params.get( "format" );
    String trackName = params.get( "trackname" );
    if (trackName != null) {
      track.name = trackName;
    }

    if (format == null || "gpx".equals(format))
    {
      result = track.formatAsGpx();
    }
    else if ("kml".equals(format))
    {
      result = track.formatAsKml();
    }
    else if ("geojson".equals(format))
    {
      result = track.formatAsGeoJson();
    }
    else if ("csv".equals(format))
    {
      try
      {
        StringWriter sw = new StringWriter();
        BufferedWriter bw = new BufferedWriter(sw);
        track.writeMessages( bw, rc );
        return sw.toString();
      }
      catch (Exception ex)
      {
        return "Error: " + ex.getMessage();
      }
    }
    else {
      System.out.println("unknown track format '" + format + "', using default");
      result = track.formatAsGpx();
    }

    return result;
  }

  @Override
  public String getMimeType()
  {
    // default
    String result = "text/plain";

    // optional, may be null
    String format = params.get( "format" );
    if ( format != null )
    {
      if ( "gpx".equals( format ) )
      {
        result = "application/gpx+xml";
      }
      else if ( "kml".equals( format ) )
      {
        result = "application/vnd.google-earth.kml+xml";
      }
      else if ( "geojson".equals( format ) )
      {
        result = "application/vnd.geo+json";
      }
      else if ( "csv".equals( format ) )
      {
        result = "text/tab-separated-values";
      }
    }

    return result;
  }

  @Override
  public String getFileName()
  {
    String fileName = null;
    String format = params.get( "format" );

    if ( format != null )
    {
      fileName = ( params.get( "trackname" ) == null ? "brouter" : params.get( "trackname" ).replaceAll("[^a-zA-Z0-9 \\._\\-]+", "") ) + "." + format;
    }

    return fileName;
  }

  private static OsmNodeNamed readPosition( String vlon, String vlat, String name )
  {
    if ( vlon == null ) throw new IllegalArgumentException( "lon " + name + " not found in input" );
    if ( vlat == null ) throw new IllegalArgumentException( "lat " + name + " not found in input" );

    return readPosition(Double.parseDouble( vlon ), Double.parseDouble( vlat ), name);
  }

  private static OsmNodeNamed readPosition( double lon, double lat, String name )<commit_msg>remove special characters in trackname<commit_after>package btools.server.request;

import btools.router.OsmNodeNamed;
import btools.router.OsmNogoPolygon;
import btools.router.OsmTrack;
import btools.router.RoutingContext;
import btools.server.ServiceContext;
import java.io.BufferedWriter;
import java.io.File;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * URL query parameter handler for web and standalone server. Supports all
 * BRouter features without restrictions.
 *
 * Parameters:
 *
 * lonlats = lon,lat|... (unlimited list of lon,lat waypoints separated by |)
 * nogos = lon,lat,radius|... (optional, radius in meters)
 * profile = profile file name without .brf
 * alternativeidx = [0|1|2|3] (optional, default 0)
 * format = [kml|gpx|geojson] (optional, default gpx)
 *
 * Example URLs:
 * {@code http://localhost:17777/brouter?lonlats=8.799297,49.565883|8.811764,49.563606&nogos=&profile=trekking&alternativeidx=0&format=gpx}
 * {@code http://localhost:17777/brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml}
 *
 */
public class ServerHandler extends RequestHandler {

  private RoutingContext rc;

  public ServerHandler( ServiceContext serviceContext, HashMap<String, String> params )
  {
    super( serviceContext, params );
  }

  @Override
  public RoutingContext readRoutingContext()
  {
    rc = new RoutingContext();
    rc.memoryclass = 128;

    String profile = params.get( "profile" );
    // when custom profile replace prefix with directory path
    if ( profile.startsWith( ProfileUploadHandler.CUSTOM_PREFIX ) )
    {
      String customProfile = profile.substring( ProfileUploadHandler.CUSTOM_PREFIX.length() );
      profile = new File( serviceContext.customProfileDir, customProfile ).getPath();
    }
    else if ( profile.startsWith( ProfileUploadHandler.SHARED_PREFIX ) )
    {
      String customProfile = profile.substring( ProfileUploadHandler.SHARED_PREFIX.length() );
      profile = new File( serviceContext.sharedProfileDir, customProfile ).getPath();
    }
    rc.localFunction = profile;

    rc.setAlternativeIdx(Integer.parseInt(params.get( "alternativeidx" )));

    List<OsmNodeNamed> nogoList = readNogoList();
    List<OsmNodeNamed> nogoPolygonsList = readNogoPolygons();

    if ( nogoList != null )
    {
      RoutingContext.prepareNogoPoints( nogoList );
      rc.nogopoints = nogoList;
    }

    if (rc.nogopoints == null)
    {
      rc.nogopoints = nogoPolygonsList;
    }
    else if ( nogoPolygonsList != null )
    {
      rc.nogopoints.addAll(nogoPolygonsList);
    }

    return rc;
  }

  @Override
  public List<OsmNodeNamed> readWayPointList()
  {
    // lon,lat|...
    String lonLats = params.get( "lonlats" );
    if (lonLats == null) throw new IllegalArgumentException( "lonlats parameter not set" );

    String[] coords = lonLats.split("\\|");
    if (coords.length < 2)
      throw new IllegalArgumentException( "we need two lat/lon points at least!" );

    List<OsmNodeNamed> wplist = new ArrayList<OsmNodeNamed>();
    for (int i = 0; i < coords.length; i++)
    {
      String[] lonLat = coords[i].split(",");
      if (lonLat.length < 2)
        throw new IllegalArgumentException( "we need two lat/lon points at least!" );
      wplist.add( readPosition( lonLat[0], lonLat[1], "via" + i ) );
    }

    wplist.get(0).name = "from";
    wplist.get(wplist.size()-1).name = "to";

    return wplist;
  }

  @Override
  public String formatTrack(OsmTrack track)
  {
    String result;
    // optional, may be null
    String format = params.get( "format" );
    String trackName = getTrackName();
    if (trackName != null) {
      track.name = trackName;
    }

    if (format == null || "gpx".equals(format))
    {
      result = track.formatAsGpx();
    }
    else if ("kml".equals(format))
    {
      result = track.formatAsKml();
    }
    else if ("geojson".equals(format))
    {
      result = track.formatAsGeoJson();
    }
    else if ("csv".equals(format))
    {
      try
      {
        StringWriter sw = new StringWriter();
        BufferedWriter bw = new BufferedWriter(sw);
        track.writeMessages( bw, rc );
        return sw.toString();
      }
      catch (Exception ex)
      {
        return "Error: " + ex.getMessage();
      }
    }
    else {
      System.out.println("unknown track format '" + format + "', using default");
      result = track.formatAsGpx();
    }

    return result;
  }

  @Override
  public String getMimeType()
  {
    // default
    String result = "text/plain";

    // optional, may be null
    String format = params.get( "format" );
    if ( format != null )
    {
      if ( "gpx".equals( format ) )
      {
        result = "application/gpx+xml";
      }
      else if ( "kml".equals( format ) )
      {
        result = "application/vnd.google-earth.kml+xml";
      }
      else if ( "geojson".equals( format ) )
      {
        result = "application/vnd.geo+json";
      }
      else if ( "csv".equals( format ) )
      {
        result = "text/tab-separated-values";
      }
    }

    return result;
  }

  @Override
  public String getFileName()
  {
    String fileName = null;
    String format = params.get( "format" );
    String trackName = getTrackName();

    if ( format != null )
    {
      fileName = ( trackName == null ? "brouter" : trackName ) + "." + format;
    }

    return fileName;
  }

  private String getTrackName()
  {
    return params.get( "trackname" ) == null ? null : params.get( "trackname" ).replaceAll("[^a-zA-Z0-9 \\._\\-]+", "");
  }

  private static OsmNodeNamed readPosition( String vlon, String vlat, String name )
  {
    if ( vlon == null ) throw new IllegalArgumentException( "lon " + name + " not found in input" );
    if ( vlat == null ) throw new IllegalArgumentException( "lat " + name + " not found in input" );

    return readPosition(Double.parseDouble( vlon ), Double.parseDouble( vlat ), name);
  }

  private static OsmNodeNamed readPosition( double lon, double lat, String name )
{'fraction_non_alphanumeric': 0.13338849813818784, 'fraction_numerical': 0.01406702523789822, 'mean_word_length': 2.9342447916666665, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '679632', 'n_tokens_mistral': 3984, 'n_tokens_neox': 3873, 'n_words': 892}