package com.fsck.k9.preferences;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;

import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.helper.DateFormatter;

public class StorageImporter {
    public static int importPreferences(Context context, String fileName, String encryptionKey) throws StorageImportExportException {
        try {
            InputStream is = new FileInputStream(fileName);
            return importPreferences(context, is, encryptionKey);
        } catch (FileNotFoundException fnfe) {
            throw new StorageImportExportException("Failure reading settings file " + fileName, fnfe);
        }
    }
    public static int importPreferences(Context context, InputStream is, String encryptionKey) throws StorageImportExportException {
        try {
            Preferences preferences = Preferences.getPreferences(context);
            SharedPreferences storage = preferences.getPreferences();
            SharedPreferences.Editor editor = storage.edit();

            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            XMLReader xr = sp.getXMLReader();
            StorageImporterHandler handler = new StorageImporterHandler();
            xr.setContentHandler(handler);

            xr.parse(new InputSource(is));
            is.close();

            Element dataset = handler.getRootElement();
            String version = dataset.attributes.get("version");
            Log.i(K9.LOG_TAG, "Got settings file version " + version);


            IStorageImporter storageImporter = null;
            if ("1".equals(version)) {
                storageImporter = new StorageImporterVersion1();
            } else {
                throw new StorageImportExportException("Unable to read file of version " + version
                                                       + "; (only version 1 is readable)");
            }
            int numAccounts = 0;
            if (storageImporter != null) {
                String data = dataset.data.toString();
                numAccounts = storageImporter.importPreferences(preferences, editor, data, encryptionKey);
            }
            editor.commit();
            Preferences.getPreferences(context).refreshAccounts();
            DateFormatter.clearChosenFormat();
            K9.loadPrefs(Preferences.getPreferences(context));
            return numAccounts;
        } catch (SAXException se) {
            throw new StorageImportExportException("Failure reading settings file", se);
        } catch (IOException ie) {
            throw new StorageImportExportException("Failure reading settings file", ie);
        } catch (ParserConfigurationException pce) {
            throw new StorageImportExportException("Failure reading settings file", pce);
        }
    }

    private static class Element {
        String name;
        Map<String, String> attributes = new HashMap<String, String>();
        Map<String, Element> subElements = new HashMap<String, Element>();
        StringBuilder data = new StringBuilder();
    }

    private static class StorageImporterHandler extends DefaultHandler {
        private Element rootElement = new Element();
        private Stack<Element> mOpenTags = new Stack<Element>();

        public Element getRootElement() {
            return this.rootElement;
        }

        @Override
        public void startDocument() throws SAXException {
        }

        @Override
        public void endDocument() throws SAXException {
            /* Do nothing */
        }

        @Override
        public void startElement(String namespaceURI, String localName,
                                 String qName, Attributes attributes) throws SAXException {
            Log.i(K9.LOG_TAG, "Starting element " + localName);
            Element element = new Element();
            element.name = localName;
            mOpenTags.push(element);
            for (int i = 0; i < attributes.getLength(); i++) {
                String key = attributes.getLocalName(i);
                String value = attributes.getValue(i);
                Log.i(K9.LOG_TAG, "Got attribute " + key + " = " + value);
                element.attributes.put(key, value);
            }
        }

        @Override
        public void endElement(String namespaceURI, String localName, String qName) {
            Log.i(K9.LOG_TAG, "Ending element " + localName);
            Element element = mOpenTags.pop();
            Element superElement = mOpenTags.empty() ? null : mOpenTags.peek();
            if (superElement != null) {
                superElement.subElements.put(element.name, element);
            } else {
                rootElement = element;
            }
        }

        @Override
        public void characters(char ch[], int start, int length) {
            String value = new String(ch, start, length);
            mOpenTags.peek().data.append(value);
        }
    }
}

@@ -905,71 +905,234 @@
     
-try %7B%0A            InputStream is = new FileInputStream(
+InputStream is = null;%0A        try %7B%0A            is = new FileInputStream(fileName);%0A        %7D catch (FileNotFoundException fnfe) %7B%0A            throw new StorageImportExportException(%22Failure opening settings file %22 + 
 fileName
 );%0A 
@@ -1119,32 +1119,38 @@
 ile %22 + fileName
+, fnfe
 );%0A            r
@@ -1144,26 +1144,56 @@
         
-    return
+%7D%0A%0A        try %7B%0A            int count =
  importP
@@ -1244,43 +1244,72 @@
     
-%7D catch (FileNotFoundException fnfe
+    return count;%0A        %7D finally %7B%0A            if (is != null
 ) %7B%0A
@@ -1324,98 +1324,245 @@
     
+ 
-throw new StorageImportExportException(%22Failure reading settings file %22 + fileName, fnfe);
+   try %7B%0A                    is.close();%0A                %7D catch (Exception e) %7B%0A                    Log.i(K9.LOG_TAG, %22Unable to close InputStream for file %22 + fileName + %22: %22 + e.getLocalizedMessage());%0A                %7D%0A            %7D
 %0A   
@@ -1564,24 +1564,25 @@
 %7D%0A        %7D%0A
+%0A
     %7D%0A    pu
@@ -2255,32 +2255,8 @@
 s));
-%0A            is.close();
 %0A%0A  
@@ -2445,17 +2445,16 @@
 sion);%0A%0A
-%0A
         

Make importer usable by other things handling InputStreams, like a facility to use the Android-wide Backup Service.