KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > bridge > IndexedPropertyBase


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java.bridge;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.util.*;
24
25 import org.openide.src.MultiPropertyChangeEvent;
26
27 /**
28  * This class serves as a base for indexed properties in the model. The class performs
29  * basic services as creating diff required to construct a specific PropertyChangeEvent,
30  * or to actually handle the property change.
31  * The class does not define how a property's value is stored. Its methods are protected;
32  * subclasses will bind the algorithms to the actual storage. This base class can be used
33  * to implement flyweight logic for "simple" properties too.
34  *
35  * @author sdedic
36  * @version
37  */

38 public abstract class IndexedPropertyBase extends Object JavaDoc {
39     public static final int SMALL_THRESHOLD = 500;
40
41     private String JavaDoc propertyName;
42     /**
43     private MatchStrategy defaultStrategy;
44     
45      * Strategy that searches for matches between two attribute sets.
46     public interface MatchStrategy {
47         /** Detects matching items and returns a mapping from new items to the old
48          * ones. If a new item does not have a match among the old items, the function
49          * report -1 at its position in the resulting array. If there are no differencies
50          * between the two item sets, the function may return null.
51          * @param it1 old (current) item set
52          * @param it2 new (updated) item set
53          * @return array indexed by item positions in the new set, containing indexes
54          * into the old set.
55         public int[] matchItems(Object[] it1, Object[] it2);
56     }
57     */

58
59     /** Helper information object that carries informations about changes made to the
60      * indexed property. This object is much like a diff and reports exact change types
61      * made to the property.
62      */

63     public static class Change {
64         // collection of removed items
65
public Collection removed;
66         // indexes of the items being removed
67
public int[] removeIndices;
68         // permutation of items reordered (if items are removed, the permutation is
69
// computed with respect to the new positions)
70
public int[] reordered;
71         // collection of new items
72
public Collection inserted;
73         // indexes where the new items were inserted
74
public int[] insertIndices;
75         // items that replaced others
76
public Collection replacements;
77         // indices for replacements
78
public int[] replaceIndices;
79         // offsets to add to old positions to get new ones. No value is defined for
80
// positions that go away during the change (i.e. matching something from removeIndices)
81
// and there's no place to record new ones (those from insertIndices)
82
public int[] offsets;
83         // determines how much phases that particular change contains.
84
public int phaseCount;
85         
86         public int[] mapping;
87         
88         public int sizeDiff;
89         
90         public String JavaDoc toString() {
91             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
92             if (removed != null) {
93                 sb.append("removed(" + removed.size() + ")"); // NOI18N
94
}
95             if (reordered != null) {
96                 sb.append(" reordered "); // NOI18N
97
}
98             if (inserted != null) {
99                 sb.append("inserted(" + inserted.size() + ")"); // NOI18N
100
}
101             if (replacements != null) {
102                 sb.append("replaced(" + replacements.size() + ")"); // NOI18N
103
}
104             sb.append("sizedif=" + sizeDiff); // NOI18N
105
return sb.toString();
106         }
107         
108         public Object JavaDoc clone() {
109             try {
110                 return super.clone();
111             } catch (Exception JavaDoc ex) {
112             }
113             return null;
114         }
115     }
116
117     /** Constructs the object. The only parameter is the property name that is used
118      * in PropertyChangeEvent construction.
119      */

120     public IndexedPropertyBase(String JavaDoc propName) {
121         this.propertyName = propName;
122     }
123     
124     /** Detects changes in the multivalued property and creates an information
125      * object that holds the changes.
126      */

127     public Change computeChanges(Object JavaDoc[] olds, Object JavaDoc[] newValue) {
128         return computeChanges2(olds, newValue, pairItems(olds, newValue));
129     }
130
131     /** Detects changes in the multivalued property and creates an information
132      * object that holds the changes.
133      */

134     public static Change computeChanges2(Object JavaDoc[] olds, Object JavaDoc[] newValue, int[] mapping) {
135         int[] perm = new int[olds.length];
136         int matched = 0;
137         int inserted = 0;
138         boolean removed = false;
139         int reordered = 0;
140         int[] insertIndices = null;
141         int removeCount;
142         int[] offsets = null;
143         //int[] reverseOffsets = null;
144

145         //Util.log("-change computation starts"); // NOI18N
146
// clean the permutation:
147
int oldIndex = 0;
148         
149         for (int i = 0; i < mapping.length; i++) {
150             if (offsets != null && i - inserted < olds.length) {
151                 offsets[i - inserted] = inserted;
152             }
153             //Util.log("mapping[" + i + "] = " + mapping[i]); // NOI18N
154
if (mapping[i] == -1) {
155                 // Ith element was newly added.
156
if (insertIndices == null) {
157                     offsets = new int[olds.length];
158                     insertIndices = new int[mapping.length];
159                     //reverseOffsets = new int[mapping.length];
160
// insertIndices == null implies that offsets == null
161
// note - item on this - old - position is offsetted too. The
162
}
163                 //Util.log("insertion at " + i); // NOI18N
164
insertIndices[inserted] = i;
165                 inserted++;
166             } else {
167                 perm[mapping[i]] = i + 1;
168                 /*
169                 if (inserted > 0)
170                     reverseOffsets[i] = -bias;
171                  */

172                 matched++;
173             }
174         }
175         if (inserted > 0 && mapping.length < olds.length) {
176             Arrays.fill(offsets, mapping.length, olds.length - 1, inserted);
177         }
178
179         removeCount = perm.length - matched;
180         removed = removeCount > 0;
181         
182         Change ch = new Change();
183         int eventCount = 0;
184         PropertyChangeEvent JavaDoc removeEvent = null, addEvent = null, reorderEvent = null;
185         
186         if (removed) {
187             Collection rem = new ArrayList(perm.length - matched);
188             int bias = 0;
189             int reorderIndex = 0;
190             int removedSoFar = 0;
191             int[] indices = new int[removeCount];
192             int indicePos = 0;
193             int diff = 0;
194
195             // offsets WILL be needed -- if there were no INSERTs, positions were
196
// changed if there are some removes.
197
if (offsets == null) {
198                 offsets = new int[olds.length];
199                 //reverseOffsets = new int[mapping.length];
200
}
201             // must fire removed items first.
202
for (int i = 0; i < perm.length; i++) {
203                 if (perm[i] == 0) {
204                     //Util.log("removing at index " + i); // NOI18N
205
bias--;
206                     indices[indicePos++] = i;
207                     rem.add(olds[i]);
208                     removedSoFar++;
209                     // the client gets position of -1 in the new state ==> item does not exist.
210
offsets[i] = -i - 1;
211                 } else {
212                     offsets[i] += bias;
213                     //reverseOffsets[i + bias] -= bias;
214
}
215             }
216             ch.removed = rem;
217             ch.removeIndices = indices;
218             eventCount++;
219         }
220         // reorder detection :-(
221
if (true) {
222             int nextInsert;
223             int insertPos = 1;
224             
225             if (insertIndices == null)
226                 nextInsert = -1;
227             else
228                 nextInsert = insertIndices[0];
229             int pos = 0;
230             
231             reordered = 0;
232             int j = 0;
233
234             //Util.log("reorder detection: "); // NOI18N
235
for (int i = 0; i < perm.length; i++) {
236                 if (perm[i] == 0) {
237                     perm[i] = -1;
238                     //Util.log("element " + i + " removed, newPos = " + j); // NOI18N
239
continue;
240                 }
241                 int newPlace = perm[i] - 1;
242                 int expectedPlace = i;
243                 
244                 if (offsets != null)
245                     expectedPlace += offsets[i];
246                 /*
247                 int newPlace = perm[i] - 1;
248                  */

249                 //Util.log("perm[" + i + "] = " + perm[i] + ", offsets[] = " + (offsets == null ? -1 : offsets[i])); // NOI18N
250
if (newPlace != expectedPlace) {
251                     reordered++;
252                     //Util.log("element " + i + " moved to " + reverseOffsets[perm[i] - 1]); // NOI18N
253
perm[i] = newPlace;
254                 } else {
255                     //Util.log("no change for " + i); // NOI18N
256
perm[i] = -1;
257                 }
258                 /*
259                 if (j == nextInsert) {
260                     if (insertPos < inserted)
261                         nextInsert = insertIndices[insertPos++];
262                     else
263                         nextInsert = -1;
264                     Util.log("element " + i + " offseted, newPos = " + j); // NOI18N
265                     Util.log("next insert is at " + nextInsert); // NOI18N
266                 }
267                 if (perm[i] - 1 == j) {
268                     Util.log("no change at pos " + i); // NOI18N
269                     perm[i] = -1;
270                 } else {
271                     Util.log("element " + i + " moved to " + (perm[i] - 1)); // NOI18N
272                     reordered++;
273                     perm[i]--;
274                 }
275                 j++;
276                  */

277             }
278             if (reordered > 0) {
279                 ch.reordered = perm;
280                 eventCount++;
281             }
282         }
283         if (inserted > 0) {
284             //Util.log("inserted: " + inserted); // NOI18N
285
Collection col = new ArrayList(inserted);
286             int[] indices = new int[inserted];
287             System.arraycopy(insertIndices, 0, indices, 0, inserted);
288             for (int i = 0; i < indices.length; i++) {
289                 col.add(newValue[insertIndices[i]]);
290             }
291             ch.inserted = col;
292             ch.insertIndices = indices;
293             eventCount++;
294         }
295         ch.offsets = offsets;
296         ch.phaseCount = eventCount;
297         ch.sizeDiff = inserted - removeCount;
298         return ch;
299     }
300
301     /** Creates a PropertyChangeEvent based on the Change object.
302      */

303     public MultiPropertyChangeEvent createPropertyEvent(Change ch,
304         Object JavaDoc source, Object JavaDoc oldV, Object JavaDoc newV) {
305         if (ch.phaseCount == 0)
306             return null;
307         
308         MultiPropertyChangeEvent evt = null;
309         boolean compound = ch.phaseCount > 1;
310         Collection sub = null;
311
312         newV = ((Object JavaDoc[])newV).clone();
313         if (compound) {
314             sub = new ArrayList(ch.phaseCount);
315         }
316         if (ch.inserted != null) {
317             evt = createInsertionEvent(source, oldV, newV, ch.inserted,
318                 ch.insertIndices);
319             if (compound)
320                 sub.add(evt);
321         }
322         if (ch.removed != null) {
323             evt = createRemovalEvent(source, oldV, newV, ch.removed,
324                 ch.removeIndices);
325             if (compound)
326                 sub.add(evt);
327         }
328         if (ch.reordered != null) {
329             evt = createReorderEvent(source, oldV, newV, ch.reordered);
330             if (compound)
331                 sub.add(evt);
332         }
333         if (compound) {
334             return createCompoundEvent(source, sub, ch.offsets, oldV, newV);
335         } else {
336             return evt;
337         }
338     }
339     
340     public MultiPropertyChangeEvent createChangeEvent(Object JavaDoc source,
341         Object JavaDoc[] olds, Object JavaDoc[] newValue) {
342         Change ch = computeChanges(olds, newValue);
343         return createPropertyEvent(ch, source, olds, newValue);
344     }
345     
346     protected MultiPropertyChangeEvent createInsertionEvent(Object JavaDoc source,
347         Object JavaDoc previous, Object JavaDoc now, Collection inserted,
348         int[] indices) {
349         MultiPropertyChangeEvent insertEvent = new MultiPropertyChangeEvent(
350             source,
351             getPropertyName(), previous, now);
352         insertEvent.makeInsertion(inserted, indices);
353         return insertEvent;
354     }
355     
356     protected MultiPropertyChangeEvent createRemovalEvent(Object JavaDoc source,
357         Object JavaDoc prev, Object JavaDoc now, Collection removed, int[] indices) {
358         MultiPropertyChangeEvent evt = new MultiPropertyChangeEvent(
359             source,
360             getPropertyName(), prev, now);
361         evt.makeRemoval(removed, indices);
362         return evt;
363     }
364     
365     protected MultiPropertyChangeEvent createModifyEvent(Object JavaDoc source,
366     Object JavaDoc prev, Object JavaDoc now, Collection origs, Collection replacements, int[] indices) {
367         MultiPropertyChangeEvent evt = new MultiPropertyChangeEvent(
368             source,
369             getPropertyName(), prev, now);
370         evt.makeReplacement(origs, replacements, indices);
371         return evt;
372     }
373     
374     /**
375      * Called to create a compound event.
376      */

377     protected MultiPropertyChangeEvent createCompoundEvent(Object JavaDoc source,
378         Collection subEvents, int[] offsets, Object JavaDoc old, Object JavaDoc newValue) {
379         MultiPropertyChangeEvent evt = new MultiPropertyChangeEvent(source,
380             getPropertyName(), old, newValue);
381         evt.makeCompound(subEvents, offsets);
382         return evt;
383     }
384     
385     /**
386      * Helper function that creates reorder event from the given bean implementation and
387      * a permutation of current elements
388      * @param beanImpl bean implementation.
389      * @param old old value of the whole multivalued property
390      * @param now new value of the multivalued property
391      * @param perm permutation that describes the change.
392      */

393     protected MultiPropertyChangeEvent createReorderEvent(Object JavaDoc source,
394         Object JavaDoc old, Object JavaDoc now, int[] perm) {
395         MultiPropertyChangeEvent evt = new MultiPropertyChangeEvent(source,
396             getPropertyName(), old, now);
397         evt.makeReorder(perm);
398         return evt;
399     }
400
401     /** Default, stupid implementation of item matcher. The matcher uses algorithm
402      * with complexity of O(n^2) comparing object referencies.
403      * @param o first (old) array of objects.
404      * @param n second (new) array of objects.
405      */

406     public int[] pairItems(Object JavaDoc[] o, Object JavaDoc[] n) {
407         int[] map = new int[n.length];
408         boolean[] used = new boolean[o.length];
409         
410         for (int i = 0; i < n.length; i++) {
411             map[i] = -1;
412             for (int j = 0; j < o.length; j++) {
413                 if (used[j])
414                     continue;
415                 if (o[j] == n[i] || compareValues(o[j], n[i])) {
416                     used[j] = true;
417                     map[i] = j;
418                 }
419             }
420         }
421         return map;
422     }
423
424
425     protected int[] createIdentityMap(Object JavaDoc[] els, Object JavaDoc[] olds) {
426         int avgOps = els.length * olds.length;
427         
428         if (avgOps < SMALL_THRESHOLD) {
429             
430             // fallback to "stupid" method that uses brute force (and is n^2)
431
return pairItems(olds, els);
432         }
433         
434         Map indexM = new HashMap((int)(olds.length * 1.3));
435         int[] mapping = new int[els.length];
436         
437         for (int idx = 0; idx < olds.length; idx++) {
438             indexM.put(olds[idx], new Integer JavaDoc(idx));
439         }
440         
441         for (int i = 0; i < els.length; i++) {
442             Integer JavaDoc pos = (Integer JavaDoc)indexM.get(els[i]);
443             mapping[i] = pos == null ? -1 : pos.intValue();
444         }
445         return mapping;
446     }
447
448     /** Returns false, causing the pairItems method to use only object identity for
449      * the comparison.
450      * @param o1 one object
451      * @param o2 another object
452      */

453     protected boolean compareValues(Object JavaDoc o1, Object JavaDoc o2) {
454         return false;
455     }
456     
457     /** Retrieves the source reference from the given bean.
458      */

459     protected Object JavaDoc getSource(ElementImpl key) {
460         return key.getElement();
461     }
462     
463     //
464
///////////////////////////////////////////////////////////////////////////
465
protected String JavaDoc getPropertyName() {
466         return propertyName;
467     }
468 }
469
Popular Tags