de.danielluedecke.zettelkasten.tasks.importtasks.ImportFromZkn.java Source code

Java tutorial

Introduction

Here is the source code for de.danielluedecke.zettelkasten.tasks.importtasks.ImportFromZkn.java

Source

/*
 * Zettelkasten - nach Luhmann
 ** Copyright (C) 2001-2014 by Daniel Ldecke (http://www.danielluedecke.de)
 * 
 * Homepage: http://zettelkasten.danielluedecke.de
 * 
 * 
 * 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; either version 3 of 
 * the License, or (at your option) any later version.
 * 
 * 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, see <http://www.gnu.org/licenses/>.
 * 
 * 
 * Dieses Programm ist freie Software. Sie knnen es unter den Bedingungen der GNU
 * General Public License, wie von der Free Software Foundation verffentlicht, weitergeben
 * und/oder modifizieren, entweder gem Version 3 der Lizenz oder (wenn Sie mchten)
 * jeder spteren Version.
 * 
 * Die Verffentlichung dieses Programms erfolgt in der Hoffnung, da es Ihnen von Nutzen sein 
 * wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder 
 * der VERWENDBARKEIT FR EINEN BESTIMMTEN ZWECK. Details finden Sie in der 
 * GNU General Public License.
 * 
 * Sie sollten ein Exemplar der GNU General Public License zusammen mit diesem Programm 
 * erhalten haben. Falls nicht, siehe <http://www.gnu.org/licenses/>.
 */

package de.danielluedecke.zettelkasten.tasks.importtasks;

import de.danielluedecke.zettelkasten.database.Bookmarks;
import de.danielluedecke.zettelkasten.database.Daten;
import de.danielluedecke.zettelkasten.database.DesktopData;
import de.danielluedecke.zettelkasten.database.SearchRequests;
import de.danielluedecke.zettelkasten.database.Settings;
import de.danielluedecke.zettelkasten.database.TasksData;
import de.danielluedecke.zettelkasten.util.Constants;
import de.danielluedecke.zettelkasten.util.Tools;
import de.danielluedecke.zettelkasten.util.FileOperationsUtil;
import de.danielluedecke.zettelkasten.util.PlatformUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.IllegalAddException;
import org.jdom2.IllegalDataException;

// TODO beim Import alter Daten und bei default-timestamp auch sek. und millisek. setzen

/**
 *
 * @author Luedeke
 */
public class ImportFromZkn extends org.jdesktop.application.Task<Object, Void> {
    /**
     * Reference to the Daten object, which contains the XML data of the Zettelkasten.
     * will be passed as parameter in the constructor, see below
     */
    private Daten dataObj;
    /**
     *
     */
    private TasksData taskinfo;
    /**
     * Reference to the Bookmarks object, which contains the XML data of the bookmarks.
     * will be passed as parameter in the constructor, see below
     */
    private Bookmarks bookmarksObj;
    /**
     * Reference to the Settings object, which contains the settings like fike paths etc...
     */
    private Settings settingsObj;
    /**
     * Reference to the DesktopData object, which contains the XML data of the desktop.
     * will be passed as parameter in the constructor, see below
     */
    private DesktopData desktopObj;
    /**
     * SearchRequests object, which contains the XML data of the searchrequests and -result
     * that are related with this data file
     */
    private SearchRequests searchrequestsObj;
    /**
     * dummy variable that stores the importet data in a XML file.
     * this data is after being importtet converted to the XML structure
     * of the Daten dataObj
     */
    private Document dummyXML;
    /**
     * file path to import file
     */
    private File filepath;
    /**
     * indicates whether a conversion from ascii to unicode chars is necessary
     */
    private boolean atou;
    /**
     * indicates which type of data format should be imported.
     * refer to the Zettelkasten.view properties file (resources) to see
     * which number is which file type.
     */
    private int importType;
    /**
     * indicates whether the data should be appended to an already opened zettelkasten
     * or whether the old zettelkasten-data-file should be closed (and saved) before and
     * a new data-file should be created from the imported data
     */
    private boolean append;
    /**
     * A default timestamp for importing old datafiles. Sometimes entries of old data files may
     * not contain timestamps. so we can insert a default value here...
     */
    private String defaulttimestamp;
    /**
     *
     */
    private StringBuilder importedTypesMessage = new StringBuilder("");
    private javax.swing.JDialog parentDialog;
    private javax.swing.JLabel msgLabel;

    /**
     * get the strings for file descriptions from the resource map
     */
    private org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application
            .getInstance(de.danielluedecke.zettelkasten.ZettelkastenApp.class).getContext()
            .getResourceMap(ImportTask.class);

    /**
     * 
     * @param app
     * @param parent
     * @param label
     * @param td
     * @param d
     * @param bm
     * @param dt
     * @param sr
     * @param ac
     * @param s
     * @param syn
     * @param stn
     * @param fp
     * @param a2u
     * @param appendit
     * @param rd
     * @param dts 
     */
    public ImportFromZkn(org.jdesktop.application.Application app, javax.swing.JDialog parent,
            javax.swing.JLabel label, TasksData td, Daten d, Bookmarks bm, DesktopData dt, SearchRequests sr,
            Settings s, File fp, boolean a2u, boolean appendit, String dts) {
        super(app);
        // init of the variable either passed as parameters or initiated the first time
        dataObj = d;
        taskinfo = td;
        bookmarksObj = bm;
        desktopObj = dt;
        searchrequestsObj = sr;
        settingsObj = s;
        parentDialog = parent;
        msgLabel = label;

        filepath = fp;
        atou = a2u;
        append = appendit;
        defaulttimestamp = dts;

        if (null == defaulttimestamp) {
            defaulttimestamp = Tools.getTimeStamp();
        }
        taskinfo.setImportOk(true);
        // initiate the XML file
        dummyXML = new Document(new Element("zettelkasten"));
        // set default import message
        msgLabel.setText(resourceMap.getString("importDlgMsgImport"));
    }

