package de.blau.android.names;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import com.google.gson.stream.JsonReader;
import android.content.Context;
import android.content.res.AssetManager;
import android.support.annotation.NonNull;
import android.util.Log;
import de.blau.android.util.SearchIndexUtils;
import de.blau.android.util.collections.MultiHashMap;
/**
* Support for the name suggestion index
* see https://github.com/simonpoole/name-suggestion-index
* @author simon
*
*/
public class Names {
public class TagMap extends TreeMap<String,String> {
private static final long serialVersionUID = 1L;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (String s:this.keySet()) {
builder.append(s.replace("|", " ") + "=" + this.get(s) + "|");
}
if (builder.length() > 0)
builder.deleteCharAt(builder.length()-1);
return builder.toString();
}
}
public class NameAndTags implements Comparable<NameAndTags>{
private String name;
TagMap tags;
public NameAndTags(String name, TagMap tags) {
this.setName(name);
this.tags = tags;
}
@Override
public String toString() {
return getName() + " (" + tags.toString() + ")";
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the name
*/
public TagMap getTags() {
return tags;
}
@Override
public int compareTo(@NonNull NameAndTags another) {
if (another.name.equals(name)) {
// more tags is better
if (tags.size() > ((NameAndTags)another).tags.size()) {
return +1;
} else if (tags.size() < another.tags.size()) {
return -1;
}
return 0;
}
return name.compareTo(another.name);
}
}
private static MultiHashMap<String,TagMap> nameList = new MultiHashMap<String,TagMap>(false); // names -> tags
private static HashMap<String,TagMap> tagList = new HashMap<String,TagMap>(); // tags string to tags
private static MultiHashMap<TagMap, String> tags2namesList = new MultiHashMap<TagMap,String>(false); //
private static MultiHashMap<String, String> categories = new MultiHashMap<String,String>(false);
private static boolean ready = false;
public Names(Context ctx) {
synchronized (nameList) {
if (!ready) {
Log.d("Names","Parsing configuration files");
AssetManager assetManager = ctx.getAssets();
try {
InputStream is = assetManager.open("name-suggestions.min.json");
JsonReader reader = new JsonReader(new InputStreamReader(is));
try {
try {
// key object
String key = null;
reader.beginObject();
while (reader.hasNext()) {
key = reader.nextName(); // amenity, shop
// value object
String value = null;
reader.beginObject();
while (reader.hasNext()) { // restaurant, fast_food, ....
value = reader.nextName();
// name object
String name = null;
int count = 0;
reader.beginObject();
while (reader.hasNext()) {
name = reader.nextName(); // name of estabishment
reader.beginObject();
TagMap secondaryTags = null; // any extra tags store here
while (reader.hasNext()) {
String jsonName = reader.nextName();
if (jsonName.equals("count")) {
count = reader.nextInt();
} else if (jsonName.equals("tags")) {
reader.beginObject();
while (reader.hasNext()) {
secondaryTags = new TagMap();
secondaryTags.put(reader.nextName(), reader.nextString());
}
reader.endObject(); // tags
} else {
reader.skipValue();
}
}
reader.endObject(); // name
// add to lists here
TagMap primaryTags = new TagMap();
primaryTags.put(key, value);
if (secondaryTags != null) {
primaryTags.putAll(secondaryTags);
}
String tagKey = primaryTags.toString();
TagMap tm = tagList.get(tagKey);
if (tm == null) {
tagList.put(tagKey, primaryTags);
tm = primaryTags;
}
nameList.add(name, tm);
tags2namesList.add(tm, name);
}
reader.endObject(); // value
}
reader.endObject(); // key
}
reader.endObject();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
finally {
try {
reader.close();
} catch (IOException ioex) {
Log.d("Names","Ignoring " + ioex);
}
}
is = assetManager.open("categories.json");
reader = new JsonReader(new InputStreamReader(is));
try {
try{
String category = null;
reader.beginObject();
while (reader.hasNext()) {
category = reader.nextName();
String poiType = null;
reader.beginObject();
while (reader.hasNext()) {
poiType = reader.nextName();
reader.beginArray();
while (reader.hasNext()) {
categories.add(category,poiType+"="+reader.nextString());
}
reader.endArray();
}
reader.endObject();
}
reader.endObject();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
finally {
try {
reader.close();
} catch (IOException ioex) {
Log.d("Names","Ignoring " + ioex);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ready = true;
}
}
}
public Collection<NameAndTags> getNames(SortedMap<String, String> tags) {
// remove irrelevant tags, TODO refine
TagMap tm = new TagMap();
String v = tags.get("amenity");
if (v!=null) {
tm.put("amenity",v);
// Log.d("Names","filtering for amenity="+v);
} else {
v = tags.get("shop");
if (v!=null) {
tm.put("shop",v);
// Log.d("Names","filtering for shop="+v);
}
}
if (tm.isEmpty())
return getNames();
Collection<NameAndTags> result = new ArrayList<NameAndTags>();
String origTagKey = tm.toString();
for (String tagKey:tagList.keySet()) {
if (tagKey.contains(origTagKey)) {
TagMap storedTagMap = tagList.get(tagKey);
for (String n:tags2namesList.get(storedTagMap)) {
NameAndTags nt = new NameAndTags(n,storedTagMap);
result.add(nt);
}
}
}
TreeSet<String> seen = new TreeSet<String>();
// check categories for similar tags and add names from them too
seen.add(origTagKey); // skip stuff we've already added
for (String category:categories.getKeys()) { // loop over categories
Set<String>set = categories.get(category);
if (set.contains(origTagKey)) {
for (String catTagKey:set) { // loop over categories content
if (!seen.contains(catTagKey)) { // suppress dups
for (String tagKey:tagList.keySet()) {
if (tagKey.contains(catTagKey)) {
TagMap storedTagMap = tagList.get(tagKey);
for (String n:tags2namesList.get(storedTagMap)) {
NameAndTags nt = new NameAndTags(n,storedTagMap);
result.add(nt);
}
}
}
seen.add(catTagKey);
}
}
}
}
// Log.d("Names","getNames result " + result.size());
return result;
}
private Collection<NameAndTags> getNames() {
Collection<NameAndTags> result = new ArrayList<NameAndTags>();
for (String n:nameList.getKeys()) {
TagMap bestTags = null;
for (TagMap t:nameList.get(n)) {
if (bestTags == null || bestTags.size() < t.size()) {
bestTags = t;
}
}
if (bestTags != null)
result.add(new NameAndTags(n, bestTags));
}
return result;
}
public Map<String,NameAndTags> getSearchIndex() {
HashMap<String,NameAndTags> result = new HashMap<String,NameAndTags>();
Collection<NameAndTags> names = getNames();
for (NameAndTags nat:names) {
result.put(SearchIndexUtils.normalize(nat.getName()), nat);
}
return result;
}
public void dump2Log() {
Log.d("Names","Name List");
for (String n:nameList.getKeys()) {
Set<TagMap> tmList = nameList.get(n);
String tags = n +": ";
for (TagMap tm:tmList) {
tags = tags + tm.toString() + "|";
}
Log.d("Names", tags);
}
Log.d("Names","tag List");
for (TagMap tm:tags2namesList.getKeys()) {
Set<String> names = tags2namesList.get(tm);
String nameStr = tm.toString() +": ";
for (String n:names) {
nameStr = nameStr + n + "|";
}
Log.d("Names", nameStr);
}
}
}