diff --git a/OsmAndCore-sample/build.gradle b/OsmAndCore-sample/build.gradle index c700a0aadd..cb8400c5f5 100644 --- a/OsmAndCore-sample/build.gradle +++ b/OsmAndCore-sample/build.gradle @@ -4,6 +4,10 @@ android { compileSdkVersion 23 buildToolsVersion "23.0.3" + dexOptions { + jumboMode true + } + defaultConfig { minSdkVersion 14 targetSdkVersion 23 diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java index 1fdf955261..4db9b56224 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/MainActivity.java @@ -23,12 +23,10 @@ import android.widget.ListView; import android.widget.TextView; import net.osmand.core.android.AtlasMapRendererView; -import net.osmand.core.android.NativeCore; import net.osmand.core.jni.AreaI; import net.osmand.core.jni.IMapLayerProvider; import net.osmand.core.jni.IMapStylesCollection; import net.osmand.core.jni.LatLon; -import net.osmand.core.jni.LogSeverityLevel; import net.osmand.core.jni.Logger; import net.osmand.core.jni.MapObjectsSymbolsProvider; import net.osmand.core.jni.MapPresentationEnvironment; @@ -45,12 +43,12 @@ import net.osmand.core.jni.Utilities; import net.osmand.core.samples.android.sample1.MultiTouchSupport.MultiTouchZoomListener; import net.osmand.core.samples.android.sample1.adapters.SearchListAdapter; import net.osmand.core.samples.android.sample1.adapters.SearchListItem; - import net.osmand.core.samples.android.sample1.search.SearchAPI; -import net.osmand.core.samples.android.sample1.search.SearchAPI.SearchAPICallback; -import net.osmand.core.samples.android.sample1.search.SearchItem; +import net.osmand.core.samples.android.sample1.search.SearchAPI.SearchCallback; +import net.osmand.core.samples.android.sample1.search.items.SearchItem; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -86,7 +84,8 @@ public class MainActivity extends Activity { private SearchAPI searchAPI; private ListView searchListView; private SearchListAdapter adapter; - private final static int MAX_SEARCH_RESULTS = 50; + private final static int MAX_SEARCH_RESULTS_CORE = 500; + private final static int MAX_SEARCH_RESULTS_IU = 50; // Germany private final static float INIT_LAT = 49.353953f; @@ -402,41 +401,50 @@ public class MainActivity extends Activity { private void runSearch(AreaI bounds31, String keyword) { searchAPI.setObfAreaFilter(bounds31); - searchAPI.startSearch(keyword, MAX_SEARCH_RESULTS, new SearchAPICallback() { - @Override - public void onSearchFinished(List searchItems, boolean cancelled) { - if (searchItems != null && !cancelled) { - LatLon latLon = Utilities.convert31ToLatLon(target31); - List rows = new ArrayList<>(); - for (SearchItem item : searchItems) { - SearchListItem row = - SearchListItem.buildListItem((SampleApplication)getApplication(), item); - rows.add(row); + searchAPI.startSearch(keyword, MAX_SEARCH_RESULTS_CORE, + // Intermediate search callback + new SearchCallback() { + @Override + public void onSearchFinished(List searchItems) { + processSearchResult(searchItems); } - - adapter.clear(); - adapter.addAll(rows); - adapter.updateDistance(latLon.getLatitude(), latLon.getLongitude()); - adapter.sort(new Comparator() { - @Override - public int compare(SearchListItem lhs, SearchListItem rhs) { - int res = Double.compare(lhs.getDistance(), rhs.getDistance()); - if (res == 0) { - return lhs.getName().compareToIgnoreCase(rhs.getName()); - } else { - return res; - } - } - }); - adapter.notifyDataSetChanged(); - if (adapter.getCount() > 0) { - searchListView.setSelection(0); + }, + // Core search callback + new SearchCallback() { + @Override + public void onSearchFinished(List searchItems) { + processSearchResult(searchItems); } + }); + } - showSearchList(); + private void processSearchResult(List searchItems) { + if (searchItems != null) { + LatLon latLon = Utilities.convert31ToLatLon(target31); + double latitude = latLon.getLatitude(); + double longitude = latLon.getLongitude(); + List rows = new ArrayList<>(); + for (SearchItem item : searchItems) { + SearchListItem listItem = + SearchListItem.buildListItem((SampleApplication)getApplication(), item); + if (listItem != null) { + rows.add(listItem); } } - }); + + if (rows.size() > MAX_SEARCH_RESULTS_IU) { + rows = rows.subList(0, MAX_SEARCH_RESULTS_IU); + } + + adapter.clear(); + adapter.addAll(rows); + adapter.notifyDataSetChanged(); + if (adapter.getCount() > 0) { + searchListView.setSelection(0); + } + + showSearchList(); + } } private class MapViewOnGestureListener extends SimpleOnGestureListener { diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/AddressSearchListItem.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/AddressSearchListItem.java index 85f3ce1653..b2fe22ddf1 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/AddressSearchListItem.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/AddressSearchListItem.java @@ -2,7 +2,7 @@ package net.osmand.core.samples.android.sample1.adapters; import net.osmand.core.samples.android.sample1.MapUtils; import net.osmand.core.samples.android.sample1.SampleApplication; -import net.osmand.core.samples.android.sample1.search.AddressSearchItem; +import net.osmand.core.samples.android.sample1.search.items.AddressSearchItem; import net.osmand.util.Algorithms; public class AddressSearchListItem extends SearchListItem { diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/AmenitySearchListItem.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/AmenitySearchListItem.java index de35d52935..bb51a8d5c3 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/AmenitySearchListItem.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/AmenitySearchListItem.java @@ -4,7 +4,7 @@ import android.graphics.drawable.Drawable; import net.osmand.core.samples.android.sample1.MapUtils; import net.osmand.core.samples.android.sample1.SampleApplication; -import net.osmand.core.samples.android.sample1.search.AmenitySearchItem; +import net.osmand.core.samples.android.sample1.search.items.AmenitySearchItem; import net.osmand.data.Amenity; import net.osmand.osm.MapPoiTypes; import net.osmand.osm.PoiCategory; diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/SearchListAdapter.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/SearchListAdapter.java index 93a4bd940d..df69def47a 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/SearchListAdapter.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/SearchListAdapter.java @@ -26,8 +26,7 @@ public class SearchListAdapter extends ArrayAdapter { for (int i = 0; i < getCount(); i++) { SearchListItem item = getItem(i); item.setDistance(Utilities.distance( - longitude, latitude, - item.getLongitude(), item.getLatitude())); + longitude, latitude, item.getLongitude(), item.getLatitude())); } } diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/SearchListItem.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/SearchListItem.java index a222114e0a..fbd5459389 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/SearchListItem.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/adapters/SearchListItem.java @@ -3,15 +3,14 @@ package net.osmand.core.samples.android.sample1.adapters; import android.graphics.drawable.Drawable; import net.osmand.core.samples.android.sample1.SampleApplication; -import net.osmand.core.samples.android.sample1.search.AddressSearchItem; -import net.osmand.core.samples.android.sample1.search.AmenitySearchItem; -import net.osmand.core.samples.android.sample1.search.SearchItem; +import net.osmand.core.samples.android.sample1.search.items.AddressSearchItem; +import net.osmand.core.samples.android.sample1.search.items.AmenitySearchItem; +import net.osmand.core.samples.android.sample1.search.items.SearchItem; public class SearchListItem { protected SampleApplication app; private SearchItem searchItem; - private double distance; public SearchListItem(SampleApplication app, SearchItem searchItem) { this.app = app; @@ -45,11 +44,11 @@ public class SearchListItem { } public double getDistance() { - return distance; + return searchItem.getDistance(); } public void setDistance(double distance) { - this.distance = distance; + searchItem.setDistance(distance); } public Drawable getIcon() { diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchAPI.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchAPI.java index 848b17840e..fd8703e5d8 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchAPI.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchAPI.java @@ -1,19 +1,15 @@ package net.osmand.core.samples.android.sample1.search; -import android.os.AsyncTask; +import android.support.annotation.NonNull; -import net.osmand.core.jni.Address; -import net.osmand.core.jni.AddressesByNameSearch; -import net.osmand.core.jni.AmenitiesByNameSearch; -import net.osmand.core.jni.Amenity; import net.osmand.core.jni.AreaI; -import net.osmand.core.jni.IQueryController; -import net.osmand.core.jni.ISearch; -import net.osmand.core.jni.ISearch.INewResultEntryCallback; -import net.osmand.core.jni.NullableAreaI; import net.osmand.core.jni.ObfsCollection; +import net.osmand.core.jni.PointI; +import net.osmand.core.samples.android.sample1.search.items.SearchItem; +import net.osmand.core.samples.android.sample1.search.requests.CoreSearchRequest; +import net.osmand.core.samples.android.sample1.search.requests.IntermediateSearchRequest; +import net.osmand.core.samples.android.sample1.search.requests.SearchRequest; -import java.util.ArrayList; import java.util.List; public class SearchAPI { @@ -21,15 +17,23 @@ public class SearchAPI { private ObfsCollection obfsCollection; private AreaI searchableArea; private AreaI obfAreaFilter; - private SearchRequestExecutor executor; + private PointI searchLocation; + private double searchRadius; - public interface SearchAPICallback { - void onSearchFinished(List searchItems, boolean cancelled); + private SearchRequestExecutor executor; + private SearchString searchString; + private SearchScope searchScope; + private List searchItems; + + public interface SearchCallback { + void onSearchFinished(List searchItems); } - public SearchAPI(ObfsCollection obfsCollection) { + public SearchAPI(@NonNull ObfsCollection obfsCollection) { this.obfsCollection = obfsCollection; - executor = new SearchRequestExecutor(); + this.executor = new SearchRequestExecutor(); + this.searchString = new SearchString(); + this.searchScope = new SearchScope(this); } public AreaI getSearchableArea() { @@ -48,8 +52,55 @@ public class SearchAPI { this.obfAreaFilter = obfAreaFilter; } - public void startSearch(String keyword, int maxSearchResults, SearchAPICallback apiCallback) { - executor.run(new SearchRequest(keyword, maxSearchResults, apiCallback), true); + public PointI getSearchLocation() { + return searchLocation; + } + + public void setSearchLocation(PointI searchLocation) { + this.searchLocation = searchLocation; + } + + public double getSearchRadius() { + return searchRadius; + } + + public void setSearchRadius(double searchRadius) { + this.searchRadius = searchRadius; + } + + public ObfsCollection getObfsCollection() { + return obfsCollection; + } + + public SearchString getSearchString() { + return searchString; + } + + public SearchScope getSearchScope() { + return searchScope; + } + + public List getSearchItems() { + return searchItems; + } + + public void setSearchItems(List searchItems) { + this.searchItems = searchItems; + } + + public void startSearch(String query, int maxSearchResults, + SearchCallback intermediateSearchCallback, + SearchCallback coreSearchCallback) { + + searchString.setQueryText(query); + searchScope.updateScope(); + IntermediateSearchRequest intermediateSearchRequest = null; + if (searchItems != null && !searchItems.isEmpty()) { + intermediateSearchRequest = + new IntermediateSearchRequest(this, maxSearchResults, intermediateSearchCallback); + } + executor.run(new CoreSearchRequest(intermediateSearchRequest, this, + maxSearchResults, coreSearchCallback), true); } public void cancelSearch() { @@ -97,127 +148,4 @@ public class SearchAPI { } } } - - public class SearchRequest { - private String keyword; - private int maxSearchResults; - private Runnable onFinished; - private SearchAPICallback apiCallback; - - private boolean cancelled; - private int amenityResultsCounter; - private int addressResultsCounter; - - public SearchRequest(String keyword, int maxSearchResults, SearchAPICallback apiCallback) { - this.keyword = keyword; - this.maxSearchResults = maxSearchResults; - this.apiCallback = apiCallback; - } - - public void run() { - - new AsyncTask>() { - @Override - protected List doInBackground(String... params) { - return doSearch(params[0]); - } - - @Override - protected void onPostExecute(List searchItems) { - - if (onFinished != null) { - onFinished.run(); - } - - if (apiCallback != null) { - apiCallback.onSearchFinished(searchItems, cancelled); - } - } - }.execute(keyword); - } - - private List doSearch(String keyword) { - System.out.println("=== Start search"); - amenityResultsCounter = 0; - addressResultsCounter = 0; - - final List searchItems = new ArrayList<>(); - - // Setup Amenities by name search - AmenitiesByNameSearch amByNameSearch = new AmenitiesByNameSearch(obfsCollection); - AmenitiesByNameSearch.Criteria amByNameCriteria = new AmenitiesByNameSearch.Criteria(); - amByNameCriteria.setName(keyword); - if (obfAreaFilter != null) { - amByNameCriteria.setObfInfoAreaFilter(new NullableAreaI(obfAreaFilter)); - } - INewResultEntryCallback amByNameResultCallback = new ISearch.INewResultEntryCallback() { - @Override - public void method(ISearch.Criteria criteria, ISearch.IResultEntry resultEntry) { - Amenity amenity = new AmenityResultEntry(resultEntry).getAmenity(); - AmenitySearchItem amenitySearchItem = new AmenitySearchItem(amenity); - searchItems.add(amenitySearchItem); - System.out.println("Poi found === " + amenitySearchItem.toString()); - amenityResultsCounter++; - } - }; - - // Setup Addresses by name search - AddressesByNameSearch addrByNameSearch = new AddressesByNameSearch(obfsCollection); - AddressesByNameSearch.Criteria addrByNameCriteria = new AddressesByNameSearch.Criteria(); - addrByNameCriteria.setName(keyword); - if (obfAreaFilter != null) { - addrByNameCriteria.setObfInfoAreaFilter(new NullableAreaI(obfAreaFilter)); - } - INewResultEntryCallback addrByNameResultCallback = new ISearch.INewResultEntryCallback() { - @Override - public void method(ISearch.Criteria criteria, ISearch.IResultEntry resultEntry) { - Address address = new AddressResultEntry(resultEntry).getAddress(); - AddressSearchItem addrSearchItem = new AddressSearchItem(address); - searchItems.add(addrSearchItem); - System.out.println("Address found === " + addrSearchItem.toString()); - addressResultsCounter++; - } - }; - - amByNameSearch.performSearch(amByNameCriteria, amByNameResultCallback.getBinding(), new IQueryController() { - @Override - public boolean isAborted() { - return amenityResultsCounter >= maxSearchResults || cancelled; - } - }); - - if (!cancelled) { - addrByNameSearch.performSearch(addrByNameCriteria, addrByNameResultCallback.getBinding(), new IQueryController() { - @Override - public boolean isAborted() { - return addressResultsCounter >= maxSearchResults || cancelled; - } - }); - } - - System.out.println("=== Finish search"); - - return searchItems; - } - - public void cancel() { - cancelled = true; - } - - public void setOnFinishedCallback(Runnable onFinished) { - this.onFinished = onFinished; - } - } - - private static class AmenityResultEntry extends AmenitiesByNameSearch.ResultEntry { - protected AmenityResultEntry(ISearch.IResultEntry resultEntry) { - super(ISearch.IResultEntry.getCPtr(resultEntry), false); - } - } - - private static class AddressResultEntry extends AddressesByNameSearch.ResultEntry { - protected AddressResultEntry(ISearch.IResultEntry resultEntry) { - super(ISearch.IResultEntry.getCPtr(resultEntry), false); - } - } } diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchScope.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchScope.java new file mode 100644 index 0000000000..a5cbc3a25b --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchScope.java @@ -0,0 +1,118 @@ +package net.osmand.core.samples.android.sample1.search; + +import net.osmand.core.jni.AddressesByNameSearch; +import net.osmand.core.jni.AmenitiesByNameSearch; +import net.osmand.core.jni.LatLon; +import net.osmand.core.jni.QStringList; +import net.osmand.core.jni.QStringStringListHash; +import net.osmand.core.jni.Utilities; +import net.osmand.core.samples.android.sample1.search.items.AddressSearchItem; +import net.osmand.core.samples.android.sample1.search.items.AmenitySearchItem; +import net.osmand.core.samples.android.sample1.search.items.SearchItem; +import net.osmand.core.samples.android.sample1.search.tokens.CitySearchToken; +import net.osmand.core.samples.android.sample1.search.tokens.PoiTypeSearchToken; +import net.osmand.core.samples.android.sample1.search.tokens.PostcodeSearchToken; +import net.osmand.core.samples.android.sample1.search.tokens.SearchToken; +import net.osmand.core.samples.android.sample1.search.tokens.SearchToken.TokenType; +import net.osmand.util.Algorithms; + +import java.util.List; +import java.util.Map; + +public class SearchScope { + + private SearchAPI searchAPI; + private Map resolvedTokens; + private double searchLat; + private double searchLon; + private double searchRadius; + + public SearchScope(SearchAPI searchAPI) { + this.searchAPI = searchAPI; + } + + public void updateScope() { + resolvedTokens = searchAPI.getSearchString().getResolvedTokens(); + LatLon latLon = Utilities.convert31ToLatLon(searchAPI.getSearchLocation()); + searchLat = latLon.getLatitude(); + searchLon = latLon.getLongitude(); + searchRadius = searchAPI.getSearchRadius(); + } + + public void setupAmenitySearchCriteria(AmenitiesByNameSearch.Criteria criteria) { + String categoryName = null; + String typeName = null; + if (resolvedTokens.containsKey(TokenType.POI_TYPE)) { + PoiTypeSearchToken token = (PoiTypeSearchToken)resolvedTokens.get(TokenType.POI_TYPE); + categoryName = token.getPoiType().getCategory().getKeyName(); + typeName = token.getName(); + } else if (resolvedTokens.containsKey(TokenType.POI_FILTER)) { + SearchToken token = resolvedTokens.get(TokenType.POI_FILTER); + categoryName = token.getName(); + } else if (resolvedTokens.containsKey(TokenType.POI_CATEGORY)) { + SearchToken token = resolvedTokens.get(TokenType.POI_CATEGORY); + categoryName = token.getName(); + } + if (!Algorithms.isEmpty(categoryName) && !Algorithms.isEmpty(typeName)) { + QStringStringListHash list = new QStringStringListHash(); + QStringList stringList = new QStringList(); + //todo list.set(categoryName, stringList); + criteria.setCategoriesFilter(list); + } else if (!Algorithms.isEmpty(categoryName)) { + QStringStringListHash list = new QStringStringListHash(); + //todo list.set(categoryName, new QStringList()); + criteria.setCategoriesFilter(list); + } + } + + public void setupAddressSearchCriteria(AddressesByNameSearch.Criteria criteria) { + //not implemented + } + + public boolean processAmenitySearchItem(AmenitySearchItem item) { + boolean res = true; + updateDistance(item); + if (searchRadius > 0) { + res = item.getDistance() < searchRadius; + } + return res; + } + + public boolean processAddressSearchItem(AddressSearchItem item) { + boolean res = true; + if (resolvedTokens.containsKey(TokenType.CITY) && item.getParentCityObfId() != null) { + CitySearchToken token = (CitySearchToken)resolvedTokens.get(TokenType.CITY); + res = token.getObfId().equals(item.getParentCityObfId()); + } else if (resolvedTokens.containsKey(TokenType.POSTCODE) && item.getParentPostcodeObfId() != null) { + PostcodeSearchToken token = (PostcodeSearchToken)resolvedTokens.get(TokenType.CITY); + res = token.getObfId().equals(item.getParentPostcodeObfId()); + } + if (res) { + updateDistance(item); + } + return res; + } + + public void processSearchResult(List searchItems) { + /* + Collections.sort(searchItems, new Comparator() { + @Override + public int compare(SearchItem lhs, SearchItem rhs) { + int res = Double.compare(lhs.getDistance(), rhs.getDistance()); + if (res == 0) { + return lhs.getName().compareToIgnoreCase(rhs.getName()); + } else { + return res; + } + } + }); + */ + + //todo + } + + private void updateDistance(SearchItem item) { + item.setDistance(Utilities.distance( + searchLon, searchLat, item.getLongitude(), item.getLatitude())); + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchString.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchString.java new file mode 100644 index 0000000000..f838a2a276 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchString.java @@ -0,0 +1,148 @@ +package net.osmand.core.samples.android.sample1.search; + +import net.osmand.core.samples.android.sample1.search.tokens.NameFilterSearchToken; +import net.osmand.core.samples.android.sample1.search.tokens.SearchToken; +import net.osmand.core.samples.android.sample1.search.tokens.SearchToken.TokenType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SearchString { + + private String queryText = ""; + private List tokens = new ArrayList<>(); + + public SearchString() { + } + + public String getQueryText() { + return queryText; + } + + public synchronized void setQueryText(String queryText) { + int newTextLength = queryText.length(); + int currTextLength = this.queryText.length(); + boolean isNewText = currTextLength == 0 + || newTextLength == 0 + || !this.queryText.regionMatches(0, queryText, 0, + newTextLength > currTextLength ? currTextLength : newTextLength); + + int lastKnownTokenIndex = -1; + if (isNewText) { + tokens.clear(); + } else { + int brokenTokenIndex = -1; + for (int i = 0; i < tokens.size(); i++) { + SearchToken token = tokens.get(i); + int lastTokenIndex = token.getLastIndex(); + if (lastTokenIndex > newTextLength - 1 + || (lastTokenIndex < newTextLength - 1 && !startWithDelimiter(queryText.substring(lastTokenIndex + 1)))) { + brokenTokenIndex = i; + break; + } + lastKnownTokenIndex = token.getLastIndex(); + } + + if (brokenTokenIndex != -1) { + if (brokenTokenIndex == 0) { + tokens.clear(); + } else { + for (int i = tokens.size() - 1; i >= brokenTokenIndex; i--) { + tokens.remove(i); + } + } + } + } + + if (newTextLength - 1 > lastKnownTokenIndex) { + int firstWordIndex = lastKnownTokenIndex + 1; + for (int i = lastKnownTokenIndex + 1; i < newTextLength; i++) { + char c = queryText.charAt(i); + if (isDelimiterChar(c)) { + if (i == firstWordIndex) { + firstWordIndex++; + } else { + SearchToken token = new NameFilterSearchToken(firstWordIndex, queryText.substring(firstWordIndex, i)); + tokens.add(token); + firstWordIndex = i + 1; + } + } + } + if (firstWordIndex <= newTextLength - 1) { + SearchToken token = new NameFilterSearchToken(firstWordIndex, queryText.substring(firstWordIndex)); + tokens.add(token); + } + } + + this.queryText = queryText; + } + + private boolean startWithDelimiter(String text) { + char firstChar = text.charAt(0); + return isDelimiterChar(firstChar); + } + + private boolean isDelimiterChar(char c) { + return c == ',' || c == ' '; + } + + public synchronized SearchToken getNextNameFilterToken() { + SearchToken res = null; + if (!tokens.isEmpty()) { + for (int i = tokens.size() - 1; i >= 0; i--) { + SearchToken token = tokens.get(i); + if (token.getType() == TokenType.NAME_FILTER) { + res = token; + } else { + break; + } + } + } + return res; + } + + public synchronized SearchToken getLastToken() { + if (!tokens.isEmpty()) { + return tokens.get(tokens.size() - 1); + } + return null; + } + + public synchronized boolean hasNameFilterTokens() { + return getNextNameFilterToken() != null; + } + + public synchronized boolean replaceToken(SearchToken oldToken, SearchToken newToken) { + int index = tokens.indexOf(oldToken); + if (index != -1) { + tokens.set(index, newToken); + return true; + } + return false; + } + + public synchronized Map getResolvedTokens() { + Map map = new HashMap<>(); + for (SearchToken token : tokens) { + if (token.getType() != SearchToken.TokenType.NAME_FILTER) { + map.put(token.getType(), token); + } + } + return map; + } + + public static void main(String[] args){ + //test + SearchString searchString = new SearchString(); + searchString.setQueryText("cit"); + searchString.setQueryText("city"); + searchString.setQueryText("city "); + searchString.setQueryText("city s"); + searchString.setQueryText("city st"); + searchString.setQueryText("city street "); + searchString.setQueryText("city street 8"); + searchString.setQueryText("new"); + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/AddressSearchItem.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/AddressSearchItem.java similarity index 74% rename from OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/AddressSearchItem.java rename to OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/AddressSearchItem.java index 14ec48b31d..c1acab1981 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/AddressSearchItem.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/AddressSearchItem.java @@ -1,17 +1,23 @@ -package net.osmand.core.samples.android.sample1.search; +package net.osmand.core.samples.android.sample1.search.items; import net.osmand.core.jni.Address; import net.osmand.core.jni.ObfAddressStreetGroupSubtype; +import net.osmand.core.jni.ObfAddressStreetGroupType; import net.osmand.core.jni.Street; import net.osmand.core.jni.StreetGroup; import net.osmand.util.Algorithms; +import java.math.BigInteger; + public class AddressSearchItem extends SearchItem { private String namePrefix; private String nameSuffix; private String typeStr; + private BigInteger parentCityObfId; + private BigInteger parentPostcodeObfId; + public AddressSearchItem(Address address) { super(); @@ -22,8 +28,14 @@ public class AddressSearchItem extends SearchItem { setNativeName(street.getNativeName()); addLocalizedNames(street.getLocalizedNames()); if (street.getStreetGroup() != null) { + StreetGroup streetGroup = street.getStreetGroup(); nameSuffix = "st."; - typeStr = street.getStreetGroup().getNativeName() + " — " + getTypeStr(street.getStreetGroup()); + typeStr = streetGroup.getNativeName() + " — " + getTypeStr(streetGroup); + if (streetGroup.getType() == ObfAddressStreetGroupType.Postcode) { + parentPostcodeObfId = streetGroup.getId().getId(); + } else { + parentCityObfId = streetGroup.getId().getId(); + } } else { typeStr = "Street"; } @@ -77,6 +89,14 @@ public class AddressSearchItem extends SearchItem { return typeStr; } + public BigInteger getParentCityObfId() { + return parentCityObfId; + } + + public BigInteger getParentPostcodeObfId() { + return parentPostcodeObfId; + } + private class StreetInternal extends Street { public StreetInternal(Address address) { super(Address.getCPtr(address), false); diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/AmenitySearchItem.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/AmenitySearchItem.java similarity index 95% rename from OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/AmenitySearchItem.java rename to OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/AmenitySearchItem.java index 49a10721fa..10c8ff83e4 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/AmenitySearchItem.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/AmenitySearchItem.java @@ -1,4 +1,4 @@ -package net.osmand.core.samples.android.sample1.search; +package net.osmand.core.samples.android.sample1.search.items; import net.osmand.core.jni.Amenity; import net.osmand.core.jni.DecodedCategoryList; diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchItem.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/SearchItem.java similarity index 82% rename from OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchItem.java rename to OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/SearchItem.java index 8dc3a2f1a0..6ed64e9b9f 100644 --- a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/SearchItem.java +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/items/SearchItem.java @@ -1,4 +1,4 @@ -package net.osmand.core.samples.android.sample1.search; +package net.osmand.core.samples.android.sample1.search.items; import net.osmand.core.jni.LatLon; import net.osmand.core.jni.PointI; @@ -16,6 +16,9 @@ public abstract class SearchItem { protected String nativeName; protected Map localizedNames = new HashMap<>(); + private double distance; + private float priority; + protected SearchItem() { } @@ -71,6 +74,22 @@ public abstract class SearchItem { } } + public double getDistance() { + return distance; + } + + public void setDistance(double distance) { + this.distance = distance; + } + + public float getPriority() { + return priority; + } + + public void setPriority(float priority) { + this.priority = priority; + } + @Override public String toString() { return getName() + " (" + getType() + ") {lat:" + getLatitude() + " lon: " + getLongitude() + "}"; diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/CoreSearchRequest.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/CoreSearchRequest.java new file mode 100644 index 0000000000..ddb693bf22 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/CoreSearchRequest.java @@ -0,0 +1,175 @@ +package net.osmand.core.samples.android.sample1.search.requests; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import net.osmand.core.jni.Address; +import net.osmand.core.jni.AddressesByNameSearch; +import net.osmand.core.jni.AmenitiesByNameSearch; +import net.osmand.core.jni.Amenity; +import net.osmand.core.jni.IQueryController; +import net.osmand.core.jni.ISearch; +import net.osmand.core.jni.NullableAreaI; +import net.osmand.core.samples.android.sample1.search.SearchAPI; +import net.osmand.core.samples.android.sample1.search.SearchAPI.SearchCallback; +import net.osmand.core.samples.android.sample1.search.items.AddressSearchItem; +import net.osmand.core.samples.android.sample1.search.items.AmenitySearchItem; +import net.osmand.core.samples.android.sample1.search.items.SearchItem; +import net.osmand.core.samples.android.sample1.search.tokens.SearchToken; + +import java.util.ArrayList; +import java.util.List; + +public class CoreSearchRequest extends SearchRequest { + + private IntermediateSearchRequest intermediateSearchRequest; + private boolean intermediateSearchDone; + + private int amenityResultsCounter; + private int addressResultsCounter; + + public CoreSearchRequest(@Nullable IntermediateSearchRequest intermediateSearchRequest, + @NonNull SearchAPI searchAPI, int maxSearchResults, @Nullable SearchCallback searchCallback) { + super(searchAPI, maxSearchResults, searchCallback); + this.intermediateSearchRequest = intermediateSearchRequest; + } + + @Override + public void run() { + + if (intermediateSearchRequest != null) { + intermediateSearchRequest.setOnFinishedCallback(new Runnable() { + @Override + public void run() { + intermediateSearchDone = true; + } + }); + intermediateSearchRequest.run(); + } + + super.run(); + } + + @Override + protected void onSearchRequestPostExecute(List searchItems) { + if (intermediateSearchRequest != null && !intermediateSearchDone) { + intermediateSearchRequest.cancel(); + } + searchAPI.setSearchItems(searchItems); + } + + @Override + public void cancel() { + if (intermediateSearchRequest != null) { + intermediateSearchRequest.cancel(); + } + super.cancel(); + } + + @Override + protected List doSearch() { + + List res = new ArrayList<>(); + + SearchToken token = searchString.getNextNameFilterToken(); + while (token != null && !cancelled) { + if (token.getType() == SearchToken.TokenType.NAME_FILTER) { + List searchItems = doCoreSearch(token); + res.clear(); + res.addAll(searchItems); + } + token = searchString.getNextNameFilterToken(); + } + + return res; + } + + private List doCoreSearch(@NonNull SearchToken token) { + System.out.println("=== Start search"); + amenityResultsCounter = 0; + addressResultsCounter = 0; + + String keyword = token.getQueryText(); + final List searchItems = new ArrayList<>(); + + // Setup Amenities by name search + AmenitiesByNameSearch amByNameSearch = new AmenitiesByNameSearch(searchAPI.getObfsCollection()); + AmenitiesByNameSearch.Criteria amenityByNameCriteria = new AmenitiesByNameSearch.Criteria(); + amenityByNameCriteria.setName(keyword); + if (searchAPI.getObfAreaFilter() != null) { + amenityByNameCriteria.setObfInfoAreaFilter(new NullableAreaI(searchAPI.getObfAreaFilter())); + } + searchScope.setupAmenitySearchCriteria(amenityByNameCriteria); + + ISearch.INewResultEntryCallback amenityByNameResultCallback = new ISearch.INewResultEntryCallback() { + @Override + public void method(ISearch.Criteria criteria, ISearch.IResultEntry resultEntry) { + Amenity amenity = new AmenityResultEntry(resultEntry).getAmenity(); + AmenitySearchItem amenitySearchItem = new AmenitySearchItem(amenity); + if (searchScope.processAmenitySearchItem(amenitySearchItem)) { + searchItems.add(amenitySearchItem); + } + System.out.println("Poi found === " + amenitySearchItem.toString()); + amenityResultsCounter++; + } + }; + + // Setup Addresses by name search + AddressesByNameSearch addrByNameSearch = new AddressesByNameSearch(searchAPI.getObfsCollection()); + AddressesByNameSearch.Criteria addrByNameCriteria = new AddressesByNameSearch.Criteria(); + addrByNameCriteria.setName(keyword); + if (searchAPI.getObfAreaFilter() != null) { + addrByNameCriteria.setObfInfoAreaFilter(new NullableAreaI(searchAPI.getObfAreaFilter())); + } + searchScope.setupAddressSearchCriteria(addrByNameCriteria); + + ISearch.INewResultEntryCallback addrByNameResultCallback = new ISearch.INewResultEntryCallback() { + @Override + public void method(ISearch.Criteria criteria, ISearch.IResultEntry resultEntry) { + Address address = new AddressResultEntry(resultEntry).getAddress(); + AddressSearchItem addrSearchItem = new AddressSearchItem(address); + if (searchScope.processAddressSearchItem(addrSearchItem)) { + searchItems.add(addrSearchItem); + } + System.out.println("Address found === " + addrSearchItem.toString()); + addressResultsCounter++; + } + }; + + amByNameSearch.performSearch(amenityByNameCriteria, amenityByNameResultCallback.getBinding(), new IQueryController() { + @Override + public boolean isAborted() { + return amenityResultsCounter >= maxSearchResults || cancelled; + } + }); + + if (!cancelled) { + addrByNameSearch.performSearch(addrByNameCriteria, addrByNameResultCallback.getBinding(), new IQueryController() { + @Override + public boolean isAborted() { + return addressResultsCounter >= maxSearchResults || cancelled; + } + }); + } + + if (!cancelled) { + searchScope.processSearchResult(searchItems); + } + + System.out.println("=== Finish search"); + + return searchItems; + } + + private class AmenityResultEntry extends AmenitiesByNameSearch.ResultEntry { + protected AmenityResultEntry(ISearch.IResultEntry resultEntry) { + super(ISearch.IResultEntry.getCPtr(resultEntry), false); + } + } + + private class AddressResultEntry extends AddressesByNameSearch.ResultEntry { + protected AddressResultEntry(ISearch.IResultEntry resultEntry) { + super(ISearch.IResultEntry.getCPtr(resultEntry), false); + } + } +} \ No newline at end of file diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/IntermediateSearchRequest.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/IntermediateSearchRequest.java new file mode 100644 index 0000000000..af968db2f9 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/IntermediateSearchRequest.java @@ -0,0 +1,43 @@ +package net.osmand.core.samples.android.sample1.search.requests; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import net.osmand.core.samples.android.sample1.search.SearchAPI; +import net.osmand.core.samples.android.sample1.search.SearchAPI.SearchCallback; +import net.osmand.core.samples.android.sample1.search.items.SearchItem; +import net.osmand.core.samples.android.sample1.search.requests.SearchRequest; +import net.osmand.core.samples.android.sample1.search.tokens.SearchToken; + +import java.util.ArrayList; +import java.util.List; + +public class IntermediateSearchRequest extends SearchRequest { + + private List searchItems; + protected String keyword = ""; + + public IntermediateSearchRequest(@NonNull SearchAPI searchAPI, int maxSearchResults, @Nullable SearchCallback searchCallback) { + super(searchAPI, maxSearchResults, searchCallback); + this.searchItems = new ArrayList<>(searchAPI.getSearchItems()); + + SearchToken token = searchString.getLastToken(); + if (token != null && token.getType() == SearchToken.TokenType.NAME_FILTER) { + keyword = token.getQueryText(); + } + } + + @Override + protected List doSearch() { + List res = new ArrayList<>(); + for (SearchItem item : searchItems) { + if (cancelled) { + break; + } + if (keyword.isEmpty() || item.getName().contains(keyword)) { + res.add(item); + } + } + return res; + } +} \ No newline at end of file diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/SearchRequest.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/SearchRequest.java new file mode 100644 index 0000000000..c91170b3e0 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/requests/SearchRequest.java @@ -0,0 +1,71 @@ +package net.osmand.core.samples.android.sample1.search.requests; + +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import net.osmand.core.samples.android.sample1.search.SearchAPI; +import net.osmand.core.samples.android.sample1.search.SearchAPI.SearchCallback; +import net.osmand.core.samples.android.sample1.search.SearchScope; +import net.osmand.core.samples.android.sample1.search.SearchString; +import net.osmand.core.samples.android.sample1.search.items.SearchItem; + +import java.util.List; + +public abstract class SearchRequest { + protected SearchAPI searchAPI; + protected SearchString searchString; + protected SearchScope searchScope; + + protected int maxSearchResults; + protected Runnable onFinished; + protected SearchCallback searchCallback; + + protected boolean cancelled; + + public SearchRequest(@NonNull SearchAPI searchAPI, int maxSearchResults, @Nullable SearchCallback searchCallback) { + this.searchAPI = searchAPI; + this.searchString = searchAPI.getSearchString(); + this.searchScope= searchAPI.getSearchScope(); + this.maxSearchResults = maxSearchResults; + this.searchCallback = searchCallback; + } + + public void run() { + + new AsyncTask>() { + + @Override + protected List doInBackground(Void... params) { + return doSearch(); + } + + @Override + protected void onPostExecute(List searchItems) { + + onSearchRequestPostExecute(searchItems); + + if (onFinished != null) { + onFinished.run(); + } + + if (searchCallback != null && !cancelled) { + searchCallback.onSearchFinished(searchItems); + } + } + }.execute(); + } + + protected void onSearchRequestPostExecute(List searchItems) { + } + + protected abstract List doSearch(); + + public void cancel() { + cancelled = true; + } + + public void setOnFinishedCallback(@Nullable Runnable onFinished) { + this.onFinished = onFinished; + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/CitySearchToken.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/CitySearchToken.java new file mode 100644 index 0000000000..b77f471b0f --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/CitySearchToken.java @@ -0,0 +1,17 @@ +package net.osmand.core.samples.android.sample1.search.tokens; + +import java.math.BigInteger; + +public class CitySearchToken extends SearchToken { + + private BigInteger obfId; + + public CitySearchToken(BigInteger obfId, int startIndex, String queryText, String name) { + super(TokenType.CITY, startIndex, queryText, name); + this.obfId = obfId; + } + + public BigInteger getObfId() { + return obfId; + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/NameFilterSearchToken.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/NameFilterSearchToken.java new file mode 100644 index 0000000000..8229843423 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/NameFilterSearchToken.java @@ -0,0 +1,8 @@ +package net.osmand.core.samples.android.sample1.search.tokens; + +public class NameFilterSearchToken extends SearchToken { + + public NameFilterSearchToken(int startIndex, String queryText) { + super(TokenType.NAME_FILTER, startIndex, queryText, queryText); + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiCategorySearchToken.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiCategorySearchToken.java new file mode 100644 index 0000000000..9ac128a4d0 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiCategorySearchToken.java @@ -0,0 +1,18 @@ +package net.osmand.core.samples.android.sample1.search.tokens; + +import android.support.annotation.NonNull; + +import net.osmand.osm.PoiCategory; + +public class PoiCategorySearchToken extends SearchToken { + private PoiCategory poiCategory; + + public PoiCategorySearchToken(@NonNull PoiCategory poiCategory, int startIndex, String queryText) { + super(TokenType.POI_CATEGORY, startIndex, queryText, poiCategory.getKeyName()); + this.poiCategory = poiCategory; + } + + public PoiCategory getPoiCategory() { + return poiCategory; + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiFilterSearchToken.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiFilterSearchToken.java new file mode 100644 index 0000000000..e33e7c8c73 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiFilterSearchToken.java @@ -0,0 +1,19 @@ +package net.osmand.core.samples.android.sample1.search.tokens; + +import android.support.annotation.NonNull; + +import net.osmand.osm.PoiCategory; +import net.osmand.osm.PoiFilter; + +public class PoiFilterSearchToken extends SearchToken { + private PoiFilter poiFilter; + + public PoiFilterSearchToken(@NonNull PoiFilter poiFilter, int startIndex, String queryText) { + super(TokenType.POI_FILTER, startIndex, queryText, poiFilter.getKeyName()); + this.poiFilter = poiFilter; + } + + public PoiFilter getPoiFilter() { + return poiFilter; + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiTypeSearchToken.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiTypeSearchToken.java new file mode 100644 index 0000000000..98d33e3b8e --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PoiTypeSearchToken.java @@ -0,0 +1,19 @@ +package net.osmand.core.samples.android.sample1.search.tokens; + +import android.support.annotation.NonNull; + +import net.osmand.osm.PoiFilter; +import net.osmand.osm.PoiType; + +public class PoiTypeSearchToken extends SearchToken { + private PoiType poiType; + + public PoiTypeSearchToken(@NonNull PoiType poiType, int startIndex, String queryText) { + super(TokenType.POI_TYPE, startIndex, queryText, poiType.getKeyName()); + this.poiType = poiType; + } + + public PoiType getPoiType() { + return poiType; + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PostcodeSearchToken.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PostcodeSearchToken.java new file mode 100644 index 0000000000..2af28a47e8 --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/PostcodeSearchToken.java @@ -0,0 +1,16 @@ +package net.osmand.core.samples.android.sample1.search.tokens; + +import java.math.BigInteger; + +public class PostcodeSearchToken extends SearchToken { + private BigInteger obfId; + + public PostcodeSearchToken(BigInteger obfId, int startIndex, String queryText, String name) { + super(TokenType.POSTCODE, startIndex, queryText, name); + this.obfId = obfId; + } + + public BigInteger getObfId() { + return obfId; + } +} diff --git a/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/SearchToken.java b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/SearchToken.java new file mode 100644 index 0000000000..d947b3a26b --- /dev/null +++ b/OsmAndCore-sample/src/net/osmand/core/samples/android/sample1/search/tokens/SearchToken.java @@ -0,0 +1,56 @@ +package net.osmand.core.samples.android.sample1.search.tokens; + +public abstract class SearchToken { + + public enum TokenType { + CITY, + POSTCODE, + STREET, + BUILDING, + POI_CATEGORY, + POI_FILTER, + POI_TYPE, + LOCATION, + NAME_FILTER + } + + private TokenType type; + private int startIndex; + protected String queryText; + protected String name; + + public SearchToken(TokenType type, int startIndex, String queryText, String name) { + this.type = type; + this.startIndex = startIndex; + this.queryText = queryText; + this.name = name; + } + + public TokenType getType() { + return type; + } + + public int getStartIndex() { + return startIndex; + } + + public void setStartIndex(int startIndex) { + this.startIndex = startIndex; + } + + public String getQueryText() { + return queryText; + } + + public int getLastIndex() { + return startIndex + queryText.length() - 1; + } + + public int getQueryTextLenght() { + return queryText.length(); + } + + public String getName() { + return name; + } +}