Permalink
Browse files

Fixes #65 (again) - Add sorting of Attributes too.

  • Loading branch information...
1 parent b2f0bfb commit dc0bfd93ee203d9056ed19bf65c65ea4d6238e5e @rolfl rolfl committed Mar 8, 2012
@@ -528,6 +528,77 @@ public String toString() {
return super.toString();
}
+ /**
+ * Unlike the Arrays.binarySearch, this method never expects an
+ * "already exists" condition, we only ever add, thus there will never
+ * be a negative insertion-point.
+ * @param indexes The pointers to search within
+ * @param len The number of pointers to search within
+ * @param val The pointer we are checking for.
+ * @param comp The Comparator to compare with
+ * @return the insertion point.
+ */
+ private final int binarySearch(final int[] indexes, final int len,
+ final int val, final Comparator<? super Attribute> comp) {
+ int left = 0, mid = 0, right = len - 1, cmp = 0;
+ final Attribute base = attributeData[val];
+ while (left <= right) {
+ mid = (left + right) >>> 1;
+ cmp = comp.compare(base, attributeData[indexes[mid]]);
+ if (cmp == 0) {
+ while (cmp == 0 && mid < right && comp.compare(
+ base, attributeData[indexes[mid + 1]]) == 0) {
+ mid++;
+ }
+ return mid + 1;
+ } else if (cmp < 0) {
+ right = mid - 1;
+ } else {
+ left = mid + 1;
+ }
+ }
+ return left;
+ }
+
+ private void sortInPlace(final int[] indexes) {
+ // the indexes are a discrete set of values that have no duplicates,
+ // and describe the relative order of each of them.
+ // as a result, we can do some tricks....
+ final int[] unsorted = ArrayCopy.copyOf(indexes, indexes.length);
+ Arrays.sort(unsorted);
+ final Attribute[] usc = new Attribute[unsorted.length];
+ for (int i = 0; i < usc.length; i++) {
+ usc[i] = attributeData[indexes[i]];
+ }
+ // usc contains the content in their pre-sorted order....
+ for (int i = 0; i < indexes.length; i ++) {
+ attributeData[unsorted[i]] = usc[i];
+ }
+ }
+
+ /**
+ * Sort the attributes using the supplied comparator. The attributes are
+ * never added using regular mechanisms, so there are never problems with
+ * detached or already-attached Attributes. The sort happens 'in place'.
+ * <p>
+ * If the comparator identifies two (or more) Attributes to be equal, then
+ * the relative order of those attributes will not be changed.
+ *
+ * @param comp The Comparator to use for sorting.
+ */
+ void sort(Comparator<? super Attribute> comp) {
+ final int sz = size;
+ int[] indexes = new int[sz];
+ for (int i = 0 ; i < sz; i++) {
+ final int ip = binarySearch(indexes, i, i, comp);
+ if (ip < i) {
+ System.arraycopy(indexes, ip, indexes, ip+1, i - ip);
+ }
+ indexes[ip] = i;
+ }
+ sortInPlace(indexes);
+ }
+
/* * * * * * * * * * * * * ContentListIterator * * * * * * * * * * * * * */
/* * * * * * * * * * * * * ContentListIterator * * * * * * * * * * * * * */
/**
@@ -562,7 +562,7 @@ public String toString() {
return super.toString();
}
- void sortInPlace(final int[] indexes) {
+ private void sortInPlace(final int[] indexes) {
// the indexes are a discrete set of values that have no duplicates,
// and describe the relative order of each of them.
// as a result, we can do some tricks....
@@ -1827,6 +1827,28 @@ public void sortChildren(Comparator <? super Element> comparator) {
((FilterList<Element>)getChildren()).sort(comparator);
}
+ /**
+ * Sort the Attributes of this Element using a mechanism that is safe
+ * for JDOM. Other child content will be unaffected. See the notes
+ * on {@link #sortContent(Filter, Comparator)} for how the algorithm works.
+ * <p>
+ * {@link Collections#sort(List, Comparator)} is not appropriate for sorting
+ * the Lists returned from {@link Element#getContent()} because those are
+ * 'live' lists, and the Collections.sort() method uses an algorithm that
+ * adds the content in the new location before removing it from the old.
+ * This creates validation issues with content attempting to attach to a
+ * parent before detaching first.
+ * <p>
+ * This method provides a safe means to conveniently sort the content.
+ *
+ * @param comparator The Comparator to use for the sorting.
+ */
+ public void sortAttributes(Comparator <? super Attribute> comparator) {
+ if (attributes != null) {
+ attributes.sort(comparator);
+ }
+ }
+
/**
* Sort the child content of this Element that matches the Filter, using a
* mechanism that is safe for JDOM content. Other child content (that does
@@ -2804,4 +2804,86 @@ public void testSortInterleavedEqualContent() {
assertTrue(emt.getContent(2) == cd3);
}
+ @Test
+ public void testSortAttributes() {
+ final Element emt = new Element("root");
+ final Attribute att1 = new Attribute("one", "001", Namespace.getNamespace("z", "uri1"));
+ final Attribute att2 = new Attribute("two", "002", Namespace.getNamespace("y", "uri1"));
+ final Attribute att3 = new Attribute("three", "003", Namespace.getNamespace("x", "uri1"));
+ final Attribute att4 = new Attribute("four", "004", Namespace.getNamespace("w", "uri2"));
+ final Attribute att5 = new Attribute("five", "005", Namespace.getNamespace("v", "uri2"));
+ emt.setAttribute(att5);
+ emt.setAttribute(att4);
+ emt.setAttribute(att3);
+ emt.setAttribute(att2);
+ emt.setAttribute(att1);
+
+ checkAttOrder(emt.getAttributes(), att5, att4, att3, att2, att1);
+
+ emt.sortAttributes(new Comparator<Attribute>() {
+ @Override
+ public int compare(Attribute o1, Attribute o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+
+ // alphabetic by string name.
+ checkAttOrder(emt.getAttributes(), att5, att4, att1, att3, att2);
+
+ emt.sortAttributes(new Comparator<Attribute>() {
+ @Override
+ public int compare(Attribute o1, Attribute o2) {
+ return o1.getNamespacePrefix().compareTo(o2.getNamespacePrefix());
+ }
+ });
+
+ // Namespace Prefixes's are reverse order
+ checkAttOrder(emt.getAttributes(), att5, att4, att3, att2, att1);
+
+ emt.sortAttributes(new Comparator<Attribute>() {
+ @Override
+ public int compare(Attribute o1, Attribute o2) {
+ return o1.getValue().compareTo(o2.getValue());
+ }
+ });
+
+ // Values are in order
+ checkAttOrder(emt.getAttributes(), att1, att2, att3, att4, att5);
+
+ // Namespace URI's have some common items.... and are in same order
+ // as a result, we should have no change at all.
+ emt.sortAttributes(new Comparator<Attribute>() {
+ @Override
+ public int compare(Attribute o1, Attribute o2) {
+ return o1.getNamespaceURI().compareTo(o2.getNamespaceURI());
+ }
+ });
+
+ // Values are in order
+ checkAttOrder(emt.getAttributes(), att1, att2, att3, att4, att5);
+
+ // Namespace URI's have some common items.... and are in same order
+ // as a result, we should have no change at all.... except, this time
+ // we do the inverse of the result... so, this moves 4&5 to the front
+ // but relative order is maintained for equal values....
+ emt.sortAttributes(new Comparator<Attribute>() {
+ @Override
+ public int compare(Attribute o1, Attribute o2) {
+ return - o1.getNamespaceURI().compareTo(o2.getNamespaceURI());
+ }
+ });
+
+ // Values are in order
+ checkAttOrder(emt.getAttributes(), att4, att5, att1, att2, att3);
+
+ }
+
+ private void checkAttOrder(List<Attribute> attributes, Attribute...atts) {
+ assertTrue(atts.length == attributes.size());
+ for (int i = atts.length - 1; i >= 0; i--) {
+ assertTrue(atts[i] == attributes.get(i));
+ }
+
+ }
+
}

0 comments on commit dc0bfd9

Please sign in to comment.