    @Override
    protected Object doInBackground() {
        // Your Task's code here.  This method runs
        // on a background thread, so don't reference
        // the Swing GUI from here.

        // init of the xml element variables here, since they are
        // being used more often
        Element zettelkasten;
        Element zettel;
        Element content;
        Element keywords;
        Element author;
        Element manlinks;
        Element remarks;
        Element timestamp;
        Element hyperlinks;
        Element title;
        Element luhmann;

        // get the filelength
        final long l = filepath.length();
        final long kbl = l / 1024;
        // this counter is used for indicating the position of the progressbar
        long counter = 0;
        // this variable stores the amount of entries before the import starts. this
        // is needed when appending data and correcting links like bookmarks etc.
        int oldcount = dataObj.getCount(Daten.ZKNCOUNT);
        // init the stringbuffer
        StringBuilder buffer = new StringBuilder("");
        // init the input stream.
        InputStream is;
        // and the input streamreader.
        InputStreamReader ips = null;
        // First of all read the file. The way how the file is
        // imported depends on the filetype. the switch command
        // is used to choose the right import routine
        //
        // here begins the import of old zettelkasten data (.zkn)
        //
        //
        // what we do here is importing an ascii file of old zettelkasten data
        // an adding the imported content to a dummy xml file/document. the old zettelkasten
        // data used simple ascii strings which were separated by zeros. each zero therefor
        // indicates the beginning of a new element (e.g. content, keywords, authors, title...)
        //
        // the header of the old datafile is skipped (see below), while the data is imported and
        // assigned to the right element via switch-command. we have an index counter which tells
        // the switch command which element of the datafile has been imported recently.
        //
        // init variables
        Document zkndoc;

        // TODO entfernen von Dubletten nur bei zkn3-Dateien, nicht bei alten ZKN-Dateien?

        // try to open the file
        try {
            is = new FileInputStream(filepath);
        } catch (FileNotFoundException fileNotFoundException) {
            // display error message box
            JOptionPane.showMessageDialog(null, resourceMap.getString("importDlgFileNotFound", filepath),
                    resourceMap.getString("importDglErrTitle"), JOptionPane.PLAIN_MESSAGE);
            // leave thread
            return null;
        }
        try {
            // when the input file is in ascii format, tell the input
            // stream to convert it into unicode
            if (atou) {
                ips = new InputStreamReader(is, "cp1252");
            }
            // else use default encoding
            else {
                ips = new InputStreamReader(is);
            }
        } catch (UnsupportedEncodingException e) {
            Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
        }
        // used for converting the imported, old zettelkasten data into the
        // new xml structure
        int authorPos;
        int keywordPos;
        String dummyString;
        // if we don't want to append the data, reset the zettelkastem
        if (!append) {
            resetDataFiles();
        }
        if (ips != null)
            try {
                // needed for skipping some information of the old file format which should
                // not be imported
                boolean skip = false;
                buffer.setLength(0);
                // the read bytes are stored in this variable
                int c;
                // every data part of the old zettelkasten data
                // is separated by a 0. the first part is the
                // file version. if this value is not "2.6", we have the data
                // in a too old fileformat, which cannot be importet
                while (!skip && (c = ips.read()) != -1) {
                    // a zero indicates a new part/entry/string of the old
                    // zettelkasten datafile which is imported. so each time we
                    // find a zero, a new part of the data structure begins
                    if (c != 0) {
                        // convert the integer value into a char value
                        char chr = (char) c;
                        buffer.append(chr);
                    }
                    // leave the loop
                    else {
                        skip = true;
                    }
                    counter++;
                    // when we have a very small data-file, don't count kilobytes,
                    // but only bytes...
                    if (l > 2048) {
                        setProgress(counter / 1024, 0, kbl);
                    } else {
                        setProgress(counter, 0, l);
                    }
                }
                // now we have to check, whether the imported data has the
                // correct format, indicated by the version number "2.6"
                // if not, show error message and leave thread
                if (!buffer.toString().contains("2.6")) {
                    // log error-message
                    Constants.zknlogger.log(Level.WARNING,
                            "Failed when importing older version of Zettelkasten-data. Data-format was older than Version 2.6!");
                    // display error message box
                    JOptionPane.showMessageDialog(null, resourceMap.getString("importDlgErrOldZknData"),
                            resourceMap.getString("importDglErrTitle"), JOptionPane.PLAIN_MESSAGE);
                    // return value that indicates that an error occured
                    taskinfo.setImportOk(false);
                    // leave thread
                    return null;
                }
                // reset skip-value
                skip = false;
                buffer.setLength(0);
                // now comes a part with the filedescription of the old
                // zettelkasten file. this information is needed and should be
                // saved in the metainformation file.
                while (!skip && (c = ips.read()) != -1) {
                    // as long as the delimiter-zero is not reached, read the bytes
                    if (c != 0) {
                        // convert the integer value into a char value
                        char chr = (char) c;
                        if (Tools.isLegalJDOMChar(c)) {
                            buffer.append(chr);
                        }
                    }
                    // otherweise, transfer the buffer to the metainformation-xml-file
                    // and leave the loop
                    else {
                        try {
                            if (!append) {
                                dataObj.setZknDescription(buffer.toString());
                            } else {
                                dataObj.addZknDescription(buffer.toString());
                            }
                            skip = true;
                        }
                        // in case we have an illegal add exception...
                        catch (IllegalAddException e) {
                            // display errormessage
                            showErrorLogMsg(e.getLocalizedMessage());
                            // reset data files
                            resetDataFiles();
                            // leave task
                            return null;
                        }
                        // ...or an illegal data exception, show error log and leave thread here
                        catch (IllegalDataException e) {
                            // display errormessage
                            showErrorLogMsg(e.getLocalizedMessage());
                            // reset data files
                            resetDataFiles();
                            // leave task
                            return null;
                        }
                    }
                    counter++;
                    // when we have a very small data-file, don't count kilobytes,
                    // but only bytes...
                    if (l > 2048) {
                        setProgress(counter / 1024, 0, kbl);
                    } else {
                        setProgress(counter, 0, l);
                    }
                }
                // reset skip-value
                skip = false;
                // and read the rest of the not needed information. we now
                // have the needed filedescription and saved it in the
                // metainformation file
                while (!skip && (c = ips.read()) != -1) {
                    // a zero indicates a new part/entry/string of the old
                    // zettelkasten datafile which is imported. so each time we
                    // find a zero, a new part of the data structure begins
                    if (0 == c) {
                        skip = true;
                    }
                    counter++;
                    // when we have a very small data-file, don't count kilobytes,
                    // but only bytes...
                    if (l > 2048) {
                        setProgress(counter / 1024, 0, kbl);
                    } else {
                        setProgress(counter, 0, l);
                    }
                }

                // reset the string buffer so it contains only
                // new read data
                buffer.setLength(0);
                // this is an indicator which "counts" the separater-zeros,
                // which means that this variable indicates which part of an
                // entry is currently read (i.e. maintext, author, keywords...)
                int dataIndicator = 0;
                // get the root element of the temporary XML structure
                // this is used below to create new child elements of each entry
                zettelkasten = dummyXML.getRootElement();
                zettel = null;
                // read the file byte per byte
                while ((c = ips.read()) != -1) {
                    // as long as the read char is no "separater" (zero),
                    // it is appended to the string buffer.
                    if (c != 0) {
                        // convert the integer value into a char value
                        char chr = (char) c;
                        // if char is a return character (new line), add a html-br-tag to
                        // the string buffer
                        if (13 == c) {
                            buffer.append("[br]");
                        }
                        // in windows-ascii, each new line command consists of two bytes:
                        // 13 and 10. If a 13 was found, the new line tag (<br>) is already
                        // set, so we skip the second bye here.
                        else if (10 == c) {
                        }
                        // else append the char to the buffer
                        else if (Tools.isLegalJDOMChar(c)) {
                            buffer.append(chr);
                        }

                    } else {
                        try {
                            // every time when a new "zettel" begins, create the
                            // related child element in the XML structure
                            if (0 == dataIndicator) {
                                zettel = new Element("zettel");
                                zettelkasten.addContent(zettel);
                            }
                            // check for null reference
                            if (null == zettel) {
                                zettel = new Element("zettel");
                                zettelkasten.addContent(zettel);
                            }
                            // if the char is a zero, it marks the end of a part
                            // of an zettelkasten entry (e.g. maintext (conten), author, keyword, etc.
                            // which are all separated by zeros)
                            // now we have to create a new XML element to copy the content
                            // of the buffer to the temporary XML structure
                            switch (dataIndicator) {
                            // the content of the string buffer is the MAINTEXT (CONTENT)
                            // of an entry. copy string buffer to related XML child element
                            case 0:
                                content = new Element("content");
                                zettel.addContent(content);
                                // we have to update the list-format-elements from the
                                // old format. while in the old format a list was just surrounded
                                // by [l]-tags and each line was a new bullet point, we
                                // now surround <li>-elements arround each line. So from
                                // now on, a bullet point may contain several lines.
                                content.addContent(replaceListElements(buffer.toString()));
                                // increase dataIndicator, so next time buffer content
                                // is regarded as the next element, i.e. keyword infos
                                dataIndicator++;
                                break;
                            // the content of the string buffer are the KEYWORDS
                            // of an entry. copy string buffer to related XML child element
                            case 1:
                                keywords = new Element(Daten.ELEMENT_KEYWORD);
                                zettel.addContent(keywords);
                                keywords.addContent(buffer.toString().trim());
                                // increase dataIndicator, so next time buffer content
                                // is regarded as the next element, i.e. author infos
                                dataIndicator++;
                                break;
                            // the content of the string buffer are the AUTHOR INFOS
                            // of an entry. copy string buffer to related XML child element
                            case 2:
                                author = new Element(Daten.ELEMENT_AUTHOR);
                                zettel.addContent(author);
                                author.addContent(buffer.toString().trim());
                                // increase dataIndicator, so next time buffer content
                                // is regarded as the next element, i.e. RELATIONS/LINKS infos
                                dataIndicator++;
                                break;
                            // the content of the string buffer are the RELATIONS/LINK INFOS
                            // of an entry. These are NOT NEEDED, so skip them
                            case 3: // increase dataIndicator, so next time buffer content
                                    // is regarded as the next element, i.e. OTHER REMARKS infos
                                dataIndicator++;
                                // reset buffer
                                buffer.setLength(0);
                                break;
                            // the content of the string buffer are the OTHER REMARKS
                            // of an entry. copy string buffer to related XML child element
                            case 4:
                                remarks = new Element("remarks");
                                zettel.addContent(remarks);
                                remarks.addContent(buffer.toString());
                                // increase dataIndicator, so next time buffer content
                                // is regarded as the next element, i.e. TIMESTAMP infos
                                dataIndicator++;
                                break;
                            // the content of the string buffer is the TIME STAMP
                            // of an entry. copy string buffer to related XML child element
                            case 5:
                                timestamp = new Element("timestamp");
                                zettel.addContent(timestamp);
                                timestamp.addContent(buffer.toString());
                                // increase dataIndicator, so next time buffer content
                                // is regarded as the next element, i.e. HYPERLINKS
                                dataIndicator++;
                                break;
                            // the content of the string buffer is the entry's HYPERLINKS
                            // of an entry. copy string buffer to related XML child element
                            case 6:
                                hyperlinks = new Element("hyperlinks");
                                zettel.addContent(hyperlinks);
                                hyperlinks.addContent(buffer.toString());
                                // increase dataIndicator, so next time buffer content
                                // is regarded as the next element, i.e. TITLE
                                dataIndicator++;
                                break;
                            // the content of the string buffer is the entry's TITLE
                            // of an entry. copy string buffer to related XML child element
                            case 7:
                                title = new Element("title");
                                zettel.addContent(title);
                                title.addContent(buffer.toString().trim());
                                // RESET the dataIndicator, because now starts the next entry
                                dataIndicator = 0;
                                break;
                            }
                            // reset buffer
                            buffer.setLength(0);
                        }
                        // in case we have an illegal-add-exception...
                        catch (IllegalAddException e) {
                            // display errormessage
                            showErrorLogMsg(e.getLocalizedMessage());
                            // reset data files
                            resetDataFiles();
                            // leave task
                            return null;
                        }
                        // ...or an illegal-data-exception, show error-log and leave thread
                        catch (IllegalDataException e) {
                            // display errormessage
                            showErrorLogMsg(e.getLocalizedMessage());
                            // reset data files
                            resetDataFiles();
                            // leave task
                            return null;
                        }
                    }
                    // increase the counter for the progress bar
                    counter++;
                    // when we have a very small data-file, don't count kilobytes,
                    // but only bytes...
                    if (l > 2048) {
                        setProgress(counter / 1024, 0, kbl);
                    } else {
                        setProgress(counter, 0, l);
                    }
                }
                /*
                 * Now that we have imported the data into a xml document, we have to
                 * transfer this document into the CData class, which stores the original
                 * zettelkasten data.
                 *
                 * We have to do some conversions here. First of all, the keywords have to be
                 * extracted and the keyword datafile (see "Document keywordFile" in "CData.java")
                 * has to be created with these extracted keywords. The keyword strings in this
                 * dummy xml structure are then replaced by the index numbers (i.e. element-position)
                 * of the related keywords in the keywordFile.
                 *
                 * After that, the same procedure has to be applied to the author elements.
                 *
                 * Finally, the timestamp has to be converted.
                 */
                // get a list with all entry-elements of the importet data
                List<?> importetList = dummyXML.getRootElement().getContent();
                // and an iterator for the loop below
                Iterator<?> iterator = importetList.iterator();
                // get the size of the xml structure which was created from the importet
                // file, i.e. get the amount of entries to be converted
                int ifl = importetList.size();
                // reset the progressbar
                setProgress(0, 0, ifl);
                counter = 0;
                // set new import message, telling that data conversion is proceeded
                msgLabel.setText(resourceMap.getString("importDlgMsgConvert"));
                // create a new dummy document, where we store the converted data
                // afterwards we simply copy this document into the dataObj (Daten)
                // via "setZknData()"
                zkndoc = new Document(new Element(Daten.DOCUMENT_ZETTELKASTEN));
                zettelkasten = zkndoc.getRootElement();
                // iteration of the dummyXML file, i.e. going through every importet
                // entry and transfer it to the "Daten" class object.
                while (iterator.hasNext()) {
                    // get each entry-element
                    Element entry = (Element) iterator.next();
                    // create a new element "zettel"
                    zettel = new Element(Daten.ELEMENT_ZETTEL);
                    // and add it to the document
                    zettelkasten.addContent(zettel);
                    // add unique ID
                    zettel.setAttribute(Daten.ATTRIBUTE_ZETTEL_ID,
                            Tools.createZknID(settingsObj.getFileName()) + String.valueOf(counter));
                    // now we have to add the contents (content, authors, title, remark etc.)
                    // of this entry (zettel) to each sub element of a zettel
                    //
                    // first of all, the title
                    //
                    title = new Element(Daten.ELEMENT_TITLE);
                    zettel.addContent(title);
                    title.setText(entry.getChild(Daten.ELEMENT_TITLE).getText());
                    //
                    // now comes the content
                    //
                    content = new Element(Daten.ELEMENT_CONTENT);
                    zettel.addContent(content);
                    content.setText(entry.getChild(Daten.ELEMENT_CONTENT).getText());
                    //
                    // now comes the author
                    //
                    // extract the author
                    // first, get the author string of the imported data
                    dummyString = entry.getChild(Daten.ELEMENT_AUTHOR).getText();
                    // create empty string buffer which stores the index numbers
                    // of the converted authors
                    StringBuilder newau = new StringBuilder("");
                    // proceed only, if authors exist
                    if (!dummyString.isEmpty()) {
                        // split author-values at line separator, in case we have several authors
                        // in one entry...
                        String[] authorparts = dummyString.split("\\[br\\]");
                        // iterate array
                        for (String ap : authorparts) {
                            // trim leading and trailing spaces
                            ap = ap.trim();
                            // check whether we have any author at all...
                            if (!ap.isEmpty()) {
                                // add it to the data file
                                // and store the position of the new added author in the
                                // variable authorPos
                                authorPos = dataObj.addAuthor(ap, 1);
                                // append author index number
                                newau.append(String.valueOf(authorPos));
                                // separator for the the index numbers, since more authors
                                // and thus more index numbers might be stored in the author element
                                newau.append(",");
                            }
                        }
                        // shorten the stringbuffer by one char, since we have a
                        // superfluous comma char (see for-loop above)
                        if (newau.length() > 1) {
                            newau.setLength(newau.length() - 1);
                        }
                    }
                    // create author element
                    author = new Element(Daten.ELEMENT_AUTHOR);
                    zettel.addContent(author);
                    // store author position as string value
                    author.setText(newau.toString());
                    //
                    // now come the keywords
                    //
                    dummyString = entry.getChild(Daten.ELEMENT_KEYWORD).getText();
                    // create empty string buffer which stores the index numbers
                    // of the converted keywords
                    StringBuilder newkw = new StringBuilder("");
                    // proceed only, if keywords exist
                    if (!dummyString.isEmpty()) {
                        // create a regular expression, that separates the keyword string at each comma.
                        // furthermore, commas within double-quotes ("") are not treated as separator-char,
                        // so the user can search for sentences that include commas as well. and finally, the
                        // quotes are removed, since we don't need them...
                        Matcher mat = Pattern.compile("(\"(.*?)\"|([^,]+)),?").matcher(dummyString);
                        // create a new list that will contain each found pattern (i.e. searchterm)
                        List<String> result = new ArrayList<String>();
                        while (mat.find()) {
                            result.add(mat.group(2) == null ? mat.group(3) : mat.group(2));
                        }
                        // and copy the list to our array...
                        String[] kws = result.toArray(new String[result.size()]);
                        // convert each keyword string
                        // therefor, iterate the array
                        for (String kw : kws) {
                            // trim leading and trailing spaces
                            kw = kw.trim();
                            // only copy keyword, if we have one...
                            if (!kw.isEmpty()) {
                                // add it to the data file
                                // and store the position of the new added keyword in the
                                // variable keywordPos
                                keywordPos = dataObj.addKeyword(kw, 1);
                                // append the index number in the string buffer
                                newkw.append(String.valueOf(keywordPos));
                                // separator for the the index numbers, since more keywords
                                // and thus more index numbers might be stored in the keyword element
                                newkw.append(",");
                            }
                        }
                        // shorten the stringbuffer by one char, since we have a
                        // superfluous comma char (see for-loop above)
                        if (newkw.length() > 1) {
                            newkw.setLength(newkw.length() - 1);
                        }
                    }
                    // create keyword element
                    keywords = new Element(Daten.ELEMENT_KEYWORD);
                    zettel.addContent(keywords);
                    // store keyword index numbers
                    keywords.setText(newkw.toString());
                    //
                    // now comes the manual links to other entries
                    //
                    manlinks = new Element(Daten.ELEMENT_MANLINKS);
                    zettel.addContent(manlinks);
                    manlinks.setText("");
                    //
                    // now comes the hyperlinks
                    //
                    hyperlinks = new Element(Daten.ELEMENT_ATTACHMENTS);
                    zettel.addContent(hyperlinks);
                    // the hyperlinks in the old data format are separated
                    // by ";", so parse them into an array and add a new
                    // sub element for each hyperlink entry
                    dummyString = entry.getChild("hyperlinks").getText();
                    // only add children, if we have text...
                    if (!dummyString.isEmpty()) {
                        // when the operating-system is *not* windows, convert
                        // backslashes to slashes
                        if (!PlatformUtil.isWindows()) {
                            dummyString = dummyString.replace("\\", System.getProperty("file.separator"));
                        }
                        // parse the single hyperlinks into a string array
                        String[] hls = dummyString.split(";");
                        // add each hyperlink string
                        // therefor, iterate the array
                        for (int hcnt = 0; hcnt < hls.length; hcnt++) {
                            Element sublink = new Element(Daten.ELEMENT_ATTCHILD);
                            sublink.setText(hls[hcnt]);
                            hyperlinks.addContent(sublink);
                        }
                    }
                    //
                    // now comes the remarks
                    //
                    remarks = new Element(Daten.ELEMENT_REMARKS);
                    zettel.addContent(remarks);
                    remarks.setText(entry.getChild("remarks").getText().trim());
                    //
                    // now comes the timestamp
                    //
                    // init the dummy variables
                    String tsDay;
                    String tsMonth = "";
                    String tsYear;
                    String tsHour;
                    String tsMinute;
                    // first get the old timestamp-string
                    String ts = entry.getChild("timestamp").getText();
                    // if no value exists, clear timestamp
                    if (null == ts) {
                        ts = "";
                    }
                    // when we have an old value, convert it...
                    if (!ts.isEmpty()) {
                        // we might have two parts, the created and the edited value, divided by ";"
                        // the format is as following:
                        // "Erstellt am: Sonntag, den 03. November 2008, um 07:28 Uhr;"
                        String[] tsParts = ts.split(";");
                        // go through array
                        for (int tscount = 0; tscount < tsParts.length; tscount++) {
                            // now look for the occurence of the day
                            int start = tsParts[tscount].indexOf(", den ");
                            // if it was found, proceed
                            if (start != -1) {
                                try {
                                    // and copy the two digits after that occurence to the day-string
                                    // 6 positions after the occurence of ", den " starts the day in the string
                                    // and it's 2 chars long, so we +6 and +8 here.
                                    tsDay = tsParts[tscount].substring(start + 6, start + 8);
                                    // now look for the next space after the month-string
                                    // after +8 comes a space sign, and after that starts the month. so we look
                                    // for the first space after "+10"
                                    int end = tsParts[tscount].indexOf(" ", start + 10);
                                    // and compare the month and copy it
                                    if (tsParts[tscount].substring(start + 10, end).equalsIgnoreCase("januar")) {
                                        tsMonth = "01";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("februar")) {
                                        tsMonth = "02";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("mrz")) {
                                        tsMonth = "03";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("april")) {
                                        tsMonth = "04";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("mai")) {
                                        tsMonth = "05";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("juni")) {
                                        tsMonth = "06";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("juli")) {
                                        tsMonth = "07";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("august")) {
                                        tsMonth = "08";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("september")) {
                                        tsMonth = "09";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("oktober")) {
                                        tsMonth = "10";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("november")) {
                                        tsMonth = "11";
                                    } else if (tsParts[tscount].substring(start + 10, end)
                                            .equalsIgnoreCase("dezember")) {
                                        tsMonth = "12";
                                    }
                                    // now check out the year
                                    // exactly 3 chars after the end-value we shoukd have the lower two digits of
                                    // the year, i.e. "07" for "2007" and so on
                                    tsYear = tsParts[tscount].substring(end + 3, end + 5);
                                    // now look for the occurence of the time
                                    start = tsParts[tscount].indexOf(", um ");
                                    // 5 positions after that we have the hour, as two-digit-value
                                    tsHour = tsParts[tscount].substring(start + 5, start + 7);
                                    // 3 positions after the hour (2 digits hour, one ":") we find the minutes
                                    tsMinute = tsParts[tscount].substring(start + 8, start + 10);
                                    // so now we should have the complete created- or editedt-timestamp
                                    // and set the string for the created-subchild
                                    if (0 == tscount) {
                                        dataObj.setTimestampCreated(zettel,
                                                tsYear + tsMonth + tsDay + tsHour + tsMinute);
                                    } else {
                                        dataObj.setTimestampEdited(zettel,
                                                tsYear + tsMonth + tsDay + tsHour + tsMinute);
                                    }
                                } catch (IndexOutOfBoundsException ex) {
                                    // set creation and modification timestamp, where the
                                    // creation date is a default timestamp and no edit timestamp
                                    // is used
                                    dataObj.setTimestamp(zettel, defaulttimestamp, "");
                                }
                            } else {
                                // set creation and modification timestamp, where the
                                // creation date is a default timestamp and no edit timestamp
                                // is used
                                dataObj.setTimestamp(zettel, defaulttimestamp, "");
                            }
                        }
                    } else {
                        // set creation and modification timestamp, where the
                        // creation date is a default timestamp and no edit timestamp
                        // is used
                        dataObj.setTimestamp(zettel, defaulttimestamp, "");
                    }
                    //
                    // now comes the luhmann number
                    //
                    luhmann = new Element(Daten.ELEMENT_TRAILS);
                    zettel.addContent(luhmann);
                    luhmann.setText("");
                    // update the progressbar
                    counter++;
                    setProgress(counter, 0, ifl);
                }
                // now that all data is converted into the variable zkndoc...
                if (!append) {
                    // set this document as main zkn data...
                    dataObj.setZknData(zkndoc);
                    // update first/last attributes
                    dataObj.db_updateEntryOrderReferences();
                }
                // resp. append the zkndoc to the maindata
                else {
                    // append imported entries.
                    dataObj.appendZknData(zkndoc);
                }
                // we're done! :-)
            } catch (IOException ex) {
                // tell user that opening/importing the source file failed
                Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage());
                // display error message box
                JOptionPane.showMessageDialog(null, resourceMap.getString("importDlgErrCorruptedFile"),
                        resourceMap.getString("importDglErrTitle"), JOptionPane.PLAIN_MESSAGE);
                // return value that indicates that an error occured
                taskinfo.setImportOk(false);
                // if import of main data failed, leave
            } finally {
                try {
                    ips.close();
                } catch (IOException ex) {
                    Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage());
                }
            }

        //
        //
        // here begins the import of old bookmark data (.zkl)
        //
        //
        /*
         *
         * !!! IMPORT OF BOOKMARKS STARTS HERE !!!
         *
         * now where we have finished importing the main data
         * we should go on with importing the bookmarks
         *
         */

        // TODO Fehler beim Import? Ein Lesezeichen hatte keine Kategorie,
        // hier Standardkategorie setzen...

        // change file extension
        filepath = new File(FileOperationsUtil.setFileExtension(filepath, ".zkl"));
        // init the stringbuffer
        buffer = new StringBuilder("");
        try {
            is = new FileInputStream(filepath);
            // when the input file is in ascii format, tell the input
            // stream to convert it into unicode
            try {
                if (atou) {
                    ips = new InputStreamReader(is, "cp1252");
                } else {
                    ips = new InputStreamReader(is);
                }
            } catch (UnsupportedEncodingException e) {
                Constants.zknlogger.log(Level.WARNING, e.getLocalizedMessage());
                ips = new InputStreamReader(is);
            }

            try {
                // needed for skipping some information of the old file format which should
                // not be imported
                boolean skip = false;
                buffer.setLength(0);
                // the read bytes are stored in this variable
                int c;
                // every data part of the old zettelkasten data
                // is separated by a 0. the first part is the
                // file version. if this value is not "2.6", we have the data
                // in a too old fileformat, which cannot be importet
                while (!skip && (c = ips.read()) != -1) {
                    // a zero indicates a new part/entry/string of the old
                    // zettelkasten datafile which is imported. so each time we
                    // find a zero, a new part of the data structure begins
                    if (c != 0) {
                        // convert the integer value into a char value
                        char chr = (char) c;
                        buffer.append(chr);
                    }
                    // leave the loop
                    else {
                        skip = true;
                    }
                }
                // now we have to check, whether the imported data has the
                // correct format, indicated by the version number "2.7"
                // if not, show error message and leave thread
                if (!buffer.toString().contains("2.7")) {
                    // display error message box
                    JOptionPane.showMessageDialog(null, resourceMap.getString("importDlgErrOldBookmarkData"),
                            resourceMap.getString("importDglErrTitle"), JOptionPane.PLAIN_MESSAGE);
                    // leave thread
                    return null;
                }
                // reset skip-value
                skip = false;
                // reset buffer-value
                buffer.setLength(0);
                // now comes a part with the categories of the bookmarks. they
                // are saved as one single string, separated by commas. so we have
                // to split the string at each comma after reading
                while (!skip && (c = ips.read()) != -1) {
                    // as long as the delimiter-zero is not reached, read the bytes
                    if (c != 0) {
                        // convert the integer value into a char value
                        char chr = (char) c;
                        buffer.append(chr);
                    }
                    // otherweise, transfer the buffer to the metainformation-xml-file
                    // and leave the loop
                    else {
                        skip = true;
                    }
                }
                // parse the categories to a string array
                // we will have to use this array later, when adding the bookmarks
                String[] catarray = buffer.toString().split(",");
                // reset skip-value
                skip = false;
                // reset buffer-value
                buffer.setLength(0);
                // now comes a part with the bookmarks. they
                // are saved as one single string, separated by commas. so we have
                // to split the string at each comma after reading. the first part
                // contains the entry-number, the second the reference to the category.
                // we retrieve the category-label from the above read array "catarray"
                while (!skip && (c = ips.read()) != -1) {
                    // as long as the delimiter-zero is not reached, read the bytes
                    if (c != 0) {
                        // convert the integer value into a char value
                        char chr = (char) c;
                        if (Tools.isLegalJDOMChar(c)) {
                            buffer.append(chr);
                        }
                    }
                    // otherwise leave the loop
                    else {
                        skip = true;
                    }
                }
                // parse the buffer to an string-array, which then contains the bookmarks
                // and category-index-numbers
                String[] bmarray = buffer.toString().split(",");
                // first check, whether any categories are used. if not, add
                // a default-category
                if (catarray.length < 1) {
                    bookmarksObj.addCategory(resourceMap.getString("defaultBookmarkCategory"));
                }
                // init the catstring, used below
                String catstring;
                // now go through all importet bookmarks and add them to the
                // bookmark-class. increase loop-counter by 2, since we have to read
                // two following array-entries: X=number, X+1=category-index
                for (int cnt = 0; cnt < bmarray.length; cnt += 2) {
                    // get the entry-number which is bookmarked
                    int bmindex = Integer.parseInt(bmarray[cnt]);
                    // get the category's index-number
                    int catindex = Integer.parseInt(bmarray[cnt + 1]);
                    // if the category-index-number is out of bounds set default category
                    if (catindex < 0 || catindex >= catarray.length) {
                        catstring = resourceMap.getString("defaultBookmarkCategory");
                    }
                    // else retrieve the category-name from the catarray
                    else {
                        catstring = catarray[catindex];
                    }
                    // if the data was appended, add amount of old entries to the
                    // bookmark-number, since the new entry-numbers are "shifted"...
                    if (append) {
                        bmindex = bmindex + oldcount;
                    }
                    // now add the data to the bookmark-class. we have no comments yet,
                    // so we will pass an empty string for that parameter
                    bookmarksObj.addBookmark(bmindex, catstring, "");
                }
                // actually, we should have done everything by now. :-)
            } catch (IOException ex) {
                // tell user that opening/importing the bookmak-file failed
                Constants.zknlogger.log(Level.WARNING, ex.getLocalizedMessage());
                // display error message box
                JOptionPane.showMessageDialog(null, resourceMap.getString("importDlgErrOldBookmarkData"),
                        resourceMap.getString("importDglErrTitle"), JOptionPane.PLAIN_MESSAGE);
            } finally {
                try {
                    ips.close();
                } catch (IOException ex) {
                    Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage());
                }
            }
        } catch (FileNotFoundException fileNotFoundException) {
            // if no bookmark file was found, simply go on
        }
        return null; // return your result
    }

    @Override
    protected void succeeded(Object result) {
        // Runs on the EDT.  Update the GUI based on
        // the result computed by doInBackground().

        // after importing, the data file is modified, so the user does not
        // forget to save the data in the new fileformat.
        if (taskinfo.isImportOk()) {
            dataObj.setModified(true);
        }
    }

    @Override
    protected void finished() {
        super.finished();
        taskinfo.setImportMessage(importedTypesMessage.toString());
        // Close Window
        parentDialog.setVisible(false);
        parentDialog.dispose();
    }

    private void showErrorLogMsg(String err) {
        // log error-message
        Constants.zknlogger.log(Level.SEVERE, err);
        // get entry number, where the import possibly was at the moment
        String nr = String.valueOf(dummyXML.getRootElement().getContentSize());
        // display error message box, telling the user, whether there might have been
        // a problematic / defect entry
        JOptionPane.showMessageDialog(null, resourceMap.getString("importDlgIllegalChar", nr),
                resourceMap.getString("importDglErrTitle"), JOptionPane.PLAIN_MESSAGE);
        // return value that indicates that an error occured
        taskinfo.setImportOk(false);
    }

    private void resetDataFiles() {
        // reset the data-files
        if (!append) {
            settingsObj.setFilePath(new File(""));
            dataObj.initZettelkasten();
            desktopObj.clear();
            bookmarksObj.clear();
            searchrequestsObj.clear();
        }
    }

    /**
     * This method looks for list-format-elements and enhances them to fit to the
     * new data format. each single line is parsed into a string and surrounded by <li> elements.
     * while in the old format a list was just surrounded by [l]-tags and each line was a
     * new bullet point, we now surround <li>-elements arround each line. So from
     * now on, a bullet point may contain several lines.
     *
     * @param s (the content of an entry)
     * @return the fixed content
     */
    private String replaceListElements(String s) {
        // first, convert old tags to new tags
        s = s.replace("<k>", Constants.FORMAT_ITALIC_OPEN).replace("</k>", Constants.FORMAT_ITALIC_CLOSE)
                .replace("<f>", Constants.FORMAT_BOLD_OPEN).replace("</f>", Constants.FORMAT_BOLD_CLOSE)
                .replace("<u>", Constants.FORMAT_UNDERLINE_OPEN).replace("</u>", Constants.FORMAT_UNDERLINE_CLOSE)
                .replace("<d>", Constants.FORMAT_STRIKE_OPEN).replace("</d>", Constants.FORMAT_STRIKE_CLOSE)
                .replace("<c>", Constants.FORMAT_ALIGNCENTER_OPEN)
                .replace("</c>", Constants.FORMAT_ALIGNCENTER_CLOSE).replace("<l>", Constants.FORMAT_LIST_OPEN)
                .replace("</l>", Constants.FORMAT_LIST_CLOSE);
        // color formatting: [color #rrggbb] becomes <span style="color:#rrggbb"> ([^\\[]*)
        s = s.replaceAll("\\<color ([^\\<]*)\\>", "\\[color $1\\]");
        s = s.replace("</color>", "[/color]");
        // margins formatting: [m 0.5] becomes <span style="margin-left:0.5cm;margin-right:0.5cm">
        s = s.replaceAll("\\<m ([^\\<]*)\\>", "\\[m $1\\]");
        s = s.replace("</m>", "[/m]");
        try {
            if (!s.contains(Constants.FORMAT_LIST_OPEN)) {
                return s;
            }
        } catch (NullPointerException e) {
            return "";
        }
        // init some variables here.
        // the pos-value indicates the position from where we should start to extract from our string
        int pos = 0;
        // these two variables store the position of the beginning and end of an [l] and [/l] elements
        int l_open = 0;
        int l_close;
        // some dummy string variables
        String dummy;
        String[] listparts;
        // some dummy string variables
        StringBuilder insert = new StringBuilder("");
        StringBuilder retval = new StringBuilder("");
        // as long as we have found
        while (l_open != -1) {
            // find the next occurence of an opening-l-tag
            l_open = s.indexOf(Constants.FORMAT_LIST_OPEN, pos);
            // check whether we have found a list-tag
            if (l_open != -1) {
                // find the next occurence of an closing-l-tag after the opened l-tag
                l_close = s.indexOf(Constants.FORMAT_LIST_CLOSE, l_open);
                // if closing-tag does not exist, find new line.
                if (-1 == l_close) {
                    l_close = s.indexOf("[br]", l_open);
                }
                // if we found something, go on...
                if (l_close != -1) {
                    // copy the string between the l-tags
                    dummy = s.substring(l_open + Constants.FORMAT_LIST_OPEN.length(), l_close);
                    // more than one bullet points were formerly separated by new lines
                    // so now we split the whole string at every new line
                    listparts = dummy.split("\\[br\\]");
                    // reset the dummy insert stringbuffer, otherwise we duplicate the content
                    // from the previous loop
                    insert.setLength(0);
                    // iterate the array of lines
                    for (String lines : listparts) {
                        // surround each line with the new bullet-tag
                        insert.append(Constants.FORMAT_LISTITEM_OPEN).append(lines)
                                .append(Constants.FORMAT_LISTITEM_CLOSE);
                    }
                    // now, copy a substring from the last position we were to the found l-open-ag
                    retval.append(s.substring(pos, l_open + Constants.FORMAT_LIST_OPEN.length()));
                    // then, insert the new lines surrounded by the bullet-tag "[lp]"
                    retval.append(insert.toString());
                    // and add the close tag
                    retval.append(Constants.FORMAT_LIST_CLOSE);
                    // tell our position indicator, that we are now behind the last closed l-tag
                    // next turn, we start looking for a l-sequence at the index "pos"
                    pos = l_close + 4;
                }
                // else remove tags, since user did not properly use
                // them and return the original content, with list-tags removed
                else {
                    s = s.replace(Constants.FORMAT_LIST_OPEN, "");
                    s = s.replace(Constants.FORMAT_LIST_CLOSE, "");
                    return s;
                }
            } else {
                // finally, append the rest of the content
                retval.append(s.substring(pos));
            }
        }

        return retval.toString();
    }
}