/** * Copyright (C) 2016 eBusiness Information * * This file is part of OSM Contributor. * * OSM Contributor 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 3 of the License, or * (at your option) any later version. * * OSM Contributor 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 OSM Contributor. If not, see <http://www.gnu.org/licenses/>. */ package io.jawg.osmcontributor.ui.managers; import android.app.Application; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.joda.time.DateTime; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import io.jawg.osmcontributor.database.PoiAssetLoader; import io.jawg.osmcontributor.database.dao.PoiDao; import io.jawg.osmcontributor.database.dao.PoiNodeRefDao; import io.jawg.osmcontributor.database.dao.PoiTagDao; import io.jawg.osmcontributor.database.dao.PoiTypeDao; import io.jawg.osmcontributor.database.dao.PoiTypeTagDao; import io.jawg.osmcontributor.database.events.DbInitializedEvent; import io.jawg.osmcontributor.database.events.InitDbEvent; import io.jawg.osmcontributor.database.helper.DatabaseHelper; import io.jawg.osmcontributor.model.entities.Poi; import io.jawg.osmcontributor.model.entities.PoiNodeRef; import io.jawg.osmcontributor.model.entities.PoiTag; import io.jawg.osmcontributor.model.entities.PoiType; import io.jawg.osmcontributor.model.entities.PoiTypeTag; import io.jawg.osmcontributor.model.events.DatabaseResetFinishedEvent; import io.jawg.osmcontributor.model.events.PleaseLoadPoiForArpiEvent; import io.jawg.osmcontributor.model.events.PleaseLoadPoiForCreationEvent; import io.jawg.osmcontributor.model.events.PleaseLoadPoiForEditionEvent; import io.jawg.osmcontributor.model.events.PleaseLoadPoiTypes; import io.jawg.osmcontributor.model.events.PleaseLoadPoisEvent; import io.jawg.osmcontributor.model.events.PleaseLoadPoisToUpdateEvent; import io.jawg.osmcontributor.model.events.PleaseRevertPoiEvent; import io.jawg.osmcontributor.model.events.PleaseRevertPoiNodeRefEvent; import io.jawg.osmcontributor.model.events.PoiForEditionLoadedEvent; import io.jawg.osmcontributor.model.events.PoiTypesLoaded; import io.jawg.osmcontributor.model.events.PoisArpiLoadedEvent; import io.jawg.osmcontributor.model.events.PoisLoadedEvent; import io.jawg.osmcontributor.model.events.PoisToUpdateLoadedEvent; import io.jawg.osmcontributor.model.events.ResetDatabaseEvent; import io.jawg.osmcontributor.model.events.ResetTypeDatabaseEvent; import io.jawg.osmcontributor.model.events.RevertFinishedEvent; import io.jawg.osmcontributor.rest.dtos.dma.H2GeoDto; import io.jawg.osmcontributor.rest.mappers.PoiTypeMapper; import io.jawg.osmcontributor.ui.events.map.ChangesInDB; import io.jawg.osmcontributor.ui.events.map.LastUsePoiTypeLoaded; import io.jawg.osmcontributor.ui.events.map.PleaseLoadLastUsedPoiType; import io.jawg.osmcontributor.ui.events.map.PleaseTellIfDbChanges; import io.jawg.osmcontributor.ui.utils.BitmapHandler; import io.jawg.osmcontributor.ui.utils.views.map.marker.LocationMarkerView; import io.jawg.osmcontributor.utils.Box; import io.jawg.osmcontributor.utils.ConfigManager; import io.jawg.osmcontributor.utils.FlavorUtils; import io.jawg.osmcontributor.utils.StringUtils; import io.jawg.osmcontributor.utils.upload.PoiUpdateWrapper; import timber.log.Timber; import static io.jawg.osmcontributor.database.helper.DatabaseHelper.loadLazyForeignCollection; /** * Manager class for POIs. * Provides a number of methods to manipulate the POIs in the database that should be used instead * of calling the {@link io.jawg.osmcontributor.database.dao.PoiDao}. */ public class PoiManager { Application application; BitmapHandler bitmapHandler; PoiDao poiDao; PoiTagDao poiTagDao; PoiNodeRefDao poiNodeRefDao; PoiTypeDao poiTypeDao; PoiTypeTagDao poiTypeTagDao; DatabaseHelper databaseHelper; ConfigManager configManager; EventBus bus; PoiAssetLoader poiAssetLoader; @Inject public PoiManager(Application application, BitmapHandler bitmapHandler, PoiDao poiDao, PoiTagDao poiTagDao, PoiNodeRefDao poiNodeRefDao, PoiTypeDao poiTypeDao, PoiTypeTagDao poiTypeTagDao, DatabaseHelper databaseHelper, ConfigManager configManager, EventBus bus, PoiAssetLoader poiAssetLoader) { this.application = application; this.bitmapHandler = bitmapHandler; this.poiDao = poiDao; this.poiTagDao = poiTagDao; this.poiNodeRefDao = poiNodeRefDao; this.poiTypeDao = poiTypeDao; this.poiTypeTagDao = poiTypeTagDao; this.databaseHelper = databaseHelper; this.configManager = configManager; this.bus = bus; this.poiAssetLoader = poiAssetLoader; } // ******************************** // ************ Events ************ // ******************************** @Subscribe(threadMode = ThreadMode.ASYNC) public void onInitDbEvent(InitDbEvent event) { if (!FlavorUtils.isPoiStorage()) { Timber.d("Initializing database ..."); initDb(); bus.postSticky(new DbInitializedEvent()); } } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseLoadPoiForEditionEvent(PleaseLoadPoiForEditionEvent event) { loadPoiForEdition(event.getPoiId()); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseLoadPoiForCreationEvent(PleaseLoadPoiForCreationEvent event) { loadPoiForCreation(event); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseLoadPoisEvent(PleaseLoadPoisEvent event) { loadPois(event); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseTellIfDbChanges(PleaseTellIfDbChanges event) { bus.post(new ChangesInDB(poiDao.countForAllChanges() > 0 || poiNodeRefDao.countAllToUpdate() > 0)); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseLoadPoisToUpdateEvent(PleaseLoadPoisToUpdateEvent event) { List<Poi> updatedPois = poiDao.queryForAllUpdated(); List<Poi> newPois = poiDao.queryForAllNew(); List<Poi> toDeletePois = poiDao.queryToDelete(); List<PoiNodeRef> wayPoiNodeRef = poiNodeRefDao.queryAllToUpdate(); List<PoiUpdateWrapper> allPois = new ArrayList<>(); for (Poi p : updatedPois) { allPois.add(new PoiUpdateWrapper(true, p, poiDao.queryForId(p.getOldPoiId()), null, PoiUpdateWrapper.PoiAction.UPDATE)); } for (Poi p : newPois) { allPois.add(new PoiUpdateWrapper(true, p, null, null, PoiUpdateWrapper.PoiAction.CREATE)); } for (Poi p : toDeletePois) { allPois.add(new PoiUpdateWrapper(true, p, poiDao.queryForId(p.getOldPoiId()), null, PoiUpdateWrapper.PoiAction.DELETED)); } for (PoiNodeRef p : wayPoiNodeRef) { allPois.add(new PoiUpdateWrapper(false, null, null, p, PoiUpdateWrapper.PoiAction.UPDATE)); } bus.post(new PoisToUpdateLoadedEvent(allPois)); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseLoadPoiTypes(PleaseLoadPoiTypes event) { bus.postSticky(new PoiTypesLoaded(getPoiTypesSortedByName())); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseLoadLastUsedPoiType(PleaseLoadLastUsedPoiType event) { bus.post(new LastUsePoiTypeLoaded(getPoiTypesSortedByLastUse())); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onResetDatabaseEvent(ResetDatabaseEvent event) { bus.post(new DatabaseResetFinishedEvent(resetDatabase())); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onResetTypeDatabaseEvent(ResetTypeDatabaseEvent event) { if (event.isByDefault()) { bus.post(new DatabaseResetFinishedEvent(resetTypesByDefault())); } else if (event.getH2GeoDto() != null) { bus.post(new DatabaseResetFinishedEvent(resetTypes(event.getH2GeoDto()))); } } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseRevertPoiEvent(PleaseRevertPoiEvent event) { Poi poi = revertPoi(event.getIdToRevert()); bus.post(new RevertFinishedEvent(poi, LocationMarkerView.MarkerType.POI)); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseRevertPoiNodeRefEvent(PleaseRevertPoiNodeRefEvent event) { PoiNodeRef poiNodeRef = revertPoiNodeRef(event.getIdToRevert()); bus.post(new RevertFinishedEvent(poiNodeRef, LocationMarkerView.MarkerType.NODE_REF)); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onPleaseLoadPoiForArpiEvent(PleaseLoadPoiForArpiEvent event) { List<Poi> pois = poiDao.queryForAllInRect(event.getBox()); bus.post(new PoisArpiLoadedEvent(pois)); } // ******************************** // ************ public ************ // ******************************** /** * Initialize the database with the data from the assets files. */ public void initDb() { if (!isDbInitialized()) { // No data, initializing from assets savePoiTypesByDefault(); savePoisFromAssets(); } Timber.d("Database initialized"); } /** * Load poi types from assets and save them in the database. */ public void savePoiTypesByDefault() { savePoiTypes(poiAssetLoader.loadPoiTypesByDefault()); } /** * Load poi types from h2GeoDto and save them in the database. * @param h2GeoDto */ public void savePoiTypesFromH2Geo(H2GeoDto h2GeoDto) { savePoiTypes(poiAssetLoader.loadPoiTypesFromH2GeoDto(h2GeoDto)); } public void savePoiTypes(List<PoiType> poiTypes) { if (poiTypes != null) { Timber.d("Loaded %s poiTypes, trying to insert them", poiTypes.size()); for (PoiType poiType : poiTypes) { Timber.d("saving poiType %s", poiType); savePoiType(poiType); Timber.d("poiType saved"); } } } /** * Load pois from assets and save them in the database. */ public void savePoisFromAssets() { List<Poi> pois = poiAssetLoader.loadPoisFromAssets(); Timber.d("Loaded %s poi, trying to insert them", pois.size()); for (Poi poi : pois) { Timber.d("saving poi %s", poi); savePoi(poi); Timber.d("poi saved"); } } /** * Method saving a poi and all the associated foreign collections. * <p/> * Do not call the DAO directly to save a poi, use this method. * * @param poi The poi to save. * @return The saved poi. */ public Poi savePoi(final Poi poi) { return databaseHelper.callInTransaction(new Callable<Poi>() { @Override public Poi call() throws Exception { return savePoiNoTransaction(poi); } }); } /** * Method saving a List of POIs and all the associated foreign collections. The saving is done in a transaction. * <p/> * Do not call the DAO directly to save a List of POIs, use this method. * * @param pois The List of POIs to save. * @return The saved List. */ public List<Poi> savePois(final List<Poi> pois) { return databaseHelper.callInTransaction(new Callable<List<Poi>>() { @Override public List<Poi> call() throws Exception { List<Poi> result = new ArrayList<>(pois.size()); for (Poi poi : pois) { result.add(savePoiNoTransaction(poi)); } return result; } }); } /** * Method saving a List of PoiNodeRefs. * * @param poiNodeRefs The List of PoiNodeReds to save. * @return The saved List. */ public List<PoiNodeRef> savePoiNodeRefs(final List<PoiNodeRef> poiNodeRefs) { return databaseHelper.callInTransaction(new Callable<List<PoiNodeRef>>() { @Override public List<PoiNodeRef> call() throws Exception { List<PoiNodeRef> result = new ArrayList<>(poiNodeRefs.size()); for (PoiNodeRef poiNodeRef : poiNodeRefs) { poiNodeRefDao.createOrUpdate(poiNodeRef); result.add(poiNodeRef); } return result; } }); } /** * Method saving a poi and all the associated foreign collections without transaction management. * <p/> * Do not call the DAO directly to save a poi, use this method. * * @param poi The poi to save * @return The saved poi * @see #savePoi(Poi) */ private Poi savePoiNoTransaction(Poi poi) { List<PoiTag> poiTagsToRemove = poiTagDao.queryByPoiId(poi.getId()); poiTagsToRemove.removeAll(poi.getTags()); for (PoiTag poiTag : poiTagsToRemove) { poiTagDao.delete(poiTag); } List<PoiNodeRef> poiNodeRefsToRemove = poiNodeRefDao.queryByPoiId(poi.getId()); poiNodeRefsToRemove.removeAll(poi.getNodeRefs()); for (PoiNodeRef poiNodeRef : poiNodeRefsToRemove) { poiNodeRefDao.delete(poiNodeRef); } poiDao.createOrUpdate(poi); if (poi.getTags() != null) { for (PoiTag poiTag : poi.getTags()) { poiTag.setPoi(poi); poiTagDao.createOrUpdate(poiTag); } } if (poi.getNodeRefs() != null) { for (PoiNodeRef poiNodeRef : poi.getNodeRefs()) { poiNodeRef.setPoi(poi); poiNodeRefDao.createOrUpdate(poiNodeRef); } } return poi; } /** * Method saving a PoiType and all the associated foreign collections. * <p/> * Do not call the DAO directly to save a PoiType, use this method. * * @param poiType The PoiType to save. * @return The saved PoiType. */ public PoiType savePoiType(final PoiType poiType) { return databaseHelper.callInTransaction(new Callable<PoiType>() { @Override public PoiType call() throws Exception { poiTypeDao.createOrUpdate(poiType); List<PoiTypeTag> poiTypeTagsToDelete = poiTypeTagDao.queryByPoiTypeId(poiType.getId()); poiTypeTagsToDelete.removeAll(poiType.getTags()); poiTypeTagDao.delete(poiTypeTagsToDelete); // Save the PoiTypeTags for (PoiTypeTag poiTypeTag : poiType.getTags()) { poiTypeTag.setPoiType(poiType); poiTypeTagDao.createOrUpdate(poiTypeTag); } return poiType; } }); } /** * Query for a Poi with a given id eagerly. * * @param id The id of the Poi to load. * @return The queried Poi. */ public Poi queryForId(Long id) { Poi poi = poiDao.queryForId(id); if (poi == null) { return null; } poiTypeDao.refresh(poi.getType()); poi.setTags(loadLazyForeignCollection(poi.getTags())); poi.getType().setTags(loadLazyForeignCollection(poi.getType().getTags())); return poi; } /** * Query for Poi with a given backendId. * * @param backendId The backendId of the Pois to load. * @return The queried Poi. */ public List<Poi> queryForBackendId(String backendId) { return poiDao.queryForBackendId(backendId); } /** * Count for POIs with the same backend Id. * * @param backendId The backend id. * @return The count of pois. */ public Long countForBackendId(String backendId) { return poiDao.countForBackendId(backendId); } /** * Delete a Poi and all PoiTags and PoiNodeRefs associated. * <p/> * Do not call the DAO directly to delete a Poi, use this method. * * @param poi The Poi to delete. */ public void deletePoi(final Poi poi) { databaseHelper.callInTransaction(new Callable<Void>() { @Override public Void call() throws Exception { Poi poiToDelete = poiDao.queryForId(poi.getId()); if (poiToDelete == null) { return null; } List<PoiNodeRef> poiNodeRefsToDelete = poiNodeRefDao.queryByPoiId(poiToDelete.getId()); List<PoiTag> poiTagsToDelete = poiTagDao.queryByPoiId(poiToDelete.getId()); Timber.d("NodeRefs to delete : %d", poiNodeRefsToDelete.size()); Timber.d("NodeTags to delete : %d", poiTagsToDelete.size()); poiTagDao.delete(poiTagsToDelete); poiNodeRefDao.delete(poiNodeRefsToDelete); poiDao.delete(poiToDelete); Timber.i("Deleted Poi %d", poiToDelete.getId()); return null; } }); } /** * Delete a PoiType and all the PoiTypeTags and POIs associated. * <p/> * Do not call the DAO directly to delete a PoiType, use this method. * * @param poiType The PoiType to delete. */ public void deletePoiType(PoiType poiType) { final Long id = poiType.getId(); databaseHelper.callInTransaction(new Callable<PoiType>() { @Override public PoiType call() throws Exception { List<Long> poiIdsToDelete = poiDao.queryAllIdsByPoiTypeId(id); Timber.d("PoiTags deleted : %d", poiTagDao.deleteByPoiIds(poiIdsToDelete)); Timber.d("PoiNodeRefs deleted : %d", poiNodeRefDao.deleteByPoiIds(poiIdsToDelete)); Timber.d("POIs deleted : %d", poiDao.deleteIds(poiIdsToDelete)); Timber.d("PoiTypeTags deleted : %d", poiTypeTagDao.deleteByPoiTypeId(id)); poiTypeDao.deleteById(id); Timber.i("Deleted PoiType id=%d", id); return null; } }); } /** * Delete all the POIs who are ways and have no PoiType ans the PoiNodeRefs associated. * <p/> * Do not call the DAO directly to delete a way, use this method. */ public void deleteAllWays() { databaseHelper.callInTransaction(new Callable<Void>() { @Override public Void call() throws Exception { List<Poi> pois = poiDao.queryForAllWaysNoType(); List<Poi> poisToDelete = new ArrayList<>(); for (Poi poi : pois) { boolean delete = true; for (PoiNodeRef poiNodeRef : poi.getNodeRefs()) { if (poiNodeRef.getUpdated()) { delete = false; } } if (delete) { poisToDelete.add(poi); poiNodeRefDao.delete(poi.getNodeRefs()); } } poiDao.delete(poisToDelete); return null; } }); } /** * Delete all the POIs who are ways and have no PoiType except the POIs in parameters. Delete also the PoiNodeRefs associated. * <p/> * Do not call the DAO directly to delete a way, use this method. * * @param exceptions The POIs who shouldn't be deleted. */ public void deleteAllWaysExcept(final List<Poi> exceptions) { databaseHelper.callInTransaction(new Callable<Void>() { @Override public Void call() throws Exception { List<Poi> pois = poiDao.queryForAllWaysNoType(); pois.removeAll(exceptions); List<Poi> poisToDelete = new ArrayList<>(); for (Poi poi : pois) { boolean delete = true; for (PoiNodeRef poiNodeRef : poi.getNodeRefs()) { if (poiNodeRef.getUpdated()) { delete = false; } } if (delete) { poisToDelete.add(poi); poiNodeRefDao.delete(poi.getNodeRefs()); } } poiDao.delete(poisToDelete); return null; } }); } /** * Merge POIs in parameters to those already in the database. * * @param remotePois The POIs to merge. */ public void mergeFromOsmPois(List<Poi> remotePois, Box box) { List<Poi> toMergePois = new ArrayList<>(); Map<String, Poi> remotePoisMap = new HashMap<>(); // Map remote Poi backend Ids for (Poi poi : remotePois) { remotePoisMap.put(poi.getBackendId(), poi); } // List matching Pois List<Poi> localPois = poiDao.queryForAllInRect(box); Map<String, Poi> localPoisMap = new ConcurrentHashMap<>(); // Map matching local Pois for (Poi localPoi : localPois) { localPoisMap.put(localPoi.getBackendId(), localPoi); } // Browse remote pois for (Poi remotePoi : remotePois) { Poi localPoi = localPoisMap.remove(remotePoi.getBackendId()); Long localVersion = -1L; // If localPoi is versioned if (localPoi != null && localPoi.getVersion() != null) { localVersion = Long.valueOf(localPoi.getVersion()); } // Compute version delta if (Long.valueOf(remotePoi.getVersion()) > localVersion) { // Remote version is newer, override existing one if (localPoi != null) { remotePoi.setId(localPoi.getId()); } // This Poi should be updated toMergePois.add(remotePoi); } } poiDao.delete(localPoisMap.values()); // savePois of either new or existing Pois savePois(toMergePois); } /** * Get the date of the last update of POIs. * * @return The date of the last update. */ public DateTime getLastUpdateDate() { return poiDao.queryForMostRecentChangeDate(); } /** * Query for all the POIs contained in the bounds defined by the box. * * @param box Bounds of the search in latitude and longitude coordinates. * @return The POIs contained in the box. */ public List<Poi> queryForAllInRect(Box box) { return poiDao.queryForAllInRect(box); } /** * Query for all POIs who are ways. * * @return The list of POIs who are ways. */ public List<Poi> queryForAllWays() { return poiDao.queryForAllWays(); } /** * Query for all the existing values of a given PoiTag. * * @param key The key of the PoiTag. * @return The list of values. */ public List<String> suggestionsForTagValue(String key, Long poiTypeId) { return poiTagDao.existingValuesForTag(key, poiTypeId); } /** * Get a Map containing all the suggestions for each tags. * <br/> * Parse the possible values of a PoiTypeTag and if there are none, query the database for all * the values for the tag. * * @param poiTypeTags The list of PoiTypeTags. * @return The map of results. */ public Map<String, List<String>> suggestionsForTagsValue(Collection<PoiTypeTag> poiTypeTags) { Map<String, List<String>> res = new HashMap<>(); for (PoiTypeTag poiTypeTag : poiTypeTags) { // If there are no possible values, load all the values in the database for the given tag name if (StringUtils.isEmpty(poiTypeTag.getPossibleValues())) { res.put(poiTypeTag.getKey(), suggestionsForTagValue(poiTypeTag.getKey(), poiTypeTag.getPoiType().getId())); } else { // Split the possible values string to a list String[] split = poiTypeTag.getPossibleValues().split(PoiTypeMapper.ITEM_SEPARATOR); List<String> values = new ArrayList<>(split.length); for (String e : values) { // FIXME add possible values as map values.add(e.split(PoiTypeMapper.VALUE_SEPARATOR)[0]); } res.put(poiTypeTag.getKey(), values); } } return res; } /** * Get all the PoiTypes in the database. * * @return A ID,PoiType map with all the PoiTypes. */ public Map<Long, PoiType> loadPoiTypes() { List<PoiType> poiTypes = poiTypeDao.queryForAll(); Map<Long, PoiType> result = new HashMap<>(); for (PoiType poiType : poiTypes) { result.put(poiType.getId(), poiType); } return result; } public List<String> loadPoiTypeKeysWithDefaultValues() { return poiTypeTagDao.queryForTagKeysWithDefaultValues(); } /** * Get the PoiType with the given id. * * @param id The id of the PoiType. * @return The PoiType. */ public PoiType getPoiType(Long id) { return poiTypeDao.queryForId(id); } /** * Get all the PoiTypes alphabetically sorted. * * @return The List of PoiTypes alphabetically sorted. */ public List<PoiType> getPoiTypesSortedByName() { return poiTypeDao.queryAllSortedByName(); } /** * Get all the PoiTypes last use sorted. * * @return The List of PoiTypes last use sorted. */ public List<PoiType> getPoiTypesSortedByLastUse() { return poiTypeDao.queryAllSortedByLastUse(); } /** * Check whether the database has been initialized. * * @return Whether the database has been initialized. */ public boolean isDbInitialized() { long count = poiTypeDao.countOf(); Timber.d("pois in database : %s", count); return count > 0; } /** * Update the PoiTypes in the database with the given List of PoiTypes. * * @param newPoiTypes The PoiTypes to update. */ public void updatePoiTypes(List<PoiType> newPoiTypes) { for (PoiType newPoiType : newPoiTypes) { PoiType byBackendId = poiTypeDao.findByBackendId(newPoiType.getBackendId()); if (byBackendId != null) { newPoiType.setId(byBackendId.getId()); } savePoiType(newPoiType); } } /** * Update the date of last use of a PoiType. * * @param id The id of the PoiType to update. */ public void updatePoiTypeLastUse(long id) { PoiType poiType = poiTypeDao.queryForId(id); if (poiType != null) { poiType.setLastUse(new DateTime()); Timber.d("Update date of : %s", poiType); poiTypeDao.createOrUpdate(poiType); } } /** * Reset the database : delete all the Pois, PoiTags and PoiNodeRefs of the database. * * @return Whether the reset was successful. */ public Boolean resetDatabase() { return databaseHelper.callInTransaction(new Callable<Boolean>() { @Override public Boolean call() throws Exception { poiDao.deleteAll(); poiNodeRefDao.deleteAll(); poiTagDao.deleteAll(); return true; } }); } /** * Reset the PoiTypes of the database : delete all the Pois, PoiTags, PoiNodeRefs, PoiTypes and PoiTypeTags of the database * then reload and save the PoiTypes from the assets. * * @return Whether the reset was successful. */ public Boolean resetTypesByDefault() { return databaseHelper.callInTransaction(new Callable<Boolean>() { @Override public Boolean call() throws Exception { Timber.d("Resetting the PoiTypes of the database"); poiDao.deleteAll(); poiNodeRefDao.deleteAll(); poiTagDao.deleteAll(); poiTypeDao.deleteAll(); poiTypeTagDao.deleteAll(); Timber.d("All Pois en PoiTypes deleted from database"); savePoiTypesByDefault(); Timber.d("Finished reloading and saving PoiTypes from assets"); return true; } }); } /** * Reset the PoiTypes of the database : delete all the Pois, PoiTags, PoiNodeRefs, PoiTypes and PoiTypeTags of the database * then reload and save the PoiTypes from the assets. * * @return Whether the reset was successful. */ public Boolean resetTypes(final H2GeoDto h2GeoDto) { return databaseHelper.callInTransaction(new Callable<Boolean>() { @Override public Boolean call() throws Exception { Timber.d("Resetting the PoiTypes of the database"); poiDao.deleteAll(); poiNodeRefDao.deleteAll(); poiTagDao.deleteAll(); poiTypeDao.deleteAll(); poiTypeTagDao.deleteAll(); Timber.d("All Pois en PoiTypes deleted from database"); savePoiTypesFromH2Geo(h2GeoDto); Timber.d("Finished reloading and saving PoiTypes from assets"); return true; } }); } // ********************************* // ************ private ************ // ********************************* /** * Send a {@link PoiForEditionLoadedEvent} containing the Poi to edit and the suggestions for the PoiTypeTags. * * @param id The id of the Poi to edit. */ private void loadPoiForEdition(Long id) { Poi poi = queryForId(id); bus.post(new PoiForEditionLoadedEvent(poi, suggestionsForTagsValue(poi.getType().getTags()))); } /** * Send a {@link PoiForEditionLoadedEvent} containing the suggestions for the PoiTypeTags and a new Poi to complete. * * @param event Event containing the position, level and PoiType of the Poi to create. */ private void loadPoiForCreation(PleaseLoadPoiForCreationEvent event) { Poi poi = new Poi(); Set<Double> level = new HashSet<>(); level.add(event.getLevel()); poi.setLevel(level); poi.setLatitude(event.getLat()); poi.setLongitude(event.getLng()); poi.setType(poiTypeDao.queryForId(event.getPoiType())); Map<String, String> defaultTags = new HashMap<>(); for (PoiTypeTag poiTypeTag : poi.getType().getTags()) { if (poiTypeTag.getValue() != null) { // default tags should be set in the corresponding POI defaultTags.put(poiTypeTag.getKey(), poiTypeTag.getValue()); } } poi.applyChanges(defaultTags); bus.post(new PoiForEditionLoadedEvent(poi, suggestionsForTagsValue(poi.getType().getTags()))); } /** * Send a {@link PoisLoadedEvent} containing all the POIs * in the Box of the {@link PleaseLoadPoisEvent}. * * @param event Event containing the box to load. */ private void loadPois(PleaseLoadPoisEvent event) { bus.post(new PoisLoadedEvent(event.getBox(), queryForAllInRect(event.getBox()))); } /** * Get the backup data and put it back in the active Poi, at the end the backup Poi is deleted. * * @param poiId The Id of the Poi to revert. */ private Poi revertPoi(Long poiId) { Poi poi = poiDao.queryForId(poiId); Poi backup = null; Long oldPoiId = poi.getOldPoiId(); if (oldPoiId != null) { backup = poiDao.queryForId(oldPoiId); // we retrieve the backup data and put it back backup.setOld(false); backup.setId(poi.getId()); savePoi(backup); //we prepare to delete the modifications poi.setId(oldPoiId); } deletePoi(poi); return backup; } /** * Get the backup data and put it back in the active NodeRefPoi * * @param poiNodeRefId The Id of the PoiNodeRef to revert. */ private PoiNodeRef revertPoiNodeRef(Long poiNodeRefId) { PoiNodeRef poiNodeRef = poiNodeRefDao.queryForId(poiNodeRefId); PoiNodeRef backup; Long oldId = poiNodeRef.getOldPoiId(); if (oldId != null) { backup = poiNodeRefDao.queryForId(oldId); poiNodeRef.setLatitude(backup.getLatitude()); poiNodeRef.setLongitude(backup.getLongitude()); poiNodeRefDao.deleteById(oldId); } poiNodeRef.setUpdated(false); poiNodeRef.setOld(false); poiNodeRef.setOldPoiId(null); poiNodeRefDao.createOrUpdate(poiNodeRef); return poiNodeRef; } public void deleteOldPoiAssociated(Poi poi) { Long oldId = poi.getOldPoiId(); if (oldId != null) { Poi old = poiDao.queryForId(oldId); if (old != null) { deletePoi(old); } poi.setOldPoiId(null); } } }