Some fixes (crash when adding raw "x=" field, "dirty" handling)
authorNiki Roo <roo.niki@gmail.com>
Fri, 11 Mar 2016 08:32:10 +0000 (09:32 +0100)
committerNiki Roo <roo.niki@gmail.com>
Fri, 11 Mar 2016 08:32:10 +0000 (09:32 +0100)
src/be/nikiroo/jvcard/BaseClass.java
src/be/nikiroo/jvcard/Card.java
src/be/nikiroo/jvcard/parsers/Format.java
src/be/nikiroo/jvcard/parsers/Parser.java
src/be/nikiroo/jvcard/tui/StringUtils.java
src/be/nikiroo/jvcard/tui/panes/ContactDetailsRaw.java

index b96edea6ee60f33a2eda0acb8ac908ebd59a4d07..cb07948aac144d6aee8406e0f87882c394e11738 100644 (file)
@@ -29,18 +29,22 @@ public abstract class BaseClass<E extends BaseClass<?>> implements List<E> {
        private List<E> list;
 
        /**
-        * Create a new {@link BaseClass} with the given list as its descendants.
+        * Create a new {@link BaseClass} with the items in the given list as its
+        * descendants.
+        * 
+        * Note: the elements will be copied from the {@link List}, you cannot
+        * manage the {@link List} from outside
         * 
         * @param list
         *            the descendants of this object, or NULL if none
         */
        protected BaseClass(List<E> list) {
                this.list = new ArrayList<E>();
-               
+
                if (list != null) {
                        this.list.addAll(list);
                }
-               
+
                for (E child : this) {
                        _enter(child, true);
                }
@@ -102,6 +106,9 @@ public abstract class BaseClass<E extends BaseClass<?>> implements List<E> {
         */
        void setDirty() {
                dirty = true;
+               if (parent != null) {
+                       parent.setDirty();
+               }
        }
 
        /**
@@ -156,8 +163,10 @@ public abstract class BaseClass<E extends BaseClass<?>> implements List<E> {
         */
        private void _enter(E child, boolean initialLoad) {
                child.setParent(this);
-               if (!initialLoad)
+               if (!initialLoad) {
+                       setDirty();
                        child.setDirty();
+               }
        }
 
        @Override
index 2622266c259a43c328178ae08c0a68b97c45e27a..e714b5633a9b5047d4a08df36463bbda05bab2f4 100644 (file)
@@ -25,36 +25,73 @@ public class Card extends BaseClass<Contact> {
        private File file;
        private String name;
        private Format format;
+       private long lastModified;
+       private boolean remote;
 
        /**
         * Create a new {@link Card} from the given {@link File} and {@link Format}.
         * 
         * @param file
-        *            the file containing the {@link Card} data, must not be NULL
+        *            the input {@link File} containing the {@link Card} data or
+        *            NULL for an empty card (usually a {@link File} name or a
+        *            network path)
         * @param format
         *            the {@link Format} to use to parse it
         * 
         * @throws IOException
         *             in case of IO error
-        * @throws NullPointerException
-        *             if file is NULL
         * @throws InvalidParameterException
         *             if format is NULL
         */
        public Card(File file, Format format) throws IOException {
-               super(load(file, format));
+               this(load(file, format));
+
+               if (file != null) {
+                       if (file.exists()) {
+                               lastModified = file.lastModified();
+                       }
+               }
 
-               this.file = file;
                this.format = format;
-               this.name = file.getName();
+
+               if (file != null) {
+                       this.file = file;
+                       switch (format) {
+                       case VCard21:
+                               this.name = file.getName().replaceAll(
+                                               ".[vV][cC][fF]$", "");
+                               break;
+                       case Abook:
+                       default:
+                               this.name = file.getName();
+                               break;
+                       }
+               }
+       }
+
+       /**
+        * Create a new {@link Card} from the given {@link Contact}s.
+        * 
+        * @param contacts
+        *            the input contacts
+        * 
+        * @throws IOException
+        *             in case of IO error
+        * @throws InvalidParameterException
+        *             if format is NULL
+        */
+       public Card(List<Contact> contacts) throws IOException {
+               super(contacts);
+
+               lastModified = -1;
        }
 
        /**
         * Save the {@link Card} to the given {@link File} with the given
         * {@link Format}.
         * 
-        * @param file
-        *            the {@link File} to save to
+        * @param output
+        *            the output to save to
         * @param format
         *            the {@link Format} to use
         * 
@@ -63,15 +100,15 @@ public class Card extends BaseClass<Contact> {
         * @throws IOException
         *             in case of IO errors
         */
-       public boolean saveAs(File file, Format format) throws IOException {
-               if (file == null)
+       public boolean saveAs(File output, Format format) throws IOException {
+               if (output == null)
                        return false;
 
-               BufferedWriter writer = new BufferedWriter(new FileWriter(file));
+               BufferedWriter writer = new BufferedWriter(new FileWriter(output));
                writer.append(toString(format));
                writer.close();
 
-               if (file.equals(this.file)) {
+               if (output.getCanonicalPath().equals(this.file.getCanonicalPath())) {
                        setPristine();
                }
 
@@ -90,6 +127,23 @@ public class Card extends BaseClass<Contact> {
                return saveAs(file, format);
        }
 
+       /**
+        * Reload the data from the input.
+        * 
+        * @return TRUE if it was done
+        * 
+        * @throws IOException
+        *             in case of IO error
+        */
+       public boolean reload() throws IOException {
+               if (file == null)
+                       return false;
+
+               this.replaceListContent(load(file, format));
+               setPristine();
+               return true;
+       }
+
        /**
         * Return a {@link String} representation of this {@link Card} in the given
         * {@link Format}.
@@ -113,6 +167,53 @@ public class Card extends BaseClass<Contact> {
                return name;
        }
 
+       /**
+        * Return the original {@link Format} of the {@link Card}.
+        * 
+        * @return the {@link Format}
+        */
+       public Format getFormat() {
+               return format;
+       }
+
+       /**
+        * Return the input which was used to open this {@link Card}.
+        * 
+        * @return the input
+        */
+       public File getInput() {
+               return file;
+       }
+
+       /**
+        * Return the date of the last modification for this {@link Card} (or -1 if
+        * unknown/new).
+        * 
+        * @return the last modified date
+        */
+       public long getLastModified() {
+               return lastModified;
+       }
+
+       /**
+        * Check if this {@link Card} is remote.
+        * 
+        * @return TRUE if this {@link Card} is remote
+        */
+       public boolean isRemote() {
+               return remote;
+       }
+
+       /**
+        * Set the remote option on this {@link Card}.
+        * 
+        * @param remote
+        *            TRUE if this {@link Card} is remote
+        */
+       public void setRemote(boolean remote) {
+               this.remote = remote;
+       }
+
        @Override
        public String toString() {
                return toString(Format.VCard21);
@@ -122,24 +223,32 @@ public class Card extends BaseClass<Contact> {
         * Load the data from the given {@link File} under the given {@link Format}.
         * 
         * @param file
-        *            the {@link File} to load from
+        *            the input to load from
         * @param format
         *            the {@link Format} to load as
         * 
         * @return the list of elements
+        * 
         * @throws IOException
         *             in case of IO error
         */
-       static private List<Contact> load(File file, Format format)
+       private static List<Contact> load(File file, Format format)
                        throws IOException {
-               BufferedReader buffer = new BufferedReader(new InputStreamReader(
-                               new FileInputStream(file), "UTF-8"));
-               List<String> lines = new LinkedList<String>();
-               for (String line = buffer.readLine(); line != null; line = buffer
-                               .readLine()) {
-                       lines.add(line);
+               List<String> lines = null;
+
+               if (file != null && file.exists()) {
+                       BufferedReader buffer = new BufferedReader(new InputStreamReader(
+                                       new FileInputStream(file), "UTF-8"));
+                       lines = new LinkedList<String>();
+                       for (String line = buffer.readLine(); line != null; line = buffer
+                                       .readLine()) {
+                               lines.add(line);
+                       }
+                       buffer.close();
                }
-               buffer.close();
+
+               if (lines == null)
+                       return new LinkedList<Contact>();
 
                return Parser.parse(lines, format);
        }
index 501d2cade05950f1023a7262f1083d6eeff19b15..5f15dec0e3287bc784df0dbb48fd0d19fef64ccb 100644 (file)
@@ -14,5 +14,5 @@ public enum Format {
        /**
         * (Al)Pine Contact Book format, also called abook (usually .addressbook).
         */
-       Abook
+       Abook,
 }
index cec4c1ae9cd8ea404f931b597474fd7eea566701..5ecad4a42abcb9fe1c8c34307b53270aa70e1f0c 100644 (file)
@@ -37,7 +37,8 @@ public class Parser {
        }
 
        // -1 = no bkeys
-       public static String toString(Contact contact, Format format, int startingBKey) {
+       public static String toString(Contact contact, Format format,
+                       int startingBKey) {
                switch (format) {
                case VCard21:
                        return Vcard21Parser.toString(contact, startingBKey);
@@ -54,8 +55,8 @@ public class Parser {
        public static int getBKey(Data data) {
                if (data.isBinary() && data.getValue().startsWith("<HIDDEN_")) {
                        try {
-                               int bkey = Integer.parseInt(data.getValue().replace("<HIDDEN_",
-                                               "").replace(">", ""));
+                               int bkey = Integer.parseInt(data.getValue()
+                                               .replace("<HIDDEN_", "").replace(">", ""));
                                if (bkey < 0)
                                        throw new InvalidParameterException(
                                                        "All bkeys MUST be positive");
index 382922605260260c8f4998da741757d277ba9f4d..569b06cf3b60edf4f15587c3d9e80e2baaec81b3 100644 (file)
@@ -2,6 +2,9 @@ package be.nikiroo.jvcard.tui;
 
 import java.text.Normalizer;
 import java.text.Normalizer.Form;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.regex.Pattern;
 
 import com.googlecode.lanterna.gui2.LinearLayout.Alignment;
@@ -135,4 +138,36 @@ public class StringUtils {
 
                return input;
        }
+
+       /**
+        * Convert between time in milliseconds to {@link String} in a "static" way
+        * (to exchange data over the wire, for instance).
+        * 
+        * @param time
+        *            the time in milliseconds
+        * 
+        * @return the time as a {@link String}
+        */
+       static public String fromTime(long time) {
+               SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+               return sdf.format(new Date(time));
+       }
+
+       /**
+        * Convert between time as a {@link String} to milliseconds in a "static"
+        * way (to exchange data over the wire, for instance).
+        * 
+        * @param time
+        *            the time as a {@link String}
+        * 
+        * @return the time in milliseconds
+        */
+       static public long toTime(String display) {
+               SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+               try {
+                       return sdf.parse(display).getTime();
+               } catch (ParseException e) {
+                       return -1;
+               }
+       }
 }
index d3bd18d1d39fe9f3580b99b3fe526d1fbfeb1655..be74d1c8cbef3a31b382afa45c9446534cedcf55 100644 (file)
@@ -117,19 +117,27 @@ public class ContactDetailsRaw extends MainContentList {
                        @Override
                        public String getQuestion() {
                                // TODO i18n
-                               return "new data (xx = yy): ";
+                               return "new data (xx.group = yy): ";
                        }
 
                        @Override
                        public String callback(String answer) {
-                               if (answer.length() > 0 && answer.contains("=")) {
-                                       String[] tab = answer.split("=");
-                                       Data data = new Data(null, tab[0].trim(), tab[1].trim(),
-                                                       null);
+                               int indexEq = answer.indexOf('=');
+                               if (indexEq >= 0) {
+                                       String name = answer.substring(0, indexEq).trim();
+                                       String value = answer.substring(indexEq + 1).trim();
+                                       String group = null;
+
+                                       int indexDt = name.indexOf('.');
+                                       if (indexDt >= 0) {
+                                               group = name.substring(indexDt + 1).trim();
+                                               name = name.substring(0, indexDt).trim();
+                                       }
+
+                                       Data data = new Data(null, name, value, group);
                                        getContact().add(data);
                                        addItem("x");
                                }
-
                                return null;
                        }
                });