X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2FBaseClass.java;h=42d4b0dd7f57090a056870f13280f573e6d2146c;hb=f04a32e97c847d7e2551037a4d5f6a070879215c;hp=b6be10e47795fd31d1169c07068eb4ca3963c6b2;hpb=e253bd50bb05519f4a16fed4fb95d5b3340128ea;p=jvcard.git diff --git a/src/be/nikiroo/jvcard/BaseClass.java b/src/be/nikiroo/jvcard/BaseClass.java index b6be10e..42d4b0d 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,20 +10,27 @@ import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +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+ * 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 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(boolean self) { + StringBuilder builder = new StringBuilder(); + buildContentStateRaw(builder, self); + return StringUtils.getMd5Hash(builder.toString()); + } + + /** + * Return the (first) child element with the given ID or NULL if not found. + * + * @param id + * the id to look for + * + * @return the child element or NULL + */ + public E getById(String id) { + for (E child : this) { + if (id == null) { + if (child.getId() == null) + return child; + } else { + if (id.equals(child.getId())) + return child; + } + } + + 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, negative value for infinite depth) + * + * @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). @@ -242,10 +327,15 @@ public abstract class BaseClass+ * 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 the data are + * identical. + *
* * @return a {@link String} representing the current state of this object, * children not included @@ -253,7 +343,65 @@ public abstract class BaseClass
+ * 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, boolean self) {
+ Collections.sort(this.list, comparator);
+ if (self)
+ builder.append(getState());
+ for (E child : this) {
+ 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, negative value for infinite depth)
+ *
+ * @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("]");
+ }
+ }
+
+ /**
+ * Notify that this element and all its parent elements has unsaved
+ * changes.
*/
void setDirty() {
dirty = true;
@@ -274,7 +422,10 @@ public abstract class BaseClass
+ * Will also check and fix if needed the parent (this) of all its
+ * descendants (recursively).
*
* @param parent
* the new parent
@@ -286,6 +437,38 @@ public abstract class BaseClass