/*
* Copyright (C) 2006, 2011.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* This program 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.
*/
package uk.me.parabola.mkgmap.build;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import uk.me.parabola.imgfmt.app.trergn.TREHeader;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.reader.osm.Tags;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
public class LocatorConfig {
private static final Logger log = Logger.getLogger(LocatorConfig.class);
/** maps country name (in all variants) to the 3 letter ISO code */
private final Map<String,String> isoMap = new HashMap<>();
/** maps the ISO code to the offset of the region in the is_in tag */
private final Map<String,Integer> regOffsetMap = new HashMap<>();
/** maps the ISO code to the POI display flag */
private final Map<String,Integer> poiDispFlagMap = new HashMap<>();
/** maps the ISO code to the drive-on-left flag */
private final Map<String,Boolean> driveOnLeftFlagMap = new HashMap<>();
/** contains the names of all continents */
private final Set<String> continents = new HashSet<>();
/** maps ISO => default country name */
private final Map<String, String> defaultCountryNames = new HashMap<String, String>();
/** Maps 3 letter ISO code to all tags of a country */
private final Map<String, Tags> countryTagMap = new HashMap<String, Tags>();
private final static LocatorConfig instance = new LocatorConfig();
public static LocatorConfig get() {
return instance;
}
private LocatorConfig()
{
loadConfig("/LocatorConfig.xml");
}
private void loadConfig(String fileName)
{
try
{
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputStream inStream;
try
{
inStream = new FileInputStream("resources/" + fileName);
}
catch (Exception ex)
{
inStream = null;
}
if(inStream == null) // If not loaded from disk use from jar file
inStream = this.getClass().getResourceAsStream(fileName);
Document document = builder.parse(inStream);
Node rootNode = document.getDocumentElement();
if(rootNode.getNodeName().equals("locator"))
{
Node cNode = rootNode.getFirstChild();
while(cNode != null)
{
if(cNode.getNodeName().equals("continent"))
{
NamedNodeMap attr = cNode.getAttributes();
if(attr != null)
{
Node nameTag = attr.getNamedItem("name");
if(nameTag != null)
addContinent(nameTag.getNodeValue());
}
}
if (cNode.getNodeName().equals("country")) {
NamedNodeMap attr = cNode.getAttributes();
String iso = null;
if (attr != null) {
Node nameTag = null;
Node abrTag = attr.getNamedItem("abr");
if (abrTag != null) {
iso = abrTag.getNodeValue().toUpperCase().trim().intern();
if (iso.length() != 3) {
log.error("ISO code (abr) must have three characters: "
+ iso);
}
}
nameTag = attr.getNamedItem("name");
if (iso != null && nameTag != null) {
addISO(nameTag.getNodeValue(), iso);
defaultCountryNames.put(iso, nameTag
.getNodeValue().trim());
}
if (iso != null)
addISO(iso, iso);
if(iso == null && nameTag != null)
addISO(nameTag.getNodeValue(),"");
Node regionOffsetTag = attr.getNamedItem("regionOffset");
if(regionOffsetTag != null && iso != null)
{
addRegionOffset(iso,Integer.parseInt(regionOffsetTag.getNodeValue()));
}
int poiDispTag = 0x0;
Node streetBeforeHousenumber = attr.getNamedItem("streetBeforeHousenumber");
if (streetBeforeHousenumber != null && "true".equals(streetBeforeHousenumber.getNodeValue())) {
poiDispTag |= TREHeader.POI_FLAG_STREET_BEFORE_HOUSENUMBER;
}
Node postalcodeBeforeCity = attr.getNamedItem("postalcodeBeforeCity");
if (postalcodeBeforeCity != null && "true".equals(postalcodeBeforeCity.getNodeValue())) {
poiDispTag |= TREHeader.POI_FLAG_POSTALCODE_BEFORE_CITY;
}
if (poiDispTag != 0x0 && iso != null) {
setPoiDispTag(iso, poiDispTag);
}
Node driveOnLeft = attr.getNamedItem("driveOnLeft");
driveOnLeftFlagMap.put(iso, driveOnLeft != null && "true".equals(driveOnLeft.getNodeValue()));
}
if (iso != null) {
Node cEntryNode = cNode.getFirstChild();
while(cEntryNode != null)
{
if(cEntryNode.getNodeName().equals("variant"))
{
Node nodeText = cEntryNode.getFirstChild();
if (nodeText != null)
addISO(nodeText.getNodeValue(), iso);
}
cEntryNode = cEntryNode.getNextSibling();
}
}
}
cNode = cNode.getNextSibling();
}
}
else
{
System.out.println(fileName + "contains invalid root tag " + rootNode.getNodeName());
}
}
catch (Exception ex)
{
ex.printStackTrace();
//System.out.println("Something is wrong here");
}
}
private void addISO(String country, String iso)
{
String cStr = country.toUpperCase().trim();
isoMap.put(cStr,iso);
}
private void addRegionOffset(String iso, Integer offset)
{
regOffsetMap.put(iso,offset);
}
private void setPoiDispTag(String iso, int flag)
{
// only two flags are allowed to be configured
poiDispFlagMap.put(iso, flag & (TREHeader.POI_FLAG_STREET_BEFORE_HOUSENUMBER | TREHeader.POI_FLAG_POSTALCODE_BEFORE_CITY));
}
private void addContinent(String continent)
{
String cStr = continent.toUpperCase().trim();
continents.add(cStr);
}
public synchronized void setDefaultCountry(String country, String abbr)
{
addISO(country, abbr);
}
public synchronized boolean isCountry(String country)
{
return isoMap.containsKey(country.toUpperCase().trim());
}
public synchronized boolean countryHasTags(String isoCode) {
return countryTagMap.containsKey(isoCode);
}
public synchronized String addCountryWithTags(String isoCode, Tags countryTags) {
if (isoCode == null) {
// cannot find three letter iso code for this countries
// do not use it
log.warn("Cannot find country with tags", countryTags);
return null;
}
if (countryHasTags(isoCode)) {
// country is already known
return isoCode;
}
// add it as new country to the tag map
countryTagMap.put(isoCode, countryTags.copy());
String name = countryTags.get("name");
if (name != null) {
addISO(name, isoCode);
}
String int_name = countryTags.get("int_name");
if (int_name != null) {
addISO(int_name, isoCode);
}
// add all variants to the abbreviation map
for (String countryName : countryTags.getTagsWithPrefix("name:", false).values()) {
addISO(countryName, isoCode);
}
return isoCode;
}
/**
* Retrieves the three letter ISO code which is used by the Garmins.
* @param country a country name
* @return the three letter ISO code (<code>null</code> = unknown)
*/
public synchronized String getCountryISOCode(String country)
{
if (country == null) {
return null;
}
String res = isoMap.get(country);
if (res == null){
res = isoMap.get(country.toUpperCase().trim());
if (res != null) {
isoMap.put(country, res);
}
}
return res;
}
/**
* Retrieves the name of a country by its three letter iso code and the list of
* name tags. The first available value of the tags in the nameTags list is returned.
*
* @param isoCode the three letter ISO code
* @param nameTags the list of name tags
* @return the full country name (<code>null</code> if unknown)
*/
public synchronized String getCountryName(String isoCode, List<String> nameTags) {
Tags countryTags = countryTagMap.get(isoCode);
if (countryTags==null) {
// no tags for this country available
// return the default country name from the LocatorConfig.xml
return defaultCountryNames.get(isoCode);
}
// search for the first available tag of the nameTags list
for (String nameTag : nameTags) {
String name = countryTags.get(nameTag);
if (name != null) {
return name;
}
}
// last try: just the simple "name" tag
return countryTags.get("name");
}
public synchronized int getRegionOffset(String iso)
{
if (iso == null) {
return 1;
}
Integer regOffset = regOffsetMap.get(iso);
if(regOffset != null)
return regOffset;
else
return 1; // Default is 1 the next string after before country
}
public synchronized int getPoiDispFlag(String iso)
{
if (iso == null) {
return 0;
}
Integer flag = poiDispFlagMap.get(iso);
if(flag != null)
return flag;
else
return 0; // Default is 0
}
public synchronized boolean isContinent(String continent)
{
String s = continent.toUpperCase().trim();
return continents.contains(s);
}
public synchronized boolean getDriveOnLeftFlag(String iso)
{
if (iso == null)
return false;
Boolean driveOnLeft = driveOnLeftFlagMap.get(iso);
if (driveOnLeft == null){
log.warn("Did not find iso code",iso,"in LocatorConfig.xml, assuming drive-on-right for it");
driveOnLeftFlagMap.put(iso,false);
return false;
}
else
return driveOnLeft;
}
}