/* * @(#)XSpan.java 1.11 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package sun.porting.utils; /** * A class which stores an X span. * * @version 1.6, 08/19/02 */ class XSpan { int x, w; XSpan next, prev; public XSpan(int x, int w) { // // Coverage.cover(114, "XSpan(x,w)"); this.x = x; this.w = w; } public XSpan(int x, int w, XSpan prev, XSpan next) { // // Coverage.cover(115, "XSpan(x,w,prev,next)"); this.x = x; this.w = w; this.prev = prev; this.next = next; } /* static void validateList(String msg, XSpan head) { for (XSpan p = head.next; p != head.prev; p = p.next) { if ((p.x + p.w) >= p.next.x) { System.err.println(msg + ": Bands are out of order??"); System.err.println(" " +p.next+ " is after " +p); System.err.println("Whole span list: "); p = head.next; while (p != head) { System.err.println(" " +p); p = p.next; } throw new RuntimeException("invalid XSpans"); } } } */ /* * Destructively add a span between x and x+w. This * replaces (or is merged with) any spans currently in * that region. */ static void cover(XSpan head, int x, int w) { // validateList("+cover",head); XSpan x1, x2; int xEnd = x + w; // one simple, special case, because append is not uncommon int endOfBand = head.prev.x + head.prev.w; if (x >= endOfBand) { // Coverage.cover(21, "x >= endOfBand"); if (x == endOfBand) { // Coverage.cover(22, "x == endOfBand"); head.prev.w += w; } else { // Coverage.cover(23, "x != endOfBand"); head.prev = head.prev.next = new XSpan(x, w, head.prev, head); } return; } // find first overlapping span for (x1 = head.next; x1 != head; x1 = x1.next) { if ((x1.x + x1.w) >= x) { break; } } if ((x1 != head) && (x1.x < x)) { // Coverage.cover(24, "x1.x < x"); x = x1.x; } // and last overlapping span if (x1 == head) { // Coverage.cover(25, "x1 == head"); x2 = x1.prev; } else { // Coverage.cover(26, "scan for x2"); for (x2 = x1; x2.next != head; x2 = x2.next) { if ((x2.x + x2.w) >= xEnd) { break; } } } if (x2.x > xEnd) { // Coverage.cover(27, "x2.x > xEnd"); x2 = x2.prev; } if ((x2.x + x2.w) > xEnd) { // Coverage.cover(28, "(x2.x + x2.w) > xEnd"); xEnd = x2.x + x2.w; } x2.next.prev = x1.prev.next = new XSpan(x, xEnd - x, x1.prev, x2.next); // validateList("-cover", head); } /* * Utility function which destructively deletes from the * list any span that is inside x..x2 */ static void deleteInside(int x, int x2, XSpan head) { // validateList("+deleteInside", head); // // Coverage.cover(116, "deleteInside(x,x2,head)"); XSpan xs; for (xs = head.next; xs != head; xs = xs.next) { if (xs.x >= x2) { // // Coverage.cover(117, "no spans in range"); return; } else if ((xs.x + xs.w) > x) { // // Coverage.cover(118, "found span"); break; } } if (xs == head) { // // Coverage.cover(119, "ran off end"); return; } while (xs != head) { int xEnd = (xs.x + xs.w); if (xs.x < x) { // // Coverage.cover(120, "shorten band"); // shorten band xs.w = x - xs.x; if (xEnd > x2) { // split // // Coverage.cover(121, "xEnd > x2 (split)"); xs.next = xs.next.prev = new XSpan(x2, xEnd - x2, xs, xs.next); } } else if (xs.x >= x2) { // // Coverage.cover(122, "out of range (return)"); return; } else if (xEnd > x2) { // // Coverage.cover(123, "xEnd > x2 (shorten)"); // shorten band xs.x = x2; xs.w = xEnd - x2; return; } else { // // Coverage.cover(124, "delete band"); xs.prev.next = xs.next; xs.next.prev = xs.prev; } // // Coverage.cover(125, "band advanced"); xs = xs.next; } // validateList("-deleteInside", head); } /* * Utility function which destructively deletes from the * list any span that is outside x..x2 */ static void deleteOutside(int x, int x2, XSpan head) { // validateList("+deleteOutside", head); // // Coverage.cover(126, "deleteOutside(x,x2,head)"); XSpan xs = head.next; while ((xs != head) && ((xs.x + xs.w) <= x)) { // // Coverage.cover(127, "scan forwards"); xs = xs.next; } if (xs.x < x) { // // Coverage.cover(128, "shorten first band"); xs.w = xs.x + xs.w - x; xs.x = x; } xs.prev = head; head.next = xs; xs = head.prev; while ((xs != head) && (xs.x > x2)) { // // Coverage.cover(129, "scan backwards"); xs = xs.prev; } if ((xs.x + xs.w) > x2) { // // Coverage.cover(130, "shorten last band"); xs.w = x2 - xs.x; if (xs.w <= 0) { // // Coverage.cover(183,"Delete band that was shortened to 0"); xs.prev.next = xs.next; xs.next.prev = xs.prev; } } xs.next = head; head.prev = xs; // validateList("-deleteOutside", head); } /* * Copy a list of spans */ static XSpan copy(XSpan head) { // validateList("+copy", head); // // Coverage.cover(131, "copy(XSpan)"); if (head == null) { throw new RuntimeException("copy: head == null"); // // Coverage.cover(132, "head == null"); // return null; } // create dummy list head XSpan q = new XSpan(0, -1); // copy first real element XSpan p = head.next; q.next = new XSpan(p.x, p.w, q, q); q.prev = q.next; // copy remainder of list for (p = p.next; p != head; p = p.next) { // // Coverage.cover(133, "scan list"); q.prev = q.prev.next = new XSpan(p.x, p.w, q.prev, q); } // validateList("-copy", q); return q; } /* * make a copy of spans, only considering those inside x..x2 */ static XSpan copyInside(int x, int x2, XSpan head) { // validateList("+copyInside", head); // // Coverage.cover(134, "copyInside(x,x2,head)"); if (head == null) { // // Coverage.cover(135, "head == null"); return null; } XSpan xs = head.next; XSpan last = head.prev; int start = xs.x; int end = (last.x + last.w); if ((end <= x) || (start >= x2)) { // // Coverage.cover(136, "everything to one side or other"); // everything is to one side or the other return null; } else if ((start >= x) && (end <= x2)) { // // Coverage.cover(137, "all inside"); // save some overhead -- they're all inside! return XSpan.copy(head); } // find the last child that could be in the region for (; last != head; last = last.prev) { // // Coverage.cover(138, "scan list"); if (last.x < x2) { // // Coverage.cover(139, "found one"); break; } } // if the last candidate is left of x, there are none. if ((last.x + last.w) <= x) { // // Coverage.cover(140, "last candidate is left of x"); return null; } // now search forward for the first span in x..x2 for (; xs != last; xs = xs.next) { // // Coverage.cover(141, "scan list"); if ((xs.x + xs.w) > x) { // // Coverage.cover(142, "found one"); break; } } // special case code to build the list head int newX = xs.x; if (x > newX) { // // Coverage.cover(143, "increase newX"); newX = x; } int xEnd = xs.x + xs.w; if (x2 < xEnd) { // // Coverage.cover(144, "decrease xEnd"); xEnd = x2; } XSpan newHead = new XSpan(0, -1); newHead.next = new XSpan(newX, xEnd - newX, newHead, newHead); newHead.prev = newHead.next; if (xs == last) { // // Coverage.cover(145, "only one span"); // there was only one span in x..x2 return newHead; } xs = xs.next; while (xs != last) { // // Coverage.cover(146, "scan list"); newHead.prev = newHead.prev.next = new XSpan(xs.x, xs.w, newHead.prev, newHead); xs = xs.next; } if ((xs.x + xs.w) > x2) { // // Coverage.cover(147, "xs.x + xs.w > x2 (add new span)"); newHead.prev = newHead.prev.next = new XSpan(xs.x, x2 - xs.x, newHead.prev, newHead); } else { // // Coverage.cover(148, "xs.x + xs.w <= x2 (add new span)"); newHead.prev = newHead.prev.next = new XSpan(xs.x, xs.w, newHead.prev, newHead); } // validateList("-copyInside", newHead); return newHead; } /* * make a copy of spans, only considering those outside x..x2 */ static XSpan copyOutside(int x, int x2, XSpan head) { // validateList("+copyOutside", head); // // Coverage.cover(149, "copyOutside(x,x2,head)"); // don't bother if all spans are inside x..x2 if ((head == null) || ((head.next.x >= x) && ((head.prev.x + head.prev.w) <= x2))) { // // Coverage.cover(150, "all inside"); return null; } // search for the first child that is outside x..x2 // (this is in case they're all on the x2 side) XSpan xs = head.next; if (xs.x >= x) { // // Coverage.cover(151, "xs.x >= x"); while ((xs.next != head) && ((xs.x + xs.w) <= x2)) { // // Coverage.cover(152, "scan list"); xs = xs.next; } } // now set up the list XSpan newHead = new XSpan(0, -1); int xEnd = xs.x + xs.w; if (xEnd > x) { if (xs.x < x) { // // Coverage.cover(153, "xEnd > x && xs.x < x"); newHead.next = new XSpan(xs.x, x - xs.x, newHead, newHead); } else if ((xs.x < x2) && (xEnd > x2)) { // // Coverage.cover(154, "xEnd > x2 && xs.x < x2"); newHead.next = new XSpan(x2, xEnd - x2, newHead, newHead); } else { // xs.x must be >= x2 // // Coverage.cover(155, "copy span verbatim"); newHead.next = new XSpan(xs.x, xs.w, newHead, newHead); } } else { // // Coverage.cover(156, "xEnd <= x"); newHead.next = new XSpan(xs.x, xs.w, newHead, newHead); } newHead.prev = newHead.next; if ((newHead.prev.x >= x2) && (xs.next == head)) { // // Coverage.cover(157, "only one element?"); return newHead; } if ((xs.x < x) && (xEnd > x2)) { // // Coverage.cover(158, "one element splits x..x2"); // it covers all of x..x2 newHead.prev = newHead.prev.next = new XSpan(x2, xEnd - x2, newHead.prev, newHead); } if (xs.x < x) { // // Coverage.cover(159, "xs.x < x"); for (xs = xs.next; xs != head; xs = xs.next) { if (xs.x >= x) { // // Coverage.cover(160, "xs.x >= x"); break; } else if ((xs.x + xs.w) > x) { // // Coverage.cover(161, "xs.x + xs.w > x"); newHead.prev = newHead.prev.next = new XSpan(xs.x, x - xs.x, newHead.prev, newHead); break; } else { // // Coverage.cover(162, "default case"); newHead.prev = newHead.prev.next = new XSpan(xs.x, xs.w, newHead.prev, newHead); } } } else if (newHead.prev.x >= x2) { xs = xs.next; } // skip over children that are inside x..x2 for (; xs != head; xs = xs.next) { // // Coverage.cover(163, "skip "); if ((xs.x + xs.w) > x2) { // // Coverage.cover(164, "found one that crosses x2"); break; } } if ((xs != head) && (xs.x < x2)) { // // Coverage.cover(165, "insert new span"); newHead.prev = newHead.prev.next = new XSpan(x2, xs.x + xs.w - x2, newHead.prev, newHead); xs = xs.next; } for (; xs != head; xs = xs.next) { // // Coverage.cover(166, "copy end of list"); newHead.prev = newHead.prev.next = new XSpan(xs.x, xs.w, newHead.prev, newHead); } // validateList("-copyOutside("+x+", "+x2+")", newHead); return newHead; } /* * Utility function to subtract span lists. Case analysis is * identical to intersection of YX lists, but simpler as there * are no children and no need to split spans. */ static void subtract(XSpan headA, XSpan headB) { // validateList("+subtract", headA); // validateList("+subtract", headB); // // Coverage.cover(167, "subtract(XSpan, XSpan)"); XSpan a = headA.next; XSpan b = headB.next; while ((a != headA) && (b != headB)) { if ((a.x == b.x) && (a.w == b.w)) { // // Coverage.cover(168, "equal spans"); // special case this in order to speed up common case // where large number of objects are equal XSpan saveA = a.prev; do { a = a.next; b = b.next; } while ((a != headA) && (b != headB) && (a.x == b.x) && (a.w == b.w)); a.prev = saveA; saveA.next = a; if ((a == headA) || (b == headB)) { // // Coverage.cover(169, "all spans equal"); return; } } int aEnd = a.x + a.w; if (aEnd <= b.x) { // // Coverage.cover(170, "no overlap (skip a)"); // no overlap -- skip a = a.next; continue; } int bEnd = b.x + b.w; if (bEnd <= a.x) { // // Coverage.cover(171, "no overlap (skip b)"); // no overlap -- skip b = b.next; continue; } if (a.x < b.x) { // // Coverage.cover(172, "a.x < b.x (trim)"); // keep the non-overlap area preceding the overlap a.w = b.x - a.x; if (aEnd > bEnd) { // // Coverage.cover(173, "aEnd > bEnd (split band)"); // make a new band for the area following the overlap a.next = a.next.prev = new XSpan(bEnd, aEnd - bEnd, a, a.next); } a = a.next; } else if (aEnd > bEnd) { // // Coverage.cover(174, "aEnd > bEnd (trim)"); // set the band to the non-overlap area following the overlap a.w = aEnd - bEnd; a.x = bEnd; b = b.next; } else { // // Coverage.cover(175, "delete overlapping band"); // it all overlaps--delete it a.prev.next = a.next; a.next.prev = a.prev; a = a.next; } } // validateList("-subtract", headA); } /* * Utility function to intersect span lists. Case analysis is * identical to intersection of YX lists, but simpler as there are * no children to deal with. There is one case where we must split * a span, changing * * A ---------- * B ----- -------- * * to * * A --- --- * B ----- -------- */ static void intersect(XSpan headA, XSpan headB) { // validateList("+intersect", headA); // validateList("+intersect", headB); // // Coverage.cover(176, "intersect(XSpan,XSpan)"); XSpan a = headA.next; XSpan b = headB.next; while ((a != headA) && (b != headB)) { int aEnd = a.x + a.w; if (aEnd <= b.x) { // // Coverage.cover(177, "no overlap (delete a)"); // no overlap -- delete a.prev.next = a.next; a.next.prev = a.prev; a = a.next; continue; } int bEnd = b.x + b.w; if (bEnd <= a.x) { // // Coverage.cover(178, "no overlap (skip b)"); // no overlap -- skip b = b.next; continue; } if (a.x <= b.x) { // // Coverage.cover(179, "a.x <= b.x (shorten band)"); // we're not interested in the non-overlap. // begin by shortening the band. a.x = b.x; a.w = aEnd - a.x; } if (aEnd > bEnd) { // // Coverage.cover(180, "aEnd > bEnd (shorten band)"); // shorten the band a.w = bEnd - a.x; b = b.next; if ((b != headB) && (b.x < aEnd)) { // // Coverage.cover(181, "split band"); // split the band a.next = a.next.prev = new XSpan(b.x, aEnd - b.x, a, a.next); // re-examine this band--it might overlap several b's } } // // Coverage.cover(182, "advance a"); a = a.next; } // delete any remaining a elements which don't overlap. a.prev.next = headA; headA.prev = a.prev; // validateList("-intersect", headA); } static void merge(XSpan headA, XSpan headB) { // validateList("+merge", headA); // validateList("+merge", headB); XSpan a = headA.next; XSpan b = headB.next; while ((a != headA) && (b != headB)) { int aEnd = a.x + a.w; // no overlap if (aEnd < b.x) { // Coverage.cover(29, "no overlap -- advance a"); a = a.next; continue; } int bEnd = b.x + b.w; // no overlap if (bEnd < a.x) { // Coverage.cover(30, "no overlap -- insert (part of) b"); a.prev = a.prev.next = new XSpan(b.x, b.w, a.prev, a); b = b.next; continue; } if (a.x > b.x) { // Coverage.cover(31, "a.x > b.x"); a.x = b.x; a.w = aEnd - a.x; if ((a.prev != headA) && (a.prev.x + a.prev.w >= a.x)) { // Coverage.cover(32, "merge with previous span"); // merge with the previous span a.prev.w = aEnd - a.prev.x; a.next.prev = a.prev; a.prev.next = a.next; a = a.prev; } } b = b.next; // from this point on we only need bEnd if (bEnd > aEnd) { // Coverage.cover(33, "bEnd > aEnd"); a.w = bEnd - a.x; aEnd = a.x + a.w; a = a.next; if ((a != headA) && (aEnd >= a.x)) { // Coverage.cover(34, "merge with previous span"); // merge with the new previous span a.prev.w = a.x + a.w - a.prev.x; a.next.prev = a.prev; a.prev.next = a.next; a = a.prev; } } else { // Coverage.cover(35, "advance a"); a = a.next; } } if (b == headB) { // Coverage.cover(36, "no more elements in b"); return; } // handle leftovers in b. If they overlap, must extend a a = headA.prev; int aEnd = a.x + a.w; while ((aEnd >= b.x) && (b != headB)) { int bEnd = b.x + b.w; // Coverage.cover(37, "overlapping leftover"); if (bEnd > aEnd) { // Coverage.cover(38, "extend a"); a.w = bEnd - a.x; } b = b.next; } // now all that's left is things to append to the list. while (b != headB) { // Coverage.cover(39, "nonoverlapping leftover"); a.next = headA.prev = new XSpan(b.x, b.w, a, headA); a = a.next; b = b.next; } // validateList("-merge", headA); } // no test coverage public static String makeString(XSpan head) { String ret = "["; for (XSpan xs = head.next; xs != head; xs = xs.next) { if (xs != head.next) { ret += " "; } String s = "(" + xs.x + "," + xs.w + ")"; ret += s; } return ret + "]"; } // no test coverage public String toString() { return "(x = " + x + ", w = " + w + ")"; } }