/* GNU General Public License CacheWolf is a software for PocketPC, Win and Linux that enables paperless caching. It supports the sites geocaching.com and opencaching.de Copyright (C) 2006 CacheWolf development team See http://www.cachewolf.de/ for more information. This program 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; version 2 of the License. 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package CacheWolf.database; import CacheWolf.utils.MutableInteger; import ewe.util.Comparer; import ewe.util.Hashtable; import ewe.util.Iterator; import ewe.util.Map.MapEntry; import ewe.util.Vector; /** * @author torsti * */ /** * @author torsti * */ public class CacheDB { /** Stores the CacheHolder objects */ private Vector vectorDB = new Vector(); /** Stores the reference of waypoints to index positions (in vectorDB). */ private Hashtable hashDB = new Hashtable(); /** * Gets the existing MutableInteger object from a given waypoint, or, * if not existant, creates a new one, and fills it with the given integer. * * @param waypoint * The waypoint whos MutableIntger object is to use * @param newValue * The integer value you want to assign to the object * @return The newly created or reused and freshly assigned object */ private MutableInteger getIntObj(String waypoint, int newValue) { MutableInteger obj = (MutableInteger) hashDB.get(waypoint); if (obj == null) { obj = new MutableInteger(); } obj.setInt(newValue); return obj; } public Vector getVectorDB() { return vectorDB; } /** * Gets the stored CacheHolder object by its position in the Cache List. * * @param index * Index of cache * @return CacheHolder object with corresponding index */ public CacheHolder get(int index) { if (vectorDB.size() > index) { return (CacheHolder) vectorDB.get(index); } else return null; } /** * Gets the stored CacheHolder object by its waypoint.<br> * If no such Cache exists, null is returned.<br> * * @param waypoint * Waypoint of cache we want * @return CacheHolder object with corresponding waypoint */ public CacheHolder get(String waypoint) { MutableInteger obj = (MutableInteger) hashDB.get(waypoint); if (obj == null) return null; // if there is a hash, there is also a vector return (CacheHolder) vectorDB.get(obj.getInt()); } /** * Gets the index of the cache with a given waypoint. * * @param waypoint * Waypoint of cache we want * @return Index of CacheHolder object in cache list. */ public int getIndex(String waypoint) { MutableInteger obj = (MutableInteger) hashDB.get(waypoint); int result; if (obj == null) { result = -1; } else { result = obj.getInt(); } return result; } /** * Gets the index of a certain CacheHolder object. * * @param ch * CacheHolder object * @return Index of CacheHolder object in cache list. */ public int getIndex(CacheHolder ch) { return getIndex(ch.getCode()); } /** * Sets a CacheHolder object at a certain position in the cache list. If this position * is already occupied by a cache object, this one discarded. * * @param index * Index where to set object * @param ch * CacheHolder object to set */ public void set(int index, CacheHolder ch) { CacheHolder oldObj = (CacheHolder) vectorDB.get(index); vectorDB.set(index, ch); hashDB.put(ch.getCode(), this.getIntObj(ch.getCode(), index)); if (oldObj != null && !oldObj.getCode().equals(oldObj.getCode())) { hashDB.remove(oldObj.getCode()); } } /** * Append a CacheHolder object at the end of the cache list. If a cache with same waypoint * is already existant in the cache list, then the old object is overwritten and the new object * is positioned at the position of the old object (so in this case <code>add</code> acts like <code>set</code>. * * @param ch * CacheHolder object to append */ public void add(CacheHolder ch) { if (this.getIndex(ch) > 0) { this.set(this.getIndex(ch), ch); } else { vectorDB.add(ch); hashDB.put(ch.getCode(), this.getIntObj(ch.getCode(), vectorDB.size() - 1)); } } /** * The number of caches in the cache list. * * @return number */ public int size() { return vectorDB.size(); } /** * Removes all cache objects from the list. */ public void clear() { hashDB.clear(); vectorDB.clear(); } /** * Same as <br> * <code>clear();<br>addAll(cachesA);<br>addAll(cachesB);<br></code>but optimized * to reduce object creation. <br> * Thus builds cacheDB out of caches of vectors cachesA and cachesB, added in this order. * * @param cachesA * First Vector of CacheHolder object to add to CacheDB * @param cachesB * Second Vector of CacheHolder object to add to CacheDB */ public void rebuild(Vector cachesA, Vector cachesB) { int vectorSize = vectorDB.size(); int cachesAsize = 0; int cachesBsize = 0; // First negate all hashtable position values, to distinguish the old from the new values Iterator iter = hashDB.entries(); while (iter.hasNext()) { MutableInteger mInt = (MutableInteger) ((MapEntry) iter.next()).getValue(); mInt.setInt(-mInt.getInt()); } // Then set all vector elements at the proper position for (int abc = 1; abc <= 2; abc++) { Vector cachesAB = null; int offset = 0; if (abc == 1) { cachesAB = cachesA; if (cachesA != null) cachesAsize = cachesA.size(); } else { cachesAB = cachesB; if (cachesA != null) offset = cachesA.size(); if (cachesB != null) cachesBsize = cachesB.size(); } if (cachesAB == null) continue; for (int i = offset; i < cachesAB.size() + offset; i++) { CacheHolder ch = (CacheHolder) cachesAB.get(i - offset); if (i < vectorSize) { vectorDB.set(i, ch); } else { vectorDB.add(ch); } hashDB.put(ch.getCode(), this.getIntObj(ch.getCode(), i)); } } // If there are more elements in vectorDB than in the sum of sizes of cachesA and cachesB // then the rest has to be deleted. for (int i = vectorDB.size() - 1; i >= cachesAsize + cachesBsize; i--) { vectorDB.del(i); } // Now delete any element from hashDB which still has a negative position value Vector wpToDelete = null; MapEntry me = null; iter = hashDB.entries(); while (iter.hasNext()) { me = (MapEntry) iter.next(); MutableInteger mInt = (MutableInteger) me.getValue(); if (mInt.getInt() < 0) { if (wpToDelete == null) wpToDelete = new Vector(); String wp = (String) me.getKey(); wpToDelete.add(wp); } } if (wpToDelete != null) { for (int i = 0; i < wpToDelete.size(); i++) { String wp = (String) wpToDelete.get(i); hashDB.remove(wp); } } } /** * Removes a CacheHolder object at the specified position in the cache list. The following * elements are renumbered.<br> * Additionally the cache details are unloaded and saved to file, if necessary. * * @param index * The index of element to remove */ public void removeElementAt(int index) { CacheHolder ch = this.get(index); ch.releaseCacheDetails(); vectorDB.removeElementAt(index); hashDB.remove(ch.getCode()); // When one element has been removed, we have to update the index // references in the hashtable, as the indexes of waypoints changed. for (int i = index; i < vectorDB.size(); i++) { CacheHolder ch2 = this.get(i); hashDB.put(ch2.getCode(), this.getIntObj(ch2.getCode(), i)); } } /** * Sorts the caches in the list * * @param comparer * Comparer object * @param descending * descending or not */ public void sort(Comparer comparer, boolean descending) { vectorDB.sort(comparer, descending); // When elements have been sorted we have to update the index // references in the hashtable, as the indexes of waypoints changed. for (int i = 0; i < vectorDB.size(); i++) { CacheHolder ch = this.get(i); hashDB.put(ch.getCode(), this.getIntObj(ch.getCode(), i)); } } /** * Adds the caches of one CacheDB to current one. Caches are appended at the end. * * @param caches * CacheDB to append */ public void addAll(CacheDB caches) { addAll(caches.vectorDB); } /** * Adds a Vector of CacheHolder objects to current database. Caches are appended at the end. * * @param caches * Vector of caches to append */ public void addAll(Vector caches) { int oldSize = vectorDB.size(); vectorDB.addAll(caches); for (int i = 0; i < caches.size(); i++) { int pos; CacheHolder currCache = (CacheHolder) vectorDB.get(i + oldSize); pos = i + oldSize; hashDB.put(currCache.getCode(), this.getIntObj(currCache.getCode(), pos)); } } /** * Returns the number of currently visible waypoints. <br> * As this number is not only dependent from * CacheHolder properties, but also from the state of the filter and so on, the determination * of this number always requires a count through all waypoints. So use with caution. * * @return Number of currently visible waypoints. */ public int countVisible() { int c = 0; for (int i = 0; i < vectorDB.size(); i++) { if (this.get(i).isVisible()) c++; } return c; } }