Fix PREF handling (was not correct relative to the RFC!)
[jvcard.git] / src / be / nikiroo / jvcard / Data.java
index 377c41e29edbbbc8d97d73d2d27c79390752944f..4d3da7e4c7d37f0aa3bd1754065cfbcbdc0e4872 100644 (file)
@@ -11,7 +11,7 @@ import java.util.List;
  * @author niki
  *
  */
-public class Data {
+public class Data extends BaseClass<TypeInfo> {
        public enum DataPart {
                FN_FAMILY, FN_GIVEN, FN_ADDITIONAL, // Name
                FN_PRE, FN_POST, // Pre/Post
@@ -24,9 +24,6 @@ public class Data {
        private String value;
        private String group;
        private int b64; // -1 = no, 0 = still not ordered, the rest is order
-       private List<TypeInfo> types;
-       private boolean dirty;
-       private Contact parent;
 
        /**
         * Create a new {@link Data} with the given values.
@@ -34,25 +31,21 @@ public class Data {
         * @param types
         *            the types of this {@link Data}
         * @param name
-        *            its name
+        *            its name (<b>MUST NOT</b> be NULL)
         * @param value
-        *            its value
+        *            its value (<b>MUST NOT</b> be NULL)
         * @param group
-        *            its group if any
+        *            its group if any (or NULL if none)
         */
        public Data(List<TypeInfo> types, String name, String value, String group) {
-               if (types == null) {
-                       types = new LinkedList<TypeInfo>();
-               }
+               super(types);
 
-               this.types = types;
-               this.name = name;
-               this.value = value;
+               this.name = name.toUpperCase();
+               this.value = value.toString(); // crash NOW if null
                this.group = group;
 
                b64 = -1;
-               for (TypeInfo type : types) {
-                       type.setParent(this);
+               for (TypeInfo type : this) {
                        if (type.getName().equals("ENCODING")
                                        && type.getValue().equals("b")) {
                                b64 = 0;
@@ -62,102 +55,93 @@ public class Data {
        }
 
        /**
-        * Return the number of {@link TypeInfo} present in this {@link Data}.
+        * Return the name of this {@link Data}
         * 
-        * @return the number of {@link TypeInfo}s
+        * @return the name
         */
-       public int size() {
-               return types.size();
+       public String getName() {
+               return name;
        }
 
        /**
-        * Return the {@link TypeInfo} at index <i>index</i>.
-        * 
-        * @param index
-        *            the index of the {@link TypeInfo} to find
+        * Return the value of this {@link Data}
         * 
-        * @return the {@link TypeInfo}
+        * @return the value
+        */
+       public String getValue() {
+               return unescape(value);
+       }
+
+       /**
+        * Change the value of this {@link Data}
         * 
-        * @throws IndexOutOfBoundsException
-        *             if the index is < 0 or >= {@link Data#size()}
+        * @param value
+        *            the new value
         */
-       public TypeInfo get(int index) {
-               return types.get(index);
+       public void setValue(String value) {
+               setRawValue(escape(value));
        }
 
        /**
-        * Add a new {@link TypeInfo} in this {@link Data}.
+        * Return the raw value of this {@link Data}
         * 
-        * @param type
-        *            the new type
+        * @return the raw value
         */
-       public void add(TypeInfo type) {
-               type.setParent(this);
-               type.setDirty();
-               types.add(type);
+       public String getRawValue() {
+               return value;
        }
 
        /**
-        * Remove the given {@link TypeInfo} from its this {@link Data} if it is in.
+        * Change the raw value of this {@link Data}
         * 
-        * @return TRUE in case of success
+        * @param value
+        *            the new raw value
         */
-       public boolean remove(TypeInfo type) {
-               if (types.remove(type)) {
+       public void setRawValue(String value) {
+               if ((value == null && this.value != null)
+                               || (value != null && !value.equals(this.value))) {
+                       this.value = value;
                        setDirty();
                }
-
-               return false;
        }
 
        /**
-        * Change the {@link TypeInfo}s of this {@link Data}.
+        * Return the {@link List} of comma-listed values from this {@link Data}.
         * 
-        * @param types
-        *            the new types
+        * @return the {@link List} of values
         */
-       @Deprecated
-       public void setTypes(List<TypeInfo> types) {
-               // TODO: check if this method is required
-               this.types.clear();
-               for (TypeInfo type : types) {
-                       this.types.add(type);
-                       type.setParent(this);
-               }
-
-               setDirty();
+       public List<String> getValues() {
+               return getList(',');
        }
 
        /**
-        * Return the name of this {@link Data}
+        * Set the {@link List} of comma-listed values from this {@link Data}.
         * 
-        * @return the name
+        * @param values
+        *            the {@link List} of values
         */
-       public String getName() {
-               return name;
+       public void setValues(List<String> values) {
+               setList(values, ',');
        }
 
        /**
-        * Return the value of this {@link Data}
+        * Return the {@link List} of semi-column-listed fields from this
+        * {@link Data}.
         * 
-        * @return the value
+        * @return the {@link List} of values
         */
-       public String getValue() {
-               return value;
+       public List<String> getFields() {
+               return getList(';');
        }
 
        /**
-        * Change the value of this {@link Data}
+        * Set the {@link List} of comma-listed values from this {@link Data}.
         * 
-        * @param value
-        *            the new value
+        * @param values
+        *            the {@link List} of values
         */
-       public void setValue(String value) {
-               if ((value == null && this.value != null)
-                               || (value != null && !value.equals(this.value))) {
-                       this.value = value;
-                       setDirty();
-               }
+       public void setFields(List<String> values) {
+               setList(values, ';');
        }
 
        /**
@@ -192,6 +176,35 @@ public class Data {
                return b64;
        }
 
+       /**
+        * Check if this {@link Data} is binary
+        * 
+        * @return TRUE if it is
+        */
+       public boolean isBinary() {
+               return b64 >= 0;
+       }
+
+       /**
+        * Return the preferred value of this {@link Data}, or
+        * {@link Integer#MAX_VALUE} if none.
+        * 
+        * @return the preferred value
+        */
+       public int getPreferred() {
+               for (TypeInfo type : this) {
+                       if (type.getName().equals("PRE")) {
+                               try {
+                                       return Integer.parseInt(type.getValue());
+                               } catch (NumberFormatException e) {
+                                       e.printStackTrace();
+                               }
+                       }
+               }
+
+               return Integer.MAX_VALUE;
+       }
+
        /**
         * Change the bkey of this {@link Data}
         * 
@@ -213,64 +226,71 @@ public class Data {
        }
 
        /**
-        * Check if this {@link Data} is binary
+        * Return the {@link List} of sep-listed values from this {@link String}
+        * data.
         * 
-        * @return TRUE if it is
-        */
-       public boolean isBinary() {
-               return b64 >= 0;
-       }
-
-       /**
-        * Delete this {@link Contact} from its parent {@link Card} if any.
+        * @param value
+        *            the data
         * 
-        * @return TRUE in case of success
+        * @param the
+        *            separator
+        * 
+        * @return the {@link List} of values
         */
-       public boolean delete() {
-               if (parent != null) {
-                       return parent.remove(this);
+       private List<String> getList(char sep) {
+               List<String> rep = new LinkedList<String>();
+
+               if (value != null && value.length() > 0) {
+                       int last = 0;
+                       for (int i = 0; i < value.length(); i++) {
+                               if (value.charAt(i) == sep
+                                               && (i == 0 || value.charAt(i - 1) != '\\')) {
+                                       rep.add(value.substring(last, i));
+                                       last = i + 1;
+                               }
+                       }
+
+                       if (last < value.length())
+                               rep.add(value.substring(last));
                }
 
-               return false;
+               return rep;
        }
 
        /**
-        * Check if this {@link Data} has unsaved changes.
+        * Create the {@link String}-encoded {@link List} of sep-listed values from
+        * the given values.
+        * 
+        * @param values
+        *            the {@link List} of values
+        * 
+        * @param sep
+        *            the separator
         * 
-        * @return TRUE if it has
+        * @return the {@link String}
         */
-       public boolean isDirty() {
-               return dirty;
-       }
+       private void setList(List<String> values, char sep) {
+               StringBuilder builder = new StringBuilder();
+               boolean first = true;
+               for (String value : values) {
+                       if (!first)
+                               builder.append(sep);
 
-       /**
-        * Notify that this element has unsaved changes, and notify its parent of
-        * the same if any.
-        */
-       protected void setDirty() {
-               this.dirty = true;
-               if (this.parent != null)
-                       this.parent.setDirty();
-       }
+                       builder.append(escape(value));
 
-       /**
-        * Notify this element <i>and all its descendants</i> that it is in pristine
-        * state (as opposed to dirty).
-        */
-       void setPristine() {
-               dirty = false;
-               for (TypeInfo type : types) {
-                       type.setPristine();
+                       first = false;
                }
+
+               value = builder.toString();
        }
 
-       /**
-        * Set the parent of this {@link Data}.
-        * 
-        * @param parent
-        *            the new parent
-        */
-       void setParent(Contact parent) {
-               this.parent = parent;
+       @Override
+       public String getId() {
+               return "" + name;
+       }
+
+       @Override
+       public String getState() {
+               return ("" + name + value + group).replace(' ', '_');
        }
 }