/*
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.exp;
import CacheWolf.MainForm;
import CacheWolf.Preferences;
import CacheWolf.database.CacheHolder;
import CacheWolf.database.CacheImages;
import CacheWolf.database.CacheType;
import CacheWolf.utils.FileBugfix;
import CacheWolf.utils.STRreplace;
import CacheWolf.utils.SafeXML;
import CacheWolf.utils.URLUTF8Encoder;
import CacheWolf.utils.W1252Codec;
import com.stevesoft.ewe_pat.Regex;
import ewe.io.File;
import ewe.io.FileBase;
import ewe.io.FileOutputStream;
import ewe.io.IOException;
import ewe.io.InputStream;
import ewe.sys.Process;
import ewe.sys.Time;
import ewe.sys.Vm;
import ewe.ui.FormBase;
import ewe.util.Enumeration;
import ewe.util.Hashtable;
import ewe.util.Iterator;
import ewe.util.Map.MapEntry;
import ewe.util.Vector;
import ewe.util.mString;
import ewe.util.zip.ZipEntry;
import ewe.util.zip.ZipException;
import ewe.util.zip.ZipFile;
/**
*
* @author Kalle
* Class to create a gpx-File with links to the pictures of a
* cache, which is used as input for the POILoader from Garmin.
*/
public class POIExporter extends Exporter {
private final String endLine = "\r\n"; //<br>
private final char splitter = ',';
private final String marker = "%";
private boolean onlySpoiler, noPictures;
private int anzLogs;
Hashtable ht;
String[] nameTagElements, cmtTagElements, descTagElements;
String[] addiNameTagElements, addiCmtTagElements, addiDescTagElements;
StringBuffer result;
int picsCounter;
TemplateTable tt;
boolean hasBitmaps = false;
ZipFile poiZip = null;
final String[] categoryNames = { "_Archived", "_Disabled", "Available", "_Found", "_Owned", "_UNKNOWN" };
final byte maxIndex = 6;
final byte indexOfArchived = -6;
final byte indexOfDisabled = -5;
final byte indexOfAvailable = -4;
final byte indexOfFound = -3;
final byte indexOfOwn = -2;
final byte indexOfUnknown = -1;
Hashtable tableOfCategories = new Hashtable();
public POIExporter() {
super();
this.outputFileExtension = "*.gpx";
this.recordMethod = EXPORT_METHOD_LAT_LON;
result = new StringBuffer(1000);
tt = new TemplateTable();
useCodec = new W1252Codec();
}
private void buildOutDBs() {
// create the table for the different categories
for (int i = 0; i < categoryNames.length; i++) {
tableOfCategories.put(categoryNames[i], new Hashtable());
}
// split profileDB by categories and CacheType
Vector profileDB = MainForm.profile.cacheDB.getVectorDB();
for (int i = 0; i < profileDB.size(); i++) {
Hashtable tableOfCategory;
CacheHolder ch = (CacheHolder) profileDB.get(i);
if (ch.isVisible()) {
Byte type = new Byte(ch.getType());
if (ch.isFound()) {
tableOfCategory = (Hashtable) tableOfCategories.get(categoryNames[maxIndex + indexOfFound]);
type = new Byte((byte) (indexOfFound));
} else if (ch.isOwned()) {
tableOfCategory = (Hashtable) tableOfCategories.get(categoryNames[maxIndex + indexOfOwn]);
type = new Byte((byte) (indexOfOwn));
} else if (ch.isArchived()) {
tableOfCategory = (Hashtable) tableOfCategories.get(categoryNames[maxIndex + indexOfArchived]);
type = new Byte((byte) (indexOfArchived));
} else if (!ch.isAvailable()) {
tableOfCategory = (Hashtable) tableOfCategories.get(categoryNames[maxIndex + indexOfDisabled]);
type = new Byte((byte) (indexOfDisabled));
} else if (ch.isAvailable()) {
tableOfCategory = (Hashtable) tableOfCategories.get(categoryNames[maxIndex + indexOfDisabled]);
// available Caches are split by type
} else {
tableOfCategory = (Hashtable) tableOfCategories.get(categoryNames[maxIndex + indexOfUnknown]);
type = new Byte((byte) (indexOfUnknown));
}
Vector dbOfCacheTypeforCategory = (Vector) tableOfCategory.get(type);
if (dbOfCacheTypeforCategory == null) {
dbOfCacheTypeforCategory = new Vector();
tableOfCategory.put(type, dbOfCacheTypeforCategory);
}
dbOfCacheTypeforCategory.add(ch);
}
}
}
public void doIt() {
POIExporterScreen gui = new POIExporterScreen(exporterName);
if (gui.execute() == FormBase.IDCANCEL)
return;
this.onlySpoiler = gui.onlySpoiler();
this.noPictures = gui.noPictures();
this.anzLogs = gui.getAnzLogs();
this.nameTagElements = split(gui.getNameTagDefinitions());
this.cmtTagElements = split(gui.getCmtTagDefinitions());
this.descTagElements = split(gui.getDescTagDefinitions());
this.addiNameTagElements = split(gui.getAddiNameTagDefinitions());
this.addiCmtTagElements = split(gui.getAddiCmtTagDefinitions());
this.addiDescTagElements = split(gui.getAddiDescTagDefinitions());
if (gui.getAutoSplitByType()) {
doItStart();
String targetDir = this.getOutputPath();
if (targetDir.length() > 0) {
buildOutDBs();
try {
String bitmapFileName = FileBase.getProgramDirectory() + "/exporticons/GarminPOI.zip"; // own version
if (!(hasBitmaps = new File(bitmapFileName).exists())) {
// cw default version
bitmapFileName = FileBase.getProgramDirectory() + "/exporticons/exporticons/GarminPOI.zip";
hasBitmaps = new File(bitmapFileName).exists();
}
if (hasBitmaps)
poiZip = new ZipFile(bitmapFileName);
} catch (IOException e) {
Preferences.itself().log("GPX Export: warning GarminPOI.zip not found", e, true);
}
String profileName;
if (gui.getCreateProfileDir()) {
profileName = MainForm.profile.name;
} else {
profileName = "";
}
targetDir = targetDir + profileName + "/";
FileBugfix f = new FileBugfix(targetDir);
f.createDir();
if (gui.clearOutput()) {
Enumeration ite = this.poiZip.entries();
while (ite.hasMoreElements()) {
ZipEntry aZippedFile = (ZipEntry) ite.nextElement();
String fname = aZippedFile.getName();
FileBugfix fn = new FileBugfix(targetDir + fname);
fn.delete();
fname = STRreplace.replace(fname, ".bmp", ".gpx");
fn = new FileBugfix(targetDir + fname);
fn.delete();
}
}
for (int i = 0; i < categoryNames.length; i++) {
Hashtable tableOfCategory = (Hashtable) tableOfCategories.get(categoryNames[i]);
if (tableOfCategory.size() > 0) {
Iterator cacheTypesOfCategory = tableOfCategory.entries();
while (cacheTypesOfCategory.hasNext()) {
MapEntry cacheTypeEntry = (MapEntry) cacheTypesOfCategory.next();
DB = (Vector) cacheTypeEntry.getValue();
// skip over empty cachetypes
if (DB.size() > 0) {
// make the name for the gpx and icon
byte cacheType = ((Byte) cacheTypeEntry.getKey()).byteValue();
String name = "";
if (cacheType >= 0) {
name = CacheType.typeImageNameForId(cacheType);
} else {
if (cacheType == (indexOfFound))
name = categoryNames[maxIndex + indexOfFound];
else if (cacheType == (indexOfOwn))
name = categoryNames[maxIndex + indexOfOwn];
else if (cacheType == (indexOfUnknown))
name = categoryNames[maxIndex + indexOfUnknown];
else if (cacheType == (indexOfArchived))
name = categoryNames[maxIndex + indexOfArchived];
else if (cacheType == (indexOfDisabled))
name = categoryNames[maxIndex + indexOfDisabled];
}
if (hasBitmaps)
copyPoiIcon(targetDir, name, "", poiZip);
this.setOutputFile(targetDir + name + outputFileExtension.substring(1));
export();
}
}
}
}
doItEnd();
if (hasBitmaps)
try {
poiZip.close();
} catch (IOException e) {
}
}
} else {
DB = MainForm.profile.cacheDB.getVectorDB();
askForOutputFile();
if (outFile == null)
return;
super.doIt();
}
if (gui.doPOILoader()) {
String[] cmd;
if (gui.doPOILoaderSilent()) {
cmd = new String[2];
cmd[0] = gui.POILoaderExe();
cmd[1] = "/silent";
} else {
cmd = new String[1];
cmd[0] = gui.POILoaderExe();
}
startProcess(cmd);
}
}
/**
* Execute the command defined by cmd
*
* @param cmd
* command and options to execute. if command or options include a space quatation marks are added. this will not wirk with the java version on unix systems
* @return a handle to the process on success or null otherwise
*/
Process startProcess(String[] cmd) {
String command = "";
for (int i = 0; i < cmd.length; i++) {
if (cmd[i].indexOf(" ") > -1) {
cmd[i] = "\"" + cmd[i] + "\"";
}
command = command + cmd[i] + " ";
}
try {
return Vm.exec(command);
} catch (IOException e) {
return null;
}
}
//Overrides: export() in Exporter
public void export() {
exportHeader();
exportBody();
exportTrailer();
}
private String[] split(String elements) {
return mString.split(STRreplace.replace(STRreplace.replace(elements, "\\r", "\r"), "\\n", "\n"), splitter);
}
public String header() {
result.setLength(0);
Time tim = new Time();
result.append("<?xml version=\"1.0\" encoding=\"Windows-1252\"?>" + endLine);
result.append("<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" creator=\"CacheWolf\" version=\"1.1\"" //
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" //
+ " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">" + endLine);
result.append("<metadata>" + endLine);
result.append("<link href=\"http://www.cachewolf.de\"><text>CacheWolf</text></link>" + endLine);
tim = tim.setFormat("yyyy-MM-dd'T'HH:mm:dd'Z'");
tim = tim.setToCurrentTime();
result.append("<time>" + tim.toString() + "</time>" + endLine);
result.append("</metadata>" + endLine);
return result.toString();
}
public String record(CacheHolder ch, String lat, String lon) {
tt.set(ch);
ht = tt.toHashtable(new Regex("[,.]", "."), null, 0, 20, this.anzLogs, true, null, true, 1, "");
result.setLength(0);
picsCounter = 0;
if (ch.isAddiWpt()) {
formatAddi(ch, lat, lon);
return result.toString();
} else {
ch.getDetails();
if (!ch.detailsLoaded())
return null;
}
if (noPictures) {
formatMain(ch, lat, lon, "", "");
return result.toString();
} else {
CacheImages images = ch.getDetails().images.getDisplayImages(ch.getCode());
String alreadyDone = "";
for (int i = 0; i < images.size(); i++) {
String filename = images.get(i).getFilename();
String comment = images.get(i).getTitle();
String url = MainForm.profile.dataDir + filename;
// POILoader can only work with JPG-Files ?convert to jpg?
if (!filename.endsWith(".jpg"))
continue;
// Try to export only Spoiler
if (onlySpoiler && (comment.toLowerCase().indexOf("oiler") < 1))
continue;
// check if the file is not deleted
if (!(new File(url)).exists())
continue;
if (alreadyDone.indexOf(url) == -1) {
alreadyDone = alreadyDone + url;
picsCounter++;
formatMain(ch, lat, lon, url, comment);
}
}
if (this.picsCounter == 0) {
formatMain(ch, lat, lon, "", "");
}
}
return result.toString();
}
private void formatAddi(CacheHolder ch, String lat, String lon) {
result.append("<wpt lat=\"" + lat + "\" lon=\"" + lon + "\">").append(endLine);
result.append("<name>");
this.appendToResult(addiNameTagElements);
result.append("</name>").append(endLine);
result.append("<cmt>");
this.appendToResult(addiCmtTagElements);
result.append("</cmt>").append(endLine);
result.append("<desc>");
this.appendToResult(addiDescTagElements);
result.append("</desc>").append(endLine);
appendLastPart();
return;
}
private void formatMain(CacheHolder ch, String lat, String lon, String url, String comment) {
result.append("<wpt lat=\"" + lat + "\" lon=\"" + lon + "\">").append(endLine);
result.append("<name>");
this.appendToResult(nameTagElements);
result.append("</name>").append(endLine);
result.append("<cmt>");
this.appendToResult(cmtTagElements);
result.append("</cmt>").append(endLine);
result.append("<desc>");
this.appendToResult(descTagElements);
result.append("</desc>").append(endLine);
if (url.length() > 0) {
result.append("<link href=\"" + URLUTF8Encoder.encode(url, false) + "\"/>").append(endLine);
}
appendLastPart();
return;
}
private void appendLastPart() {
result.append("<sym>Scenic Area</sym>").append(endLine) //
.append("<extensions>").append(endLine) //
.append(" <gpxx:WaypointExtension xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\">").append(endLine) //
.append(" <gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>").append(endLine) //
.append(" </gpxx:WaypointExtension>").append(endLine) //
.append("</extensions>").append(endLine) //
.append("</wpt>").append(endLine) //
.append(endLine);
}
// Overrides: trailer() in Exporter
public String trailer() {
return "</gpx>" + endLine;
}
/**
* copy the bitmap identified by <code>prefix</code> and <code>type</code> from <code>poiZip</code> to <code>outdir</code>
*
* @param outdir
* @param type
* @param prefix
* @param poiZip
* @return true on success, false otherwise
*/
private boolean copyPoiIcon(String outdir, String type, String prefix, ZipFile poiZip) {
ZipEntry icon;
byte[] buff;
int len;
try {
icon = poiZip.getEntry(type + ".bmp");
if (icon == null)
return false; // icon not found in archive
buff = new byte[icon.getSize()];
InputStream fis = poiZip.getInputStream(icon);
FileOutputStream fos = new FileOutputStream(outdir + (FileBase.separator) + prefix + type + ".bmp");
while (0 < (len = fis.read(buff)))
fos.write(buff, 0, len);
fos.flush();
fos.close();
fis.close();
} catch (ZipException e) {
Preferences.itself().log("failed to copy icon " + type + ".bmp", e, true);
return false;
} catch (IOException e) {
Preferences.itself().log("failed to copy icon " + type + ".bmp", e, true);
return false;
}
return true;
}
private void appendToResult(String[] elements) {
for (int i = 0; i < elements.length; i++) {
if (elements[i].startsWith(marker)) {
getElementValue(elements[i].substring(1));
} else {
result.append(SafeXML.cleanGPX(elements[i]));
}
}
}
private void getElementValue(String element) {
Object obj = ht.get(element);
if (obj != null) {
if (obj instanceof String) {
result.append(SafeXML.cleanGPX(removeHtmlTags((String) obj)));
} else {
if (element.equals("ATTRIBUTES")) {
Vector attributes = (Vector) obj;
int i = 0;
for (Iterator ite = attributes.iterator(); ite.hasNext();) {
if (i != 0)
result.append(",");
Hashtable attribute = (Hashtable) ite.next();
result.append(SafeXML.cleanGPX((String) attribute.get("INFO")));
i++;
}
} else if (element.equals("LOGS")) {
Vector logs = (Vector) obj;
int i = 0;
for (Iterator ite = logs.iterator(); ite.hasNext();) {
Hashtable log = (Hashtable) ite.next();
result.append(SafeXML.cleanGPX((String) log.get("LOGGER"))) //
.append(" ").append(SafeXML.cleanGPX((String) log.get("LOGTYPE"))) //
.append(" on ").append((String) log.get("DATE")) //
.append(": ").append(SafeXML.cleanGPX(removeHtmlTags((String) log.get("MESSAGE")))) //
.append(this.endLine);
i++;
}
}
}
} else if (element.equals("PIC#")) {
if (picsCounter > 0)
result.append("" + picsCounter);
}
}
}