001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.visualweb.css2;
042:
043: import java.util.Arrays;
044: import java.util.Comparator;
045: import java.util.List;
046:
047: import org.openide.ErrorManager;
048: import org.w3c.dom.Element;
049:
050: /**
051: * Maintains a list of boxes, which can be sorted in z-order.
052: * @author Tor Norbye
053: * @todo Why is not standard <code>List</code> impl used.
054: */
055: public class BoxList {
056: private CssBox[] boxes;
057: private int size = 0;
058:
059: /** Should the list be kept sorted? */
060: private boolean keepSorted;
061:
062: /** Is the list already sorted? */
063: private boolean isSorted;
064:
065: /** Field which indicates if we need to sort. This will
066: * be set if the box list contains a box with a z index other
067: * than "auto".
068: */
069: private boolean mustSort;
070:
071: /** Should we keep the parent indices on the box in sync with the
072: * box list position? */
073: private boolean syncParentIndices = true;
074:
075: /** Create a list of boxes. The initialSize parameter is a hint
076: * as to how large to make the list, but more boxes than that
077: * can be added to the lsit.
078: */
079: public BoxList(int initialSize) {
080: if (initialSize < 1) {
081: initialSize = 2;
082: }
083:
084: boxes = new CssBox[initialSize];
085: }
086:
087: /** Return the number of boxes in the list */
088: public int size() {
089: return size;
090: }
091:
092: /** Set whether or not the box list should be sorted
093: * according to the z-order attribute on boxes.
094: * Default is false.
095: */
096: public void setKeepSorted(boolean keepSorted) {
097: this .keepSorted = keepSorted;
098: }
099:
100: /** Set whether the box list should keep the parent indices
101: * for the boxes synced as the box list is manipulated.
102: */
103: public void setSyncParentIndices(boolean sync) {
104: this .syncParentIndices = sync;
105: }
106:
107: /** Return the box at the given position in the list.
108: * When the list is sorted, the higher the index, the
109: * further up in the stack / the closer the box will
110: * appear (e.g. higher indices occlude lower indices when
111: * their coordinates overlap.)
112: */
113: public CssBox get(int index) {
114: if (keepSorted && !isSorted) {
115: zsort();
116: }
117:
118: // if (!(index >= 0 && index < size)) {
119: // // Assertion check -- remove later
120: // System.out.println("Box List violation: index=" + index + " in box list of size " + size);
121: // System.out.println("Box list is: " + toString());
122: // }
123: // assert (index >= 0) && (index < size) : "Invalid box index: " + index + ", size is " +
124: // size + "; this list is " + toString(); // NOI18N
125: // XXX #94677 Supress asserts, no real fix for flow layout issues.
126: if (index < 0 || index >= size) {
127: ErrorManager.getDefault().notify(
128: ErrorManager.INFORMATIONAL,
129: new IndexOutOfBoundsException("Index=" + index
130: + ", expected to be in [0," + (size - 1)
131: + "].")); // NOI18N
132: return null;
133: }
134:
135: return boxes[index];
136: }
137:
138: public int indexOf(CssBox cssBox) {
139: if (keepSorted && !isSorted) {
140: zsort();
141: }
142:
143: List<CssBox> cssBoxes = Arrays.asList(boxes);
144: return cssBoxes.indexOf(cssBox);
145: }
146:
147: /** Remove the given box from the list and update parent indices.
148: * Returns true if the box was deleted, false if it was not found.
149: */
150: public boolean remove(CssBox box) {
151: isSorted = false;
152:
153: // If you remove the last box that has a z index it's
154: // possible that we could clear the mustSort flag. However,
155: // checking for that will cost, and is probably not worth the
156: // occasional savings when this scenario occurs.
157: int pos = 0;
158:
159: for (; pos < size; pos++) {
160: if (boxes[pos] == box) {
161: break;
162: }
163: }
164:
165: if (pos < size) {
166: int numMoved = size - pos - 1;
167:
168: if (numMoved > 0) {
169: System.arraycopy(boxes, pos + 1, boxes, pos, numMoved);
170: }
171:
172: boxes[--size] = null; // Let gc do its work
173:
174: if (syncParentIndices) {
175: for (int i = pos; i < size; i++) {
176: // Adjust parent indices
177: boxes[i].setParentIndex(i);
178: }
179: }
180: } else {
181: ErrorManager.getDefault().log(
182: "Didn't find box " + box
183: + " in the box list - illegal remove call");
184:
185: return false;
186: }
187:
188: if (syncParentIndices) {
189: box.setParentIndex(-1);
190: }
191:
192: return true;
193: }
194:
195: /** Add a box to the list. This method will NOT preserve
196: * the sortedness of the list, so if a sorted list is necessary,
197: * the client must call sort again. If both after and before are null,
198: * the box will be appended.
199: * @param after Add the box after the given box, if not null
200: * @param before Add the box right before the given box, if not null
201: * @todo Is this method unused?
202: */
203: public void add(CssBox box, CssBox after, CssBox before) {
204: isSorted = false;
205:
206: if (keepSorted) {
207: if (box.getElement() != null) {
208: box.initializeZOrder();
209:
210: if (box.z != CssBox.AUTO) {
211: mustSort = true;
212: }
213: }
214: }
215:
216: if ((after == null) && (before == null)) {
217: // Simple case: just append
218: ensureCapacity(size + 1);
219: boxes[size++] = box;
220:
221: if (syncParentIndices) {
222: box.setParentIndex(size - 1);
223: }
224:
225: return;
226: }
227:
228: int pos = 0;
229:
230: if (after != null) {
231: for (; pos < size; pos++) {
232: if (boxes[pos] == after) {
233: pos++;
234:
235: break;
236: }
237: }
238: }
239:
240: if (before != null) {
241: for (; pos < size; pos++) {
242: if (boxes[pos] == before) {
243: break;
244: }
245: }
246: }
247:
248: if (pos < size) {
249: // Insert at the given location
250: ensureCapacity(size + 1);
251: System.arraycopy(boxes, pos, boxes, pos + 1, size - pos);
252: boxes[pos] = box;
253:
254: if (syncParentIndices) {
255: box.setParentIndex(pos);
256: }
257:
258: size++;
259:
260: if (syncParentIndices) {
261: for (int i = pos + 1; i < size; i++) {
262: // Adjust parent indices
263: boxes[i].setParentIndex(i);
264: }
265: }
266: } else {
267: // XXX #111179 This assertion seems to be incorrect here.
268: // Revise, does it mean there is some issue with the computation,
269: // or the parameters are invalid? If former, fix the computation,
270: // if latter, validate the parameters at the beginning.
271: // // Add to end of the list
272: // assert before == null;
273:
274: // Simple case: just append
275: ensureCapacity(size + 1);
276: boxes[size++] = box;
277:
278: if (syncParentIndices) {
279: box.setParentIndex(size - 1);
280: }
281:
282: return;
283: }
284: }
285:
286: // From java.util.ArrayList
287: private void ensureCapacity(int minCapacity) {
288: int oldCapacity = boxes.length;
289:
290: if (minCapacity > oldCapacity) {
291: Object[] oldData = boxes;
292: int newCapacity = ((oldCapacity * 3) / 2) + 1;
293:
294: if (newCapacity < minCapacity) {
295: newCapacity = minCapacity;
296: }
297:
298: boxes = new CssBox[newCapacity];
299: System.arraycopy(oldData, 0, boxes, 0, size);
300: }
301: }
302:
303: /** Sort the list in z order. */
304: public void zsort() {
305: if (!keepSorted || !mustSort || (boxes == null) || (size < 2)) {
306: isSorted = true;
307:
308: return;
309: }
310:
311: Arrays.sort(boxes, 0, size, new CssBoxComparator());
312:
313: if (syncParentIndices) {
314: // Update indices
315: for (int i = 0; i < size; i++) {
316: boxes[i].setParentIndex(i);
317: }
318: }
319:
320: isSorted = true;
321: }
322:
323: /** Truncate the boxlist to contain only the boxes up to and including
324: * the given position.
325: * @param pos The position of the last box to be left in the list. Must
326: * be less than or equal to the size of the list.
327: */
328: public void truncate(int pos) {
329: assert pos <= size;
330:
331: for (int i = pos + 1; i < size; i++) {
332: boxes[i] = null; // let gc do its work
333: }
334:
335: size = pos;
336: }
337:
338: public String toString() {
339: StringBuffer sb = new StringBuffer();
340: sb.append("{");
341:
342: for (int i = 0; i < size; i++) {
343: if (i != 0) {
344: sb.append(", ");
345: }
346:
347: CssBox box = get(i);
348: String cls = box.getClass().getName();
349: String boxName = cls.substring(cls.lastIndexOf('.') + 1);
350: sb.append(boxName);
351: sb.append(':');
352:
353: Element element = box.getElement();
354: if (element != null) {
355: sb.append(element.toString());
356: } else if (box instanceof TextBox) {
357: sb.append(((TextBox) box).getText());
358: } else if (box instanceof SpaceBox) {
359: sb.append(' ');
360: } else {
361: sb.append(box.toString());
362: }
363: }
364:
365: sb.append("}");
366:
367: return sb.toString();
368: }
369:
370: private static class CssBoxComparator implements Comparator<CssBox> {
371: public int compare(CssBox b1, CssBox b2) {
372: if (b1.z == b2.z) {
373: return 0;
374: }
375:
376: if (b1.z == CssBox.AUTO) {
377: return -1;
378: } else if (b2.z == CssBox.AUTO) {
379: return 1;
380: } else {
381: return b1.z - b2.z;
382: }
383: }
384: } // End of CssBoxComparator.
385:
386: }
|