org.gtdfree.model.GTDDataXMLTools.java Source code

Java tutorial

Introduction

Here is the source code for org.gtdfree.model.GTDDataXMLTools.java

Source

/*
 *    Copyright (C) 2008-2010 Igor Kriznar
 *    
 *    This file is part of GTD-Free.
 *    
 *    GTD-Free 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.
 *    
 *    GTD-Free 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 GTD-Free.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.gtdfree.model;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.URL;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.gtdfree.ApplicationHelper;
import org.gtdfree.model.Action.ActionType;
import org.gtdfree.model.Folder.FolderType;

/**
 * @author ikesan
 *
 */
public final class GTDDataXMLTools {

    public static class DataHeader {
        public DataHeader(File file, String ver, String mod) {
            this.file = file;
            version = ver;
            if (mod != null) {
                try {
                    modified = ApplicationHelper.parseLongISO(mod);
                } catch (ParseException e) {
                    Logger.getLogger(this.getClass()).error("Parse error.", e); //$NON-NLS-1$
                }
            }
        }

        public DataHeader(File f)
                throws FileNotFoundException, XMLStreamException, javax.xml.stream.FactoryConfigurationError {
            file = f;

            InputStream in = null;
            XMLStreamReader r = null;
            try {

                in = new BufferedInputStream(new FileInputStream(f));
                r = XMLInputFactory.newInstance().createXMLStreamReader(in);
                r.nextTag();

                if ("gtd-data".equals(r.getLocalName())) { //$NON-NLS-1$
                    version = r.getAttributeValue(null, "version"); //$NON-NLS-1$
                    try {
                        modified = ApplicationHelper.parseLongISO(r.getAttributeValue(null, "modified")); //$NON-NLS-1$
                    } catch (ParseException e) {
                        Logger.getLogger(this.getClass()).error("Parse error.", e); //$NON-NLS-1$
                    }
                    if (modified == null) {
                        modified = new Date(f.lastModified());
                    }
                }

            } finally {
                if (r != null) {
                    try {
                        r.close();
                    } catch (Exception e) {
                        Logger.getLogger(this.getClass()).debug("I/O error.", e); //$NON-NLS-1$
                    }
                    if (in != null) {
                        try {
                            in.close();
                        } catch (Exception e) {
                            Logger.getLogger(this.getClass()).debug("I/O error.", e); //$NON-NLS-1$
                        }
                    }
                }
            }

        }

        private File file;
        String version;
        private Date modified;

        /**
         * @return the version
         */
        public String getVersion() {
            return version;
        }

        /**
         * @return the modified
         */
        public Date getModified() {
            return modified;
        }

        public File getFile() {
            return file;
        }

        @Override
        public String toString() {
            return file.toString() + " " + ApplicationHelper.toISODateTimeString(modified) + " " + version; //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    private final static String EOL = "\n"; //$NON-NLS-1$
    private final static String SKIP = "  "; //$NON-NLS-1$
    private final static String SKIPSKIP = "    "; //$NON-NLS-1$

    private static void _load_1_0(GTDModel model, XMLStreamReader r) throws XMLStreamException {
        if (checkTagStart(r, "folders")) { //$NON-NLS-1$

            r.nextTag();

            while (checkTagStart(r, "folder")) { //$NON-NLS-1$
                String type = r.getAttributeValue(null, "type").trim(); //$NON-NLS-1$
                Folder ff = null;
                if ("NOTE".equals(type)) { //$NON-NLS-1$
                    ff = model.getInBucketFolder();
                } else {
                    ff = model.createFolder(r.getAttributeValue(null, "name"), FolderType.valueOf(type)); //$NON-NLS-1$
                }
                r.nextTag();

                while (checkTagStart(r, "action")) { //$NON-NLS-1$
                    int i = Integer.parseInt(r.getAttributeValue(null, "id")); //$NON-NLS-1$
                    Date cr = new Date(Long.parseLong(r.getAttributeValue(null, "created"))); //$NON-NLS-1$
                    Date re = r.getAttributeValue(null, "resolved") == null ? null //$NON-NLS-1$
                            : new Date(Long.parseLong(r.getAttributeValue(null, "resolved"))); //$NON-NLS-1$
                    String d = r.getAttributeValue(null, "description"); //$NON-NLS-1$
                    if (d != null) {
                        d = d.replace("\\n", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
                    }
                    Action a = new Action(i, cr, re, d);
                    a.setResolution(Action.Resolution.toResolution(r.getAttributeValue(null, "resolution"))); //$NON-NLS-1$

                    String s = r.getAttributeValue(null, "start"); //$NON-NLS-1$
                    if (s != null)
                        a.setStart(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "remind"); //$NON-NLS-1$
                    if (s != null)
                        a.setRemind(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "due"); //$NON-NLS-1$
                    if (s != null)
                        a.setDue(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "type"); //$NON-NLS-1$
                    if (s != null)
                        a.setType(ActionType.valueOf(s));

                    s = r.getAttributeValue(null, "url"); //$NON-NLS-1$
                    if (s != null) {
                        try {
                            a.setUrl(new URL(s));
                        } catch (Exception e) {
                            Logger.getLogger(GTDDataXMLTools.class).debug("Internal error.", e); //$NON-NLS-1$
                        }
                    }

                    ff.add(a);
                    if (a.getId() > model.getLastActionID()) {
                        model.setLastActionID(a.getId());
                    }
                    findTagEnd(r, "action"); //$NON-NLS-1$
                    r.nextTag();
                }

                findTagEnd(r, "folder"); //$NON-NLS-1$
                r.nextTag();
            }

        }
    }

    private static void _load_2_0(GTDModel model, XMLStreamReader r) throws XMLStreamException {

        HashMap<Integer, Action> withProject = new HashMap<Integer, Action>();
        HashMap<Integer, Action> queued = new HashMap<Integer, Action>();

        if (checkTagStart(r, "folders")) { //$NON-NLS-1$

            r.nextTag();
            while (checkTagStart(r, "folder")) { //$NON-NLS-1$
                Folder ff;
                String id = r.getAttributeValue(null, "id"); //$NON-NLS-1$
                if (id != null) {
                    ff = model.createFolder(Integer.parseInt(id), r.getAttributeValue(null, "name"), //$NON-NLS-1$
                            FolderType.valueOf(r.getAttributeValue(null, "type"))); //$NON-NLS-1$
                } else {
                    String s = r.getAttributeValue(null, "type").replace("NOTE", "INBUCKET"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    ff = model.createFolder(r.getAttributeValue(null, "name"), FolderType.valueOf(s)); //$NON-NLS-1$
                }
                String s = r.getAttributeValue(null, "closed"); //$NON-NLS-1$
                if (s != null)
                    ff.setClosed(Boolean.parseBoolean(s));
                s = r.getAttributeValue(null, "description"); //$NON-NLS-1$
                if (s != null) {
                    s = s.replace("\\n", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
                }
                if (!ff.isInBucket()) {
                    ff.setDescription(s);
                }

                r.nextTag();

                while (checkTagStart(r, "action")) { //$NON-NLS-1$
                    int i = Integer.parseInt(r.getAttributeValue(null, "id")); //$NON-NLS-1$
                    Date cr = new Date(Long.parseLong(r.getAttributeValue(null, "created"))); //$NON-NLS-1$
                    Date re = r.getAttributeValue(null, "resolved") == null ? null //$NON-NLS-1$
                            : new Date(Long.parseLong(r.getAttributeValue(null, "resolved"))); //$NON-NLS-1$
                    String d = r.getAttributeValue(null, "description"); //$NON-NLS-1$
                    if (d != null) {
                        d = d.replace("\\n", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
                    }
                    Action a = new Action(i, cr, re, d);

                    s = r.getAttributeValue(null, "type"); //$NON-NLS-1$
                    if (s != null)
                        a.setType(ActionType.valueOf(s));

                    s = r.getAttributeValue(null, "url"); //$NON-NLS-1$
                    if (s != null) {
                        try {
                            a.setUrl(new URL(s));
                        } catch (Exception e) {
                            Logger.getLogger(GTDDataXMLTools.class).debug("Internal error.", e); //$NON-NLS-1$
                        }
                    }

                    s = r.getAttributeValue(null, "start"); //$NON-NLS-1$
                    if (s != null)
                        a.setStart(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "remind"); //$NON-NLS-1$
                    if (s != null)
                        a.setRemind(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "due"); //$NON-NLS-1$
                    if (s != null)
                        a.setDue(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "queued"); //$NON-NLS-1$
                    if (s != null)
                        a.setQueued(Boolean.parseBoolean(s));

                    s = r.getAttributeValue(null, "project"); //$NON-NLS-1$
                    if (s != null)
                        a.setProject(Integer.parseInt(s));

                    s = r.getAttributeValue(null, "priority"); //$NON-NLS-1$
                    if (s != null)
                        a.setPriority(Priority.valueOf(s));

                    a.setResolution(Action.Resolution.toResolution(r.getAttributeValue(null, "resolution"))); //$NON-NLS-1$

                    ff.add(a);

                    if (a.getProject() != null) {
                        withProject.put(a.getId(), a);
                    }

                    if (a.isQueued()) {
                        queued.put(a.getId(), a);
                    }

                    if (a.getId() > model.getLastActionID()) {
                        model.setLastActionID(a.getId());
                    }
                    findTagEnd(r, "action"); //$NON-NLS-1$
                    r.nextTag();
                }

                findTagEnd(r, "folder"); //$NON-NLS-1$
                r.nextTag();
            }
            findTagEnd(r, "folders"); //$NON-NLS-1$
            //r.nextTag();

        }

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }
        // read projects
        r.nextTag();

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        if (checkTagStart(r, "projects")) { //$NON-NLS-1$

            r.nextTag();
            while (checkTagStart(r, "project")) { //$NON-NLS-1$
                Project pp;
                String id = r.getAttributeValue(null, "id"); //$NON-NLS-1$
                if (id != null) {
                    pp = (Project) model.createFolder(Integer.parseInt(id), r.getAttributeValue(null, "name"), //$NON-NLS-1$
                            FolderType.PROJECT);
                } else {
                    pp = (Project) model.createFolder(r.getAttributeValue(null, "name"), FolderType.PROJECT); //$NON-NLS-1$
                }
                pp.setClosed(Boolean.parseBoolean(r.getAttributeValue(null, "closed"))); //$NON-NLS-1$
                pp.setGoal(r.getAttributeValue(null, "goal")); //$NON-NLS-1$

                String s = r.getAttributeValue(null, "actions"); //$NON-NLS-1$

                if (s != null && s.trim().length() > 0) {
                    String[] ss = s.trim().split(","); //$NON-NLS-1$
                    for (int i = 0; i < ss.length; i++) {
                        if (ss[i].trim().length() > 0) {
                            int ii = Integer.parseInt(ss[i].trim());
                            Action a = withProject.remove(ii);
                            if (a != null) {
                                pp.add(a);
                            }
                        }
                    }
                }
                r.nextTag();
                findTagEnd(r, "project"); //$NON-NLS-1$
                r.nextTag();
            }
            findTagEnd(r, "projects"); //$NON-NLS-1$
        }

        for (Action a : withProject.values()) {
            if (a.getProject() != null) {
                Project p = model.getProject(a.getProject());

                if (p != null) {
                    p.add(a);
                } else {
                    System.err.println("Project " + p + " in action " + a + " does not exsist."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    a.setProject(null);
                }
            }
        }

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        // read projects
        r.nextTag();

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        if (checkTagStart(r, "queue")) { //$NON-NLS-1$
            Folder f = model.getQueue();

            String s = r.getAttributeValue(null, "actions"); //$NON-NLS-1$

            if (s != null && s.trim().length() > 0) {
                String[] ss = s.trim().split(","); //$NON-NLS-1$
                for (int i = 0; i < ss.length; i++) {
                    if (ss[i].trim().length() > 0) {
                        int ii = Integer.parseInt(ss[i].trim());
                        Action a = queued.remove(ii);
                        if (a != null) {
                            f.add(a);
                        }
                    }
                }
            }
            r.nextTag();
            findTagEnd(r, "queue"); //$NON-NLS-1$
            r.nextTag();
        }

        for (Action a : queued.values()) {
            if (a.isQueued()) {
                System.err.println("Action " + a + " is queued but not in queue list."); //$NON-NLS-1$ //$NON-NLS-2$
                model.getQueue().add(a);
            }
        }

    }

    private static void _load_2_1(GTDModel model, XMLStreamReader r) throws XMLStreamException {

        HashMap<Integer, Action> withProject = new HashMap<Integer, Action>();
        HashMap<Integer, Action> queued = new HashMap<Integer, Action>();

        if (checkTagStart(r, "lists")) { //$NON-NLS-1$

            r.nextTag();
            while (checkTagStart(r, "list")) { //$NON-NLS-1$
                Folder ff;
                String id = r.getAttributeValue(null, "id"); //$NON-NLS-1$
                if (id != null) {
                    ff = model.createFolder(Integer.parseInt(id), r.getAttributeValue(null, "name"), //$NON-NLS-1$
                            FolderType.valueOf(r.getAttributeValue(null, "type"))); //$NON-NLS-1$
                } else {
                    String s = r.getAttributeValue(null, "type").replace("NOTE", "INBUCKET"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    ff = model.createFolder(r.getAttributeValue(null, "name"), FolderType.valueOf(s)); //$NON-NLS-1$
                }
                String s = r.getAttributeValue(null, "closed"); //$NON-NLS-1$
                if (s != null)
                    ff.setClosed(Boolean.parseBoolean(s));
                s = r.getAttributeValue(null, "description"); //$NON-NLS-1$
                if (s != null) {
                    s = s.replace("\\n", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
                }
                if (!ff.isInBucket()) {
                    ff.setDescription(s);
                }

                r.nextTag();

                while (checkTagStart(r, "action")) { //$NON-NLS-1$
                    int i = Integer.parseInt(r.getAttributeValue(null, "id")); //$NON-NLS-1$
                    Date cr = new Date(Long.parseLong(r.getAttributeValue(null, "created"))); //$NON-NLS-1$
                    Date re = r.getAttributeValue(null, "resolved") == null ? null //$NON-NLS-1$
                            : new Date(Long.parseLong(r.getAttributeValue(null, "resolved"))); //$NON-NLS-1$
                    String d = r.getAttributeValue(null, "description"); //$NON-NLS-1$
                    if (d != null) {
                        d = d.replace("\\n", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
                    }

                    Action a = new Action(i, cr, re, d);

                    s = r.getAttributeValue(null, "type"); //$NON-NLS-1$
                    if (s != null)
                        a.setType(ActionType.valueOf(s));

                    s = r.getAttributeValue(null, "url"); //$NON-NLS-1$
                    if (s != null) {
                        try {
                            a.setUrl(new URL(s));
                        } catch (Exception e) {
                            Logger.getLogger(GTDDataXMLTools.class).debug("Internal error.", e); //$NON-NLS-1$
                        }
                    }

                    s = r.getAttributeValue(null, "start"); //$NON-NLS-1$
                    if (s != null)
                        a.setStart(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "remind"); //$NON-NLS-1$
                    if (s != null)
                        a.setRemind(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "due"); //$NON-NLS-1$
                    if (s != null)
                        a.setDue(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "queued"); //$NON-NLS-1$
                    if (s != null)
                        a.setQueued(Boolean.parseBoolean(s));

                    s = r.getAttributeValue(null, "project"); //$NON-NLS-1$
                    if (s != null)
                        a.setProject(Integer.parseInt(s));

                    s = r.getAttributeValue(null, "priority"); //$NON-NLS-1$
                    if (s != null)
                        a.setPriority(Priority.valueOf(s));

                    a.setResolution(Action.Resolution.toResolution(r.getAttributeValue(null, "resolution"))); //$NON-NLS-1$

                    ff.add(a);

                    if (a.getProject() != null) {
                        withProject.put(a.getId(), a);
                    }

                    if (a.isQueued()) {
                        queued.put(a.getId(), a);
                    }

                    if (a.getId() > model.getLastActionID()) {
                        model.setLastActionID(a.getId());
                    }
                    findTagEnd(r, "action"); //$NON-NLS-1$
                    r.nextTag();
                }

                findTagEnd(r, "list"); //$NON-NLS-1$
                r.nextTag();
            }

            findTagEnd(r, "lists"); //$NON-NLS-1$
            //r.nextTag();
        }

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }
        // read projects
        r.nextTag();

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        if (checkTagStart(r, "projects")) { //$NON-NLS-1$

            r.nextTag();
            while (checkTagStart(r, "project")) { //$NON-NLS-1$
                Project pp;
                String id = r.getAttributeValue(null, "id"); //$NON-NLS-1$
                if (id != null) {
                    pp = (Project) model.createFolder(Integer.parseInt(id), r.getAttributeValue(null, "name"), //$NON-NLS-1$
                            FolderType.PROJECT);
                } else {
                    pp = (Project) model.createFolder(r.getAttributeValue(null, "name"), FolderType.PROJECT); //$NON-NLS-1$
                }
                pp.setClosed(Boolean.parseBoolean(r.getAttributeValue(null, "closed"))); //$NON-NLS-1$
                pp.setGoal(r.getAttributeValue(null, "goal")); //$NON-NLS-1$

                String s = r.getAttributeValue(null, "actions"); //$NON-NLS-1$

                if (s != null && s.trim().length() > 0) {
                    String[] ss = s.trim().split(","); //$NON-NLS-1$
                    for (int i = 0; i < ss.length; i++) {
                        if (ss[i].trim().length() > 0) {
                            int ii = Integer.parseInt(ss[i].trim());
                            Action a = withProject.remove(ii);
                            if (a != null) {
                                pp.add(a);
                            }
                        }
                    }
                }
                r.nextTag();
                findTagEnd(r, "project"); //$NON-NLS-1$
                r.nextTag();
            }
            findTagEnd(r, "projects"); //$NON-NLS-1$
        }

        for (Action a : withProject.values()) {
            if (a.getProject() != null) {
                Project p = model.getProject(a.getProject());

                if (p != null) {
                    p.add(a);
                } else {
                    System.err.println("Project " + p + " in action " + a + " does not exsist."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    a.setProject(null);
                }
            }
        }

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        // read projects
        r.nextTag();

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        if (checkTagStart(r, "queue")) { //$NON-NLS-1$
            Folder f = model.getQueue();

            String s = r.getAttributeValue(null, "actions"); //$NON-NLS-1$

            if (s != null && s.trim().length() > 0) {
                String[] ss = s.trim().split(","); //$NON-NLS-1$
                for (int i = 0; i < ss.length; i++) {
                    if (ss[i].trim().length() > 0) {
                        int ii = Integer.parseInt(ss[i].trim());
                        Action a = queued.remove(ii);
                        if (a != null) {
                            f.add(a);
                        }
                    }
                }
            }
            r.nextTag();
            findTagEnd(r, "queue"); //$NON-NLS-1$
            r.nextTag();
        }

        for (Action a : queued.values()) {
            if (a.isQueued()) {
                System.err.println("Action " + a + " is queued but not in queue list."); //$NON-NLS-1$ //$NON-NLS-2$
                model.getQueue().add(a);
            }
        }

    }

    private static void _load_2_2(GTDModel model, XMLStreamReader r) throws XMLStreamException {

        HashMap<Integer, Action> withProject = new HashMap<Integer, Action>();
        HashMap<Integer, Action> queued = new HashMap<Integer, Action>();

        if (checkTagStart(r, "lists")) { //$NON-NLS-1$

            r.nextTag();
            while (checkTagStart(r, "list")) { //$NON-NLS-1$
                Folder ff;
                String id = r.getAttributeValue(null, "id"); //$NON-NLS-1$
                if (id != null) {
                    ff = model.createFolder(Integer.parseInt(id), r.getAttributeValue(null, "name"), //$NON-NLS-1$
                            FolderType.valueOf(r.getAttributeValue(null, "type"))); //$NON-NLS-1$
                } else {
                    String s = r.getAttributeValue(null, "type").replace("NOTE", "INBUCKET"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    ff = model.createFolder(r.getAttributeValue(null, "name"), FolderType.valueOf(s)); //$NON-NLS-1$
                }
                String s = r.getAttributeValue(null, "closed"); //$NON-NLS-1$
                if (s != null)
                    ff.setClosed(Boolean.parseBoolean(s));

                s = StringEscapeUtils.unescapeJava(r.getAttributeValue(null, "description")); //$NON-NLS-1$

                if (!ff.isInBucket()) {
                    ff.setDescription(s);
                }

                Date cr = null, mo = null, re = null;
                s = r.getAttributeValue(null, "created"); //$NON-NLS-1$
                if (s != null) {
                    cr = new Date(Long.parseLong(s));
                }
                s = r.getAttributeValue(null, "modified"); //$NON-NLS-1$
                if (s != null) {
                    mo = new Date(Long.parseLong(s));
                }
                s = r.getAttributeValue(null, "resolved"); //$NON-NLS-1$
                if (s != null) {
                    re = new Date(Long.parseLong(s));
                }
                ff.setDates(cr, mo, re);

                r.nextTag();

                while (checkTagStart(r, "action")) { //$NON-NLS-1$
                    int i = Integer.parseInt(r.getAttributeValue(null, "id")); //$NON-NLS-1$
                    cr = new Date(Long.parseLong(r.getAttributeValue(null, "created"))); //$NON-NLS-1$
                    re = r.getAttributeValue(null, "resolved") == null ? null //$NON-NLS-1$
                            : new Date(Long.parseLong(r.getAttributeValue(null, "resolved"))); //$NON-NLS-1$
                    mo = r.getAttributeValue(null, "modified") == null ? null //$NON-NLS-1$
                            : new Date(Long.parseLong(r.getAttributeValue(null, "modified"))); //$NON-NLS-1$

                    String d = StringEscapeUtils.unescapeJava(r.getAttributeValue(null, "description")); //$NON-NLS-1$

                    Action a = new Action(i, cr, re, d, mo);

                    s = r.getAttributeValue(null, "type"); //$NON-NLS-1$
                    if (s != null)
                        a.setType(ActionType.valueOf(s));

                    s = r.getAttributeValue(null, "url"); //$NON-NLS-1$
                    if (s != null) {
                        try {
                            a.setUrl(new URL(s));
                        } catch (Exception e) {
                            Logger.getLogger(GTDDataXMLTools.class).debug("Internal error.", e); //$NON-NLS-1$
                        }
                    }

                    s = r.getAttributeValue(null, "start"); //$NON-NLS-1$
                    if (s != null)
                        a.setStart(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "remind"); //$NON-NLS-1$
                    if (s != null)
                        a.setRemind(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "due"); //$NON-NLS-1$
                    if (s != null)
                        a.setDue(new Date(Long.parseLong(s)));

                    s = r.getAttributeValue(null, "queued"); //$NON-NLS-1$
                    if (s != null)
                        a.setQueued(Boolean.parseBoolean(s));

                    s = r.getAttributeValue(null, "project"); //$NON-NLS-1$
                    if (s != null)
                        a.setProject(Integer.parseInt(s));

                    s = r.getAttributeValue(null, "priority"); //$NON-NLS-1$
                    if (s != null)
                        a.setPriority(Priority.valueOf(s));

                    a.setResolution(Action.Resolution.toResolution(r.getAttributeValue(null, "resolution"))); //$NON-NLS-1$

                    ff.add(a);

                    if (a.getProject() != null) {
                        withProject.put(a.getId(), a);
                    }

                    if (a.isQueued()) {
                        queued.put(a.getId(), a);
                    }

                    if (a.getId() > model.getLastActionID()) {
                        model.setLastActionID(a.getId());
                    }

                    findTagEnd(r, "action"); //$NON-NLS-1$
                    r.nextTag();
                }

                findTagEnd(r, "list"); //$NON-NLS-1$
                r.nextTag();
            }
            findTagEnd(r, "lists"); //$NON-NLS-1$
            //r.nextTag();
        }

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }
        // read projects
        r.nextTag();

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        if (checkTagStart(r, "projects")) { //$NON-NLS-1$

            r.nextTag();
            while (checkTagStart(r, "project")) { //$NON-NLS-1$
                Project pp;
                String id = r.getAttributeValue(null, "id"); //$NON-NLS-1$
                if (id != null) {
                    pp = (Project) model.createFolder(Integer.parseInt(id), r.getAttributeValue(null, "name"), //$NON-NLS-1$
                            FolderType.PROJECT);
                } else {
                    pp = (Project) model.createFolder(r.getAttributeValue(null, "name"), FolderType.PROJECT); //$NON-NLS-1$
                }
                pp.setClosed(Boolean.parseBoolean(r.getAttributeValue(null, "closed"))); //$NON-NLS-1$
                pp.setGoal(r.getAttributeValue(null, "goal")); //$NON-NLS-1$

                String s = StringEscapeUtils.unescapeJava(r.getAttributeValue(null, "description")); //$NON-NLS-1$
                if (s != null) {
                    pp.setDescription(s);
                }

                Date cr = null, mo = null, re = null;
                s = r.getAttributeValue(null, "created"); //$NON-NLS-1$
                if (s != null) {
                    cr = new Date(Long.parseLong(s));
                }
                s = r.getAttributeValue(null, "modified"); //$NON-NLS-1$
                if (s != null) {
                    mo = new Date(Long.parseLong(s));
                }
                s = r.getAttributeValue(null, "resolved"); //$NON-NLS-1$
                if (s != null) {
                    re = new Date(Long.parseLong(s));
                }
                pp.setDates(cr, mo, re);

                s = r.getAttributeValue(null, "actions"); //$NON-NLS-1$

                if (s != null && s.trim().length() > 0) {
                    String[] ss = s.trim().split(","); //$NON-NLS-1$
                    for (int i = 0; i < ss.length; i++) {
                        if (ss[i].trim().length() > 0) {
                            int ii = Integer.parseInt(ss[i].trim());
                            Action a = withProject.remove(ii);
                            if (a != null) {
                                pp.add(a);
                            }
                        }
                    }
                }
                r.nextTag();
                findTagEnd(r, "project"); //$NON-NLS-1$
                r.nextTag();
            }
            findTagEnd(r, "projects"); //$NON-NLS-1$
        }

        for (Action a : withProject.values()) {
            if (a.getProject() != null) {
                Project p = model.getProject(a.getProject());

                if (p != null) {
                    p.add(a);
                } else {
                    System.err.println("Project " + p + " in action " + a + " does not exsist."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    a.setProject(null);
                }
            }
        }

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        // read projects
        r.nextTag();

        if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
            return;
        }

        if (checkTagStart(r, "queue")) { //$NON-NLS-1$
            Folder f = model.getQueue();

            String s = r.getAttributeValue(null, "actions"); //$NON-NLS-1$

            if (s != null && s.trim().length() > 0) {
                String[] ss = s.trim().split(","); //$NON-NLS-1$
                for (int i = 0; i < ss.length; i++) {
                    if (ss[i].trim().length() > 0) {
                        int ii = Integer.parseInt(ss[i].trim());
                        Action a = queued.remove(ii);
                        if (a != null) {
                            f.add(a);
                        }
                    }
                }
            }
            r.nextTag();
            findTagEnd(r, "queue"); //$NON-NLS-1$
            r.nextTag();
        }

        for (Action a : queued.values()) {
            if (a.isQueued()) {
                System.err.println("Action " + a + " is queued but not in queue list."); //$NON-NLS-1$ //$NON-NLS-2$
                model.getQueue().add(a);
            }
        }

    }

    static private boolean checkTagStart(XMLStreamReader r, String tag) throws XMLStreamException {
        return tag.equals(r.getLocalName()) && r.getEventType() == XMLStreamReader.START_ELEMENT;
    }

    private static void findTagEnd(XMLStreamReader r, String tag) throws XMLStreamException {
        while (!r.getLocalName().equals(tag) || XMLStreamReader.END_ELEMENT != r.getEventType()) {
            if (r.getEventType() == XMLStreamReader.END_DOCUMENT) {
                return;
            }
            r.nextTag();
        }
    }

    static public void importFile(GTDModel model, InputStream in)
            throws XMLStreamException, FactoryConfigurationError, IOException {
        GTDModel m = new GTDModel(null);
        load(m, in);
        model.importData(m);
    }

    static public void importFile(GTDModel model, File file)
            throws XMLStreamException, FactoryConfigurationError, IOException {
        InputStream r = new FileInputStream(file);
        try {
            importFile(model, r);
        } finally {
            try {
                r.close();
            } catch (IOException e) {
                Logger.getLogger(GTDDataXMLTools.class).debug("I/O error.", e); //$NON-NLS-1$
            }
        }
    }

    static public void load(GTDModel model, File f) throws XMLStreamException, IOException {
        InputStream r = new FileInputStream(f);
        try {
            load(model, r);
        } finally {
            try {
                r.close();
            } catch (IOException e) {
                Logger.getLogger(GTDDataXMLTools.class).debug("I/O error.", e); //$NON-NLS-1$
            }
        }
    }

    static public DataHeader load(GTDModel model, InputStream in) throws XMLStreamException, IOException {

        model.setSuspendedForMultipleChanges(true);
        model.getDataRepository().suspend(true);

        XMLStreamReader r;
        try {

            // buffer size is same as default in 1.6, we explicitly request it so, not to brake if defaut changes.
            BufferedInputStream bin = new BufferedInputStream(in, 8192);
            bin.mark(8191);

            Reader rr = new InputStreamReader(bin);
            CharBuffer b = CharBuffer.allocate(96);
            rr.read(b);
            b.position(0);
            //System.out.println(b);
            Pattern pattern = Pattern.compile("<\\?.*?encoding\\s*?=.*?\\?>", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
            Matcher matcher = pattern.matcher(b);

            // reset back to start of file
            bin.reset();

            // we check if encoding is defined in xml, by the book encoding on r should be null if not defined in xml,
            // but in reality it can be arbitrary if not defined in xml. So we have to check ourselves.
            if (matcher.find()) {
                //System.out.println(matcher);
                // if defined, then XML parser will pick it up and use it
                r = XMLInputFactory.newInstance().createXMLStreamReader(bin);
                Logger.getLogger(GTDDataXMLTools.class).info("XML declared encoding: " + r.getEncoding() //$NON-NLS-1$
                        + ", system default encoding: " + Charset.defaultCharset()); //$NON-NLS-1$
            } else {
                //System.out.println(matcher);
                // if not defined, then we assume it is generated by gtd-free version 0.4 or some local editor,
                // so we assume system default encoding.
                r = XMLInputFactory.newInstance().createXMLStreamReader(new InputStreamReader(bin));
                Logger.getLogger(GTDDataXMLTools.class)
                        .info("XML assumed system default encoding: " + Charset.defaultCharset()); //$NON-NLS-1$
            }

            r.nextTag();
            if ("gtd-data".equals(r.getLocalName())) { //$NON-NLS-1$
                DataHeader dh = new DataHeader(null, r.getAttributeValue(null, "version"), //$NON-NLS-1$
                        r.getAttributeValue(null, "modified")); //$NON-NLS-1$
                if (dh.version != null) {
                    if (dh.version.equals("2.0")) { //$NON-NLS-1$
                        r.nextTag();
                        _load_2_0(model, r);
                        return dh;
                    }
                }
                String s = r.getAttributeValue(null, "lastActionID"); //$NON-NLS-1$
                if (s != null) {
                    try {
                        model.setLastActionID(Integer.parseInt(s));
                    } catch (Exception e) {
                        Logger.getLogger(GTDDataXMLTools.class).debug("Internal error.", e); //$NON-NLS-1$
                    }
                }
                if (dh.version != null) {
                    if (dh.version.equals("2.1")) { //$NON-NLS-1$
                        r.nextTag();
                        _load_2_1(model, r);
                        return dh;

                    }
                    if (dh.version.startsWith("2.2")) { //$NON-NLS-1$
                        r.nextTag();
                        _load_2_2(model, r);
                        return dh;
                    }
                }
                throw new IOException("XML gtd-free data with version number " + dh.version //$NON-NLS-1$
                        + " can not be imported. Data version is newer then supported versions. Update your GTD-Free application to latest version."); //$NON-NLS-1$
            }

            _load_1_0(model, r);

            return null;

        } catch (XMLStreamException e) {
            if (e.getNestedException() != null) {
                Logger.getLogger(GTDDataXMLTools.class).debug("Parse error.", e.getNestedException()); //$NON-NLS-1$
            } else {
                Logger.getLogger(GTDDataXMLTools.class).debug("Parse error.", e); //$NON-NLS-1$
            }
            throw e;
        } catch (IOException e) {
            throw e;
        } finally {
            model.setSuspendedForMultipleChanges(false);
            model.getDataRepository().suspend(false);
        }

    }

    static public void store(GTDModel model, File f, ActionFilter filter)
            throws IOException, XMLStreamException, FactoryConfigurationError {
        BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream(f));
        store(model, bw, filter);
        bw.close();
    }

    static public void store(GTDModel model, File f)
            throws IOException, XMLStreamException, FactoryConfigurationError {
        store(model, f, new DummyFilter(true));
    }

    static public void store(GTDModel model, OutputStream out, ActionFilter filter)
            throws IOException, XMLStreamException, FactoryConfigurationError {

        if (filter == null) {
            filter = new DummyFilter(true);
        }
        XMLStreamWriter w = XMLOutputFactory.newInstance().createXMLStreamWriter(out, "UTF-8"); //$NON-NLS-1$

        w.writeStartDocument("UTF-8", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$

        w.writeCharacters(EOL);
        w.writeCharacters(EOL);

        w.writeStartElement("gtd-data"); //$NON-NLS-1$
        w.writeAttribute("version", "2.2"); //$NON-NLS-1$ //$NON-NLS-2$
        w.writeAttribute("modified", ApplicationHelper.formatLongISO(new Date())); //$NON-NLS-1$
        w.writeAttribute("lastActionID", Integer.toString(model.getLastActionID())); //$NON-NLS-1$
        w.writeCharacters(EOL);
        w.writeCharacters(EOL);

        // Write folders

        Folder[] fn = model.toFoldersArray();
        w.writeStartElement("lists"); //$NON-NLS-1$
        w.writeCharacters(EOL);

        for (int i = 0; i < fn.length; i++) {
            Folder ff = fn[i];
            if (ff.isMeta() || !filter.isAcceptable(ff, null)) {
                continue;
            }
            w.writeCharacters(SKIP);
            w.writeStartElement("list"); //$NON-NLS-1$
            w.writeAttribute("id", String.valueOf(ff.getId())); //$NON-NLS-1$
            w.writeAttribute("name", ff.getName()); //$NON-NLS-1$
            w.writeAttribute("type", ff.getType().toString()); //$NON-NLS-1$
            w.writeAttribute("closed", Boolean.toString(ff.isClosed())); //$NON-NLS-1$
            if (ff.getCreated() != null)
                w.writeAttribute("created", Long.toString(ff.getCreated().getTime())); //$NON-NLS-1$
            if (ff.getModified() != null)
                w.writeAttribute("modified", Long.toString(ff.getModified().getTime())); //$NON-NLS-1$
            if (ff.getResolved() != null)
                w.writeAttribute("resolved", Long.toString(ff.getResolved().getTime())); //$NON-NLS-1$
            if (!ff.isInBucket() && ff.getDescription() != null) {
                w.writeAttribute("description", ApplicationHelper.escapeControls(ff.getDescription())); //$NON-NLS-1$
            }
            w.writeCharacters(EOL);

            for (Action a : ff) {

                if (!filter.isAcceptable(ff, a)) {
                    continue;
                }

                w.writeCharacters(SKIPSKIP);
                w.writeStartElement("action"); //$NON-NLS-1$
                w.writeAttribute("id", Integer.toString(a.getId())); //$NON-NLS-1$
                w.writeAttribute("created", Long.toString(a.getCreated().getTime())); //$NON-NLS-1$
                w.writeAttribute("resolution", a.getResolution().toString()); //$NON-NLS-1$
                if (a.getResolved() != null)
                    w.writeAttribute("resolved", Long.toString(a.getResolved().getTime())); //$NON-NLS-1$
                if (a.getModified() != null)
                    w.writeAttribute("modified", Long.toString(a.getModified().getTime())); //$NON-NLS-1$
                if (a.getDescription() != null)
                    w.writeAttribute("description", ApplicationHelper.escapeControls(a.getDescription())); //$NON-NLS-1$
                if (a.getStart() != null)
                    w.writeAttribute("start", Long.toString(a.getStart().getTime())); //$NON-NLS-1$
                if (a.getRemind() != null)
                    w.writeAttribute("remind", Long.toString(a.getRemind().getTime())); //$NON-NLS-1$
                if (a.getDue() != null)
                    w.writeAttribute("due", Long.toString(a.getDue().getTime())); //$NON-NLS-1$
                if (a.getType() != null)
                    w.writeAttribute("type", a.getType().toString()); //$NON-NLS-1$
                if (a.getUrl() != null)
                    w.writeAttribute("url", a.getUrl().toString()); //$NON-NLS-1$
                if (a.isQueued())
                    w.writeAttribute("queued", Boolean.toString(a.isQueued())); //$NON-NLS-1$
                if (a.getProject() != null)
                    w.writeAttribute("project", a.getProject().toString()); //$NON-NLS-1$
                if (a.getPriority() != null)
                    w.writeAttribute("priority", a.getPriority().toString()); //$NON-NLS-1$
                w.writeEndElement();
                w.writeCharacters(EOL);
            }
            w.writeCharacters(SKIP);
            w.writeEndElement();
            w.writeCharacters(EOL);
        }
        w.writeEndElement();
        w.writeCharacters(EOL);

        // Write projects
        Project[] pn = model.toProjectsArray();
        w.writeStartElement("projects"); //$NON-NLS-1$
        w.writeCharacters(EOL);

        for (int i = 0; i < pn.length; i++) {
            Project ff = pn[i];

            if (!filter.isAcceptable(ff, null)) {
                continue;
            }

            w.writeCharacters(SKIP);
            w.writeStartElement("project"); //$NON-NLS-1$
            w.writeAttribute("id", String.valueOf(ff.getId())); //$NON-NLS-1$
            w.writeAttribute("name", ff.getName()); //$NON-NLS-1$
            w.writeAttribute("closed", String.valueOf(ff.isClosed())); //$NON-NLS-1$
            if (ff.getCreated() != null)
                w.writeAttribute("created", Long.toString(ff.getCreated().getTime())); //$NON-NLS-1$
            if (ff.getModified() != null)
                w.writeAttribute("modified", Long.toString(ff.getModified().getTime())); //$NON-NLS-1$
            if (ff.getResolved() != null)
                w.writeAttribute("resolved", Long.toString(ff.getResolved().getTime())); //$NON-NLS-1$

            if (ff.getDescription() != null) {
                w.writeAttribute("description", ApplicationHelper.escapeControls(ff.getDescription())); //$NON-NLS-1$
            }

            StringBuilder sb = new StringBuilder();

            for (Action a : ff) {
                if (!filter.isAcceptable(ff, a)) {
                    continue;
                }
                if (sb.length() > 0) {
                    sb.append(","); //$NON-NLS-1$
                }
                sb.append(a.getId());
            }
            w.writeAttribute("actions", sb.toString()); //$NON-NLS-1$
            w.writeEndElement();
            w.writeCharacters(EOL);
        }
        w.writeEndElement();
        w.writeCharacters(EOL);

        // Write queue
        Folder f = model.getQueue();

        if (filter.isAcceptable(f, null)) {
            w.writeStartElement("queue"); //$NON-NLS-1$
            w.writeAttribute("id", String.valueOf(f.getId())); //$NON-NLS-1$
            w.writeAttribute("name", f.getName()); //$NON-NLS-1$

            StringBuilder sb = new StringBuilder();
            Iterator<Action> i = f.iterator();
            if (i.hasNext()) {
                sb.append(i.next().getId());
            }
            while (i.hasNext()) {
                sb.append(","); //$NON-NLS-1$
                sb.append(i.next().getId());
            }
            w.writeAttribute("actions", sb.toString()); //$NON-NLS-1$
            w.writeEndElement();
            w.writeCharacters(EOL);
        }

        // containers
        w.writeEndElement();
        w.writeEndDocument();

        w.flush();
        w.close();

        //changed=false;

    }

}