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-2006 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:
042: package org.netbeans.swing.tabcontrol.event;
043:
044: import org.netbeans.swing.tabcontrol.TabData;
045:
046: import java.util.*;
047:
048: /*
049: * ArrayDiff.java
050: *
051: * Created on November 5, 2003, 12:44 PM
052: */
053:
054: /**
055: * Class representing a diff of two arrays. Note that it is
056: * <strong>not</strong> designed to work with arrays which contain the same
057: * element more than one time - in that case, the results are undefined.
058: * <p/>
059: * <p>Note the current implementation is unoptimized and fairly brute force.
060: *
061: * @author Tim Boudreau
062: */
063: public final class ArrayDiff {
064: /**
065: * The old array
066: */
067: private TabData[] old;
068: /**
069: * The new array
070: */
071: private TabData[] nue;
072:
073: //XXX all of this could be implemented more efficiently with a single
074: //loop to calculate all statistics and so forth. The approach is algorithmically
075: //inelegant and brute force. To do that would significantly
076: //increase the complexity of the code, but it could be done later as an
077: //optimization
078:
079: /**
080: * Creates a new instance of ArrayDiff
081: */
082: private ArrayDiff(TabData[] old, TabData[] nue) {
083: this .old = old;
084: this .nue = nue;
085: if (nue == null || old == null) {
086: throw new NullPointerException(
087: old == null && nue == null ? "Both arrays are null"
088: : old == null ? "Old array is null"
089: : "New array is null");
090: }
091: }
092:
093: /**
094: * Get the array representing the old state
095: */
096: public TabData[] getOldData() {
097: return old;
098: }
099:
100: /**
101: * Get the array representing the new state
102: */
103: public TabData[] getNewData() {
104: return nue;
105: }
106:
107: /**
108: * Returns an ArrayDiff object if the two arrays are not the same, or null
109: * if they are
110: */
111: public static ArrayDiff createDiff(TabData[] old, TabData[] nue) {
112: if (!Arrays.equals(old, nue)) {
113: return new ArrayDiff(old, nue);
114: } else {
115: return null;
116: }
117: }
118:
119: private Set<Integer> deleted = null;
120:
121: /**
122: * Returns the indices of objects in the old array which are not present in
123: * the new array. The resulting array's size will be that of the old array
124: */
125: public Set<Integer> getDeletedIndices() {
126: if (deleted == null) {
127: HashSet<TabData> set = new HashSet<TabData>(Arrays
128: .asList(nue));
129: HashSet<Integer> results = new HashSet<Integer>(old.length);
130: for (int i = 0; i < old.length; i++) {
131: if (!set.contains(old[i])) {
132: results.add(new Integer(i));
133: }
134: }
135: deleted = results;
136: }
137: return deleted;
138: }
139:
140: private Set<Integer> added = null;
141:
142: /**
143: * Returns the indices of objects in the new array which are not present in
144: * the old array
145: */
146: public Set<Integer> getAddedIndices() {
147: if (added == null) {
148: HashSet<TabData> set = new HashSet<TabData>(Arrays
149: .asList(old));
150: Set<Integer> results = new HashSet<Integer>(nue.length);
151: for (int i = 0; i < nue.length; i++) {
152: if (!set.contains(nue[i])) {
153: results.add(new Integer(i));
154: }
155: }
156: added = results;
157: }
158: return added;
159: }
160:
161: /**
162: * Returns the indices of objects which differ in any way between the new
163: * and old array. The size of the result is Math.max(old.length,
164: * nue.length).
165: */
166: public Set<Integer> getChangedIndices() {
167: //XXX can add similar caching as with deleted/added fields if it looks
168: //to prove useful. getDeletedIndices() and getAddedIndices() are called
169: //more than once, and the computation can be expensive.
170: int max = Math.max(nue.length, old.length);
171: HashSet<Integer> results = new HashSet<Integer>(max);
172:
173: for (int i = 0; i < max; i++) {
174: if (i < old.length && i < nue.length) {
175: if (!old[i].equals(nue[i])) {
176: results.add(new Integer(i));
177: }
178: } else {
179: results.add(new Integer(i));
180: }
181: }
182: return results;
183: }
184:
185: /**
186: * Returns the indices of objects which were in the old array and are also
187: * in the new array, but at a different index. The indices returned are
188: * indices into the old array.
189: */
190: public Set<Integer> getMovedIndices() {
191: HashSet<TabData> set = new HashSet<TabData>(Arrays.asList(nue));
192: HashSet<Integer> results = new HashSet<Integer>(old.length);
193:
194: for (int i = 0; i < old.length; i++) {
195: boolean isPresent = set.contains(old[i]);
196: if (isPresent) {
197: boolean isMoved = (i < nue.length && !nue[i]
198: .equals(old[i]))
199: || i >= nue.length;
200: if (isMoved) {
201: results.add(new Integer(i));
202: }
203: }
204: }
205: return results;
206: }
207:
208: public String toString() {
209: StringBuffer sb = new StringBuffer();
210: sb.append("<ArrayDiff: deleted indices: [");
211: sb.append(outCol(getDeletedIndices()));
212: sb.append("] added indices: [");
213: sb.append(outCol(getAddedIndices()));
214: sb.append("] changed indices: [");
215: sb.append(outCol(getChangedIndices()));
216: sb.append("] moved indices: [");
217: sb.append(outCol(getChangedIndices()));
218: sb.append("]>");
219: return sb.toString();
220: }
221:
222: private static String outCol(Collection c) {
223: Iterator i = c.iterator();
224: StringBuffer result = new StringBuffer();
225: while (i.hasNext()) {
226: Object o = i.next();
227: result.append(o.toString());
228: if (i.hasNext()) {
229: result.append(",");
230: }
231: }
232: return result.toString();
233: }
234:
235: public boolean equals(Object o) {
236: if (o instanceof ArrayDiff) {
237: if (o == this ) {
238: return true;
239: }
240: TabData[] otherOld = ((ArrayDiff) o).getOldData();
241: TabData[] otherNue = ((ArrayDiff) o).getNewData();
242: return Arrays.equals(old, otherOld)
243: && Arrays.equals(nue, otherNue);
244: }
245: return false;
246: }
247:
248: public int hashCode() {
249: return arrayHashCode(old) ^ arrayHashCode(nue);
250: }
251:
252: private static int arrayHashCode(Object[] o) {
253: int result = 0;
254: for (int i = 0; i < o.length; i++) {
255: result += o[i].hashCode() ^ i;
256: }
257: return result;
258: }
259: }
|