/* This file is part of RouteConverter. RouteConverter is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. RouteConverter is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RouteConverter; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Copyright (C) 2007 Christian Pesch. All Rights Reserved. */ package slash.navigation.tour; import slash.navigation.base.IniFileFormat; import slash.navigation.base.ParserContext; import slash.navigation.base.RouteCharacteristics; import slash.navigation.common.NavigationPosition; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.util.Arrays.asList; import static java.util.Arrays.sort; import static slash.common.io.Transfer.*; import static slash.navigation.base.RouteCharacteristics.Waypoints; /** * Reads and writes Falk Navigator (.tour) files. * * @author Christian Pesch */ public class TourFormat extends IniFileFormat<TourRoute> { private static final String TOUR_TITLE = "TOUR"; private static final String HOME_TITLE = "HOME"; private static final String NAME = "Name"; private static final String ZIPCODE = "ZipCode"; private static final String CITY = "City"; private static final String STREET = "Street"; private static final String HOUSENO = "HouseNo"; private static final String LONGITUDE = "Longitude"; private static final String LATITUDE = "Latitude"; static final String POSITION_IN_LIST = "PositionInList"; static final String EXTEND_ROUTE = "ExtendRoute"; static final String CLASS = "Class"; static final String ASSEMBLY = "Assembly"; static final String VISITED = "Visited"; private static final String CREATOR = "Creator"; private static final Pattern SECTION_TITLE_PATTERN = Pattern. compile(".*\\" + SECTION_PREFIX + "(" + "\\d+" + "|" + TOUR_TITLE + "|" + HOME_TITLE + ")\\" + SECTION_POSTFIX + ".*"); public String getExtension() { return ".tour"; } public String getName() { return "Falk Navigator (*" + getExtension() + ")"; } public int getMaximumPositionCount() { return UNLIMITED_MAXIMUM_POSITION_COUNT; } public boolean isSupportsMultipleRoutes() { return false; } public boolean isWritingRouteCharacteristics() { return false; } @SuppressWarnings("unchecked") public <P extends NavigationPosition> TourRoute createRoute(RouteCharacteristics characteristics, String name, List<P> positions) { return new TourRoute(name, (List<TourPosition>) positions); } public void read(InputStream source, ParserContext<TourRoute> context) throws Exception { read(source, UTF8_ENCODING, context); } public void read(BufferedReader reader, String encoding, ParserContext<TourRoute> context) throws IOException { List<TourPosition> positions = new ArrayList<>(); Map<String, String> map = new HashMap<>(); String sectionTitle = null, routeName = null; while (true) { String line = reader.readLine(); if (line == null) { TourPosition position = parsePosition(map, sectionTitle); if (position != null) positions.add(position); break; } if (line.length() == 0) continue; if (isSectionTitle(line)) { if (sectionTitle != null) { if (TOUR_TITLE.equals(sectionTitle)) { routeName = map.get(NAME); // ignore everything else from the [TOUR] section map.clear(); } else { // if there is no PositionInList key, use the sectionTitle to order the positions if (!map.containsKey(POSITION_IN_LIST)) map.put(POSITION_IN_LIST, sectionTitle); TourPosition position = parsePosition(map, sectionTitle); if (position != null) positions.add(position); map = new HashMap<>(); } } sectionTitle = parseSectionTitle(line); } else if (isNameValue(line)) { String name = trim(parseName(line)); String value = trim(parseValue(line)); map.put(name, value); } else { return; } } if (positions.size() > 0) context.appendRoute(createRoute(Waypoints, routeName, sortPositions(positions))); } private List<TourPosition> sortPositions(List<TourPosition> positions) { TourPosition[] positionArray = positions.toArray(new TourPosition[positions.size()]); sort(positionArray, new PositionInListComparator()); return new ArrayList<>(asList(positionArray)); } boolean isSectionTitle(String line) { Matcher matcher = SECTION_TITLE_PATTERN.matcher(line); return matcher.matches(); } String parseSectionTitle(String line) { Matcher matcher = SECTION_TITLE_PATTERN.matcher(line); if (!matcher.matches()) throw new IllegalArgumentException("'" + line + "' does not match"); return matcher.group(1); } TourPosition parsePosition(Map<String, String> map, String sectionTitle) { String zipCode = trim(map.get(ZIPCODE)); String city = trim(map.get(CITY)); String street = trim(map.get(STREET)); String houseNo = trim(map.get(HOUSENO)); String name = trim(map.get(NAME)); Long x = parseLong(trim(map.get(LONGITUDE))); Long y = parseLong(trim(map.get(LATITUDE))); if (x == null || y == null) return null; map.remove(ZIPCODE); map.remove(CITY); map.remove(STREET); map.remove(HOUSENO); map.remove(NAME); map.remove(LONGITUDE); map.remove(LATITUDE); return new TourPosition(x, y, zipCode, city, street, houseNo, name, HOME_TITLE.equals(sectionTitle), map); } public void write(TourRoute route, OutputStream target, int startIndex, int endIndex) throws IOException { write(route, target, UTF8_ENCODING, startIndex, endIndex); } private static final String TOUR_FORMAT_NAME_VALUE_SEPARATOR = " " + NAME_VALUE_SEPARATOR + " "; public void write(TourRoute route, PrintWriter writer, int startIndex, int endIndex) { writer.println(SECTION_PREFIX + TOUR_TITLE + SECTION_POSTFIX); writer.println(NAME + TOUR_FORMAT_NAME_VALUE_SEPARATOR + asRouteName(route.getName())); writer.println(CREATOR + TOUR_FORMAT_NAME_VALUE_SEPARATOR + GENERATED_BY); writer.println(); List<TourPosition> positions = route.getPositions(); for (int i = startIndex; i < endIndex; i++) { TourPosition position = positions.get(i); String sectionTitle = position.isHome() ? HOME_TITLE : Integer.toString(i); writer.println(SECTION_PREFIX + sectionTitle + SECTION_POSTFIX); String name = position.getName(); if (name == null) name = position.getCity(); if (name == null) name = position.getDescription(); writer.println(NAME + TOUR_FORMAT_NAME_VALUE_SEPARATOR + name); writer.println(POSITION_IN_LIST + TOUR_FORMAT_NAME_VALUE_SEPARATOR + Integer.toString(i)); if (position.getZipCode() != null) writer.println(ZIPCODE + TOUR_FORMAT_NAME_VALUE_SEPARATOR + position.getZipCode()); if (position.getCity() != null && !position.getCity().equals(name)) writer.println(CITY + TOUR_FORMAT_NAME_VALUE_SEPARATOR + position.getCity()); if (position.getStreet() != null) writer.println(STREET + TOUR_FORMAT_NAME_VALUE_SEPARATOR + position.getStreet()); if (position.getHouseNo() != null) writer.println(HOUSENO + TOUR_FORMAT_NAME_VALUE_SEPARATOR + position.getHouseNo()); writer.println(LONGITUDE + TOUR_FORMAT_NAME_VALUE_SEPARATOR + position.getX()); writer.println(LATITUDE + TOUR_FORMAT_NAME_VALUE_SEPARATOR + position.getY()); if (i < endIndex - 1) writer.println(EXTEND_ROUTE + TOUR_FORMAT_NAME_VALUE_SEPARATOR + "1"); for (String key : position.keySet()) { if (!key.equals(POSITION_IN_LIST) && !key.equals(EXTEND_ROUTE)) { String value = position.get(key); writer.println(key + TOUR_FORMAT_NAME_VALUE_SEPARATOR + value); } } if (!position.keySet().contains(CLASS)) writer.println(CLASS + TOUR_FORMAT_NAME_VALUE_SEPARATOR + "FMI.FalkNavigator.PersistentAddress"); if (!position.keySet().contains(ASSEMBLY)) writer.println(ASSEMBLY + TOUR_FORMAT_NAME_VALUE_SEPARATOR + "FalkNavigator"); if (!position.keySet().contains(VISITED)) writer.println(VISITED + TOUR_FORMAT_NAME_VALUE_SEPARATOR + "0"); writer.println(); } } }