Some fixes (crash when adding raw "x=" field, "dirty" handling)
[jvcard.git] / src / be / nikiroo / jvcard / Card.java
index 4c4e5d174e419bb47c9037e2f4477a08438a3b73..e714b5633a9b5047d4a08df36463bbda05bab2f4 100644 (file)
@@ -8,7 +8,6 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.security.InvalidParameterException;
-import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -22,77 +21,77 @@ import be.nikiroo.jvcard.parsers.Parser;
  * @author niki
  * 
  */
-public class Card {
-       private List<Contact> contacts;
+public class Card extends BaseClass<Contact> {
        private File file;
-       private boolean dirty;
        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 {
-               this.file = file;
-               this.format = format;
-               this.name = file.getName();
-
-               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);
+               this(load(file, format));
+
+               if (file != null) {
+                       if (file.exists()) {
+                               lastModified = file.lastModified();
+                       }
                }
-               buffer.close();
 
-               load(lines, format);
-               dirty = false; // initial load, so no change yet, so no need to call
-                                               // setPristine()
-       }
+               this.format = format;
 
-       /**
-        * Return the number of {@link Contact} present in this {@link Card}.
-        * 
-        * @return the number of {@link Contact}s
-        */
-       public int size() {
-               return contacts.size();
+               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;
+                       }
+               }
        }
 
        /**
-        * Return the {@link Contact} at index <i>index</i>.
+        * Create a new {@link Card} from the given {@link Contact}s.
         * 
-        * @param index
-        *            the index of the {@link Contact} to find
+        * @param contacts
+        *            the input contacts
         * 
-        * @return the {@link Contact}
-        * 
-        * @throws IndexOutOfBoundsException
-        *             if the index is < 0 or >= {@link Card#size()}
+        * @throws IOException
+        *             in case of IO error
+        * @throws InvalidParameterException
+        *             if format is NULL
         */
-       public Contact get(int index) {
-               return contacts.get(index);
+       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
         * 
@@ -101,15 +100,15 @@ public class Card {
         * @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();
                }
 
@@ -128,6 +127,23 @@ public class Card {
                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}.
@@ -141,15 +157,6 @@ public class Card {
                return Parser.toString(this, format);
        }
 
-       /**
-        * Check if this {@link Card} has unsaved changes.
-        * 
-        * @return TRUE if it has
-        */
-       public boolean isDirty() {
-               return dirty;
-       }
-
        /**
         * Return the name of this card (the name of the {@link File} which it was
         * opened from).
@@ -160,67 +167,89 @@ public class Card {
                return name;
        }
 
-       @Override
-       public String toString() {
-               return toString(Format.VCard21);
+       /**
+        * Return the original {@link Format} of the {@link Card}.
+        * 
+        * @return the {@link Format}
+        */
+       public Format getFormat() {
+               return format;
        }
 
        /**
-        * Load the given data from the given {@link Format} in this {@link Card}.
+        * Return the input which was used to open this {@link Card}.
         * 
-        * @param serializedContent
-        *            the data
-        * @param format
-        *            the {@link Format}
+        * @return the input
         */
-       protected void load(String serializedContent, Format format) {
-               // note: fixed size array
-               List<String> lines = Arrays.asList(serializedContent.split("\n"));
-               load(lines, format);
+       public File getInput() {
+               return file;
        }
 
        /**
-        * Load the given data from the given {@link Format} in this {@link Card}.
+        * Return the date of the last modification for this {@link Card} (or -1 if
+        * unknown/new).
         * 
-        * @param lines
-        *            the data
-        * @param format
-        *            the {@link Format}
+        * @return the last modified date
         */
-       protected void load(List<String> lines, Format format) {
-               this.contacts = Parser.parse(lines, format);
-               setDirty();
-
-               for (Contact contact : contacts) {
-                       contact.setParent(this);
-               }
+       public long getLastModified() {
+               return lastModified;
        }
 
        /**
-        * Return the full list of {@link Contact}s. Please use responsibly (this is
-        * the original list).
+        * Check if this {@link Card} is remote.
         * 
-        * @return the list of {@link Contact}s
+        * @return TRUE if this {@link Card} is remote
         */
-       List<Contact> getContactsList() {
-               return contacts;
+       public boolean isRemote() {
+               return remote;
        }
 
        /**
-        * Notify that this element has unsaved changes.
+        * Set the remote option on this {@link Card}.
+        * 
+        * @param remote
+        *            TRUE if this {@link Card} is remote
         */
-       void setDirty() {
-               dirty = true;
+       public void setRemote(boolean remote) {
+               this.remote = remote;
+       }
+
+       @Override
+       public String toString() {
+               return toString(Format.VCard21);
        }
 
        /**
-        * Notify this element <i>and all its descendants</i> that it is in pristine
-        * state (as opposed to dirty).
+        * Load the data from the given {@link File} under the given {@link Format}.
+        * 
+        * @param file
+        *            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
         */
-       void setPristine() {
-               dirty = false;
-               for (Contact contact : contacts) {
-                       contact.setPristine();
+       private static List<Contact> load(File file, Format format)
+                       throws IOException {
+               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();
                }
+
+               if (lines == null)
+                       return new LinkedList<Contact>();
+
+               return Parser.parse(lines, format);
        }
 }