X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2FBaseClass.java;h=9533321b2f0b0673a5a00d236c3c8cff008a2540;hb=f06c81000632cfb5f525ca458f719338f55f9f66;hp=df0fcf00de78f998ad87d03842dc4947f86a966a;hpb=e4444b0bc462544629d9e7e7ab62b96a4d9cab10;p=jvcard.git diff --git a/src/be/nikiroo/jvcard/BaseClass.java b/src/be/nikiroo/jvcard/BaseClass.java index df0fcf0..9533321 100644 --- a/src/be/nikiroo/jvcard/BaseClass.java +++ b/src/be/nikiroo/jvcard/BaseClass.java @@ -1,5 +1,6 @@ package be.nikiroo.jvcard; +import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -9,22 +10,27 @@ import java.util.LinkedList; import java.util.List; import java.util.ListIterator; -import be.nikiroo.jvcard.resources.StringUtils; +import be.nikiroo.utils.StringUtils; /** * This class is basically a List with a parent and a "dirty" state check. It * sends all commands down to the initial list, but will mark itself and its * children as dirty or not when needed. * - * All child elements can identify their parent. + *

+ * All child elements can identify their parent, and must not be added to 2 + * different objects without without first being removed from the previous one. + *

* + *

* The dirty state is bubbling up (when dirty = true) or down (when dirty = * false) -- so, making changes to a child element will also mark its parent as * "dirty", and marking an element as pristine will also affect all its child * elements. + *

* * @author niki - * + * * @param * the type of the child elements */ @@ -119,8 +125,8 @@ public abstract class BaseClass> implements List { * If not equals, the differences will be represented by the given * {@link List}s if they are not NULL. *
    - *
  • addedwill represent the elements in list but not in - * this
  • + *
  • added will represent the elements in list but not + * in this
  • *
  • removed will represent the elements in this but not * in list
  • *
  • from will represent the elements in list that are @@ -250,23 +256,26 @@ public abstract class BaseClass> implements List { } /** - * Get the recursive state of the current object, i.e., its children. It - * represents the full state information about this object's children. It - * may not contains spaces nor new lines. + * Get the recursive state of the current object, i.e., its children + * included. It represents the full state information about this object's + * children. It may not contains spaces nor new lines. * *

    * Not that this state is lossy. You cannot retrieve the data from - * the state, it can only be used as an ID to check if thw data are - * identical. + * the state, it can only be used as an ID to check if data are identical. *

    * + * @param self + * also include state information about the current object itself + * (as opposed to its children) + * * @return a {@link String} representing the current content state of this * object, i.e., its children included */ - public String getContentState() { + public String getContentState(boolean self) { StringBuilder builder = new StringBuilder(); - buildContentStateRaw(builder); - return StringUtils.getHash(builder.toString()); + buildContentStateRaw(builder, self); + return StringUtils.getMd5Hash(builder.toString()); } /** @@ -291,6 +300,23 @@ public abstract class BaseClass> implements List { return null; } + /** + * Return a {@link String} that can be used to identify this object in DEBUG + * mode, i.e., a "toString" method that can identify the object's content + * but still be readable in a log. + * + * @param depth + * the depth into which to descend (0 = only this object, not its + * children) + * + * @return the debug {@link String} + */ + public String getDebugInfo(int depth) { + StringBuilder builder = new StringBuilder(); + getDebugInfo(builder, depth, 0); + return builder.toString(); + } + /** * Return the current ID of this object -- it is allowed to change over time * (so, do not cache it). @@ -317,19 +343,59 @@ public abstract class BaseClass> implements List { abstract public String getState(); /** - * Get the recursive state of the current object, i.e., its children. It - * represents the full state information about this object's children. + * Get the recursive state of the current object, i.e., its children + * included. It represents the full state information about this object's + * children. * * It is not hashed. * * @param builder * the {@link StringBuilder} that will represent the current * content state of this object, i.e., its children included + * @param self + * also include state information about the current object itself + * (as opposed to its children) */ - void buildContentStateRaw(StringBuilder builder) { - builder.append(getState()); + void buildContentStateRaw(StringBuilder builder, boolean self) { + Collections.sort(this.list, comparator); + if (self) + builder.append(getState()); for (E child : this) { - child.buildContentStateRaw(builder); + child.buildContentStateRaw(builder, true); + } + } + + /** + * Populate a {@link StringBuilder} that can be used to identify this object + * in DEBUG mode, i.e., a "toString" method that can identify the object's + * content but still be readable in a log. + * + * @param depth + * the depth into which to descend (0 = only this object, not its + * children) + * + * @param tab + * the current tabulation increment + */ + void getDebugInfo(StringBuilder builder, int depth, int tab) { + for (int i = 0; i < tab; i++) + builder.append(" "); + builder.append(getContentState(false) + " " + getId()); + + if (depth > 0) + builder.append(": ["); + + if (depth > 0) { + for (E child : this) { + builder.append("\n"); + child.getDebugInfo(builder, depth - 1, tab + 1); + } + } + if (depth > 0) { + builder.append("\n"); + for (int i = 0; i < tab; i++) + builder.append(" "); + builder.append("]"); } } @@ -367,6 +433,38 @@ public abstract class BaseClass> implements List { } } + /** + * Escape the given value to VCF standard. + * + * @param value + * the value to escape + * + * @return the escaped value + */ + protected String escape(String value) { + if (value == null) + return null; + + return value.replaceAll(",", "\\\\,").replaceAll(";", "\\\\;") + .replaceAll("\n", "\\\\n"); + } + + /** + * Unescape the given value from the VCF standard. + * + * @param value + * the value to unescape + * + * @return the unescaped value + */ + protected String unescape(String value) { + if (value == null) + return null; + + return value.replaceAll("\\\\,", ",").replaceAll("\\\\;", ";") + .replaceAll("\\\\n", "\n"); + } + /** * Each element that leaves the parent will pass trough here. * @@ -374,6 +472,12 @@ public abstract class BaseClass> implements List { * the element to remove from this */ private void _leave(E child) { + if (child.parent != null && child.parent != this) { + throw new InvalidParameterException( + "You are removing this child from its rightful parent, it must be yours to do so"); + } + + child.parent = null; setDirty(); } @@ -394,6 +498,11 @@ public abstract class BaseClass> implements List { * the element to add to this */ private void _enter(E child, boolean initialLoad) { + if (child.parent != null && child.parent != this) { + throw new InvalidParameterException( + "You are stealing this child from its rightful parent, you must remove it first"); + } + child.setParent(this); if (!initialLoad) { setDirty();