openide.util/src/org/openide/util/lookup/ArrayStorage.java
author Jesse Glick <jglick@netbeans.org>
Fri, 09 Oct 2009 15:11:13 -0400
changeset 833 0e00857c5827
parent 829 3b2ed3e1f01b
permissions -rw-r--r--
Warnings.
jtulach@7
     1
/*
jtulach@302
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jtulach@7
     3
 *
mzlamal@829
     4
 * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
jtulach@7
     5
 *
jtulach@302
     6
 * The contents of this file are subject to the terms of either the GNU
jtulach@302
     7
 * General Public License Version 2 only ("GPL") or the Common
jtulach@302
     8
 * Development and Distribution License("CDDL") (collectively, the
jtulach@302
     9
 * "License"). You may not use this file except in compliance with the
jtulach@302
    10
 * License. You can obtain a copy of the License at
jtulach@302
    11
 * http://www.netbeans.org/cddl-gplv2.html
jtulach@302
    12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
jtulach@302
    13
 * specific language governing permissions and limitations under the
jtulach@302
    14
 * License.  When distributing the software, include this License Header
jtulach@302
    15
 * Notice in each file and include the License file at
jtulach@302
    16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
jtulach@302
    17
 * particular file as subject to the "Classpath" exception as provided
jtulach@302
    18
 * by Sun in the GPL Version 2 section of the License file that
jtulach@302
    19
 * accompanied this code. If applicable, add the following below the
jtulach@302
    20
 * License Header, with the fields enclosed by brackets [] replaced by
jtulach@302
    21
 * your own identifying information:
jtulach@189
    22
 * "Portions Copyrighted [year] [name of copyright owner]"
jtulach@189
    23
 *
jtulach@302
    24
 * Contributor(s):
jtulach@302
    25
 *
jtulach@189
    26
 * The Original Software is NetBeans. The Initial Developer of the Original
jtulach@189
    27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
jtulach@7
    28
 * Microsystems, Inc. All Rights Reserved.
jtulach@302
    29
 *
jtulach@302
    30
 * If you wish your version of this file to be governed by only the CDDL
jtulach@302
    31
 * or only the GPL Version 2, indicate your decision by adding
jtulach@302
    32
 * "[Contributor] elects to include this software in this distribution
jtulach@302
    33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
jtulach@302
    34
 * single choice of license, a recipient has the option to distribute
jtulach@302
    35
 * your version of this file under either the CDDL, the GPL Version 2 or
jtulach@302
    36
 * to extend the choice of license to its licensees as provided above.
jtulach@302
    37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
jtulach@302
    38
 * Version 2 license, then the option applies only if the new code is
jtulach@302
    39
 * made subject to such option by the copyright holder.
jtulach@7
    40
 */
jtulach@7
    41
package org.openide.util.lookup;
jtulach@7
    42
jtulach@7
    43
import org.openide.util.Lookup;
jtulach@7
    44
jtulach@7
    45
jtulach@7
    46
jtulach@7
    47
import java.util.*;
jtulach@132
    48
import org.openide.util.lookup.AbstractLookup.Pair;
jtulach@7
    49
jtulach@7
    50
jtulach@7
    51
/** ArrayStorage of Pairs from AbstractLookup.
jtulach@7
    52
 * @author  Jaroslav Tulach
jtulach@7
    53
 */
jtulach@132
    54
final class ArrayStorage extends Object
jtulach@132
    55
implements AbstractLookup.Storage<ArrayStorage.Transaction> {
jtulach@7
    56
    /** default trashold */
jtulach@7
    57
    static final Integer DEFAULT_TRASH = new Integer(11);
jtulach@7
    58
jtulach@7
    59
    /** list of items */
jtulach@7
    60
    private Object content;
jtulach@7
    61
jtulach@7
    62
    /** linked list of refernces to results */
jtulach@132
    63
    private transient AbstractLookup.ReferenceToResult<?> results;
jtulach@7
    64
jtulach@7
    65
    /** Constructor
jtulach@7
    66
     */
jtulach@7
    67
    public ArrayStorage() {
jtulach@7
    68
        this(DEFAULT_TRASH);
jtulach@7
    69
    }
jtulach@7
    70
jtulach@7
    71
    /** Constructs new ArrayStorage */
jtulach@7
    72
    public ArrayStorage(Integer treshhold) {
jtulach@7
    73
        this.content = treshhold;
jtulach@7
    74
    }
jtulach@7
    75
jtulach@7
    76
    /** Adds an item into the tree.
jtulach@7
    77
    * @param item to add
jtulach@7
    78
    * @return true if the Item has been added for the first time or false if some other
jtulach@7
    79
    *    item equal to this one already existed in the lookup
jtulach@7
    80
    */
jtulach@132
    81
    public boolean add(AbstractLookup.Pair<?> item, Transaction changed) {
jtulach@7
    82
        Object[] arr = changed.current;
jtulach@7
    83
jtulach@7
    84
        if (changed.arr == null) {
jtulach@7
    85
            // just simple add of one item
jtulach@7
    86
            for (int i = 0; i < arr.length; i++) {
jtulach@7
    87
                if (arr[i] == null) {
jtulach@7
    88
                    arr[i] = item;
jtulach@7
    89
                    changed.add(item);
jtulach@7
    90
jtulach@7
    91
                    return true;
jtulach@7
    92
                }
jtulach@7
    93
jtulach@7
    94
                if (arr[i].equals(item)) {
jtulach@7
    95
                    // reassign the item number
jtulach@7
    96
                    item.setIndex(null, ((AbstractLookup.Pair) arr[i]).getIndex());
jtulach@7
    97
jtulach@7
    98
                    // already there, but update it
jtulach@7
    99
                    arr[i] = item;
jtulach@7
   100
jtulach@7
   101
                    return false;
jtulach@7
   102
                }
jtulach@7
   103
            }
jtulach@7
   104
jtulach@7
   105
            // cannot happen as the beginTransaction ensured we can finish 
jtulach@7
   106
            // correctly
jtulach@7
   107
            throw new IllegalStateException();
jtulach@7
   108
        } else {
jtulach@7
   109
            // doing remainAll after that, let Transaction hold the new array
jtulach@7
   110
            int newIndex = changed.addPair(item);
jtulach@7
   111
jtulach@7
   112
            for (int i = 0; i < arr.length; i++) {
jtulach@7
   113
                if (arr[i] == null) {
jtulach@7
   114
                    changed.add(item);
jtulach@7
   115
jtulach@7
   116
                    return true;
jtulach@7
   117
                }
jtulach@7
   118
jtulach@7
   119
                if (arr[i].equals(item)) {
jtulach@7
   120
                    // already there
jtulach@7
   121
                    if (i != newIndex) {
jtulach@7
   122
                        // change in index
jtulach@7
   123
                        changed.add(item);
jtulach@7
   124
jtulach@7
   125
                        return false;
jtulach@7
   126
                    } else {
jtulach@7
   127
                        // no change
jtulach@7
   128
                        return false;
jtulach@7
   129
                    }
jtulach@7
   130
                }
jtulach@7
   131
            }
jtulach@7
   132
jtulach@7
   133
            // if not found in the original array
jtulach@7
   134
            changed.add(item);
jtulach@7
   135
jtulach@7
   136
            return true;
jtulach@7
   137
        }
jtulach@7
   138
    }
jtulach@7
   139
jtulach@7
   140
    /** Removes an item.
jtulach@7
   141
    */
jtulach@132
   142
    public void remove(AbstractLookup.Pair item, Transaction changed) {
jtulach@7
   143
        Object[] arr = changed.current;
jtulach@271
   144
        if (arr == null) {
jtulach@271
   145
            return;
jtulach@271
   146
        }
jtulach@7
   147
jtulach@7
   148
        int found = -1;
jtulach@7
   149
jtulach@7
   150
        for (int i = 0; i < arr.length;) {
jtulach@7
   151
            if (arr[i] == null) {
jtulach@7
   152
                // end of task
jtulach@7
   153
                return;
jtulach@7
   154
            }
jtulach@7
   155
jtulach@7
   156
            if ((found == -1) && arr[i].equals(item)) {
jtulach@7
   157
                // already there
jtulach@132
   158
                Pair<?> p = (Pair<?>)arr[i];
jtulach@132
   159
                p.setIndex(null, -1);
jtulach@132
   160
                changed.add(p);
jtulach@7
   161
                found = i;
jtulach@7
   162
            }
jtulach@7
   163
jtulach@7
   164
            i++;
jtulach@7
   165
jtulach@7
   166
            if (found != -1) {
jtulach@434
   167
                if (i < arr.length && !(arr[i] instanceof Integer)) {
jtulach@7
   168
                    // moving the array
jtulach@7
   169
                    arr[i - 1] = arr[i];
jtulach@7
   170
                } else {
jtulach@7
   171
                    arr[i - 1] = null;
jtulach@7
   172
                }
jtulach@7
   173
            }
jtulach@7
   174
        }
jtulach@7
   175
    }
jtulach@7
   176
jtulach@7
   177
    /** Removes all items that are not present in the provided collection.
jtulach@7
   178
    * @param retain Pair -> AbstractLookup.Info map
jtulach@7
   179
    * @param notify set of Classes that has possibly changed
jtulach@7
   180
    */
jtulach@132
   181
    public void retainAll(Map retain, Transaction changed) {
jtulach@7
   182
        Object[] arr = changed.current;
jtulach@7
   183
jtulach@7
   184
        for (int from = 0; from < arr.length; from++) {
jtulach@7
   185
            if (!(arr[from] instanceof AbstractLookup.Pair)) {
jtulach@7
   186
                // end of content
jtulach@7
   187
                break;
jtulach@7
   188
            }
jtulach@7
   189
jtulach@7
   190
            AbstractLookup.Pair p = (AbstractLookup.Pair) arr[from];
jtulach@7
   191
jtulach@7
   192
            AbstractLookup.Info info = (AbstractLookup.Info) retain.get(p);
jtulach@7
   193
jtulach@7
   194
            if (info == null) {
jtulach@7
   195
                // was removed
jtulach@7
   196
jtulach@7
   197
                /*
jtulach@7
   198
                if (info != null) {
jtulach@7
   199
                if (info.index < arr.length) {
jtulach@7
   200
                    newArr[info.index] = p;
jtulach@7
   201
                }
jtulach@7
   202
jtulach@7
   203
                if (p.getIndex() != info.index) {
jtulach@7
   204
                    p.setIndex (null, info.index);
jtulach@7
   205
                    changed.add (p);
jtulach@7
   206
                }
jtulach@7
   207
                } else {
jtulach@7
   208
                // removed
jtulach@7
   209
                 */
jtulach@7
   210
                changed.add(p);
jtulach@7
   211
            }
jtulach@7
   212
        }
jtulach@7
   213
    }
jtulach@7
   214
jtulach@7
   215
    /** Queries for instances of given class.
jtulach@7
   216
    * @param clazz the class to check
jtulach@7
   217
    * @return enumeration of Item
jtulach@7
   218
    * @see #unsorted
jtulach@7
   219
    */
jtulach@132
   220
    public <T> Enumeration<Pair<T>> lookup(final Class<T> clazz) {
jtulach@548
   221
        if (content instanceof Object[]) {
jtulach@548
   222
            final Enumeration<Object> all = InheritanceTree.arrayEn((Object[]) content);
jtulach@548
   223
            class JustPairs implements Enumeration<Pair<T>> {
jtulach@548
   224
                private Pair<T> next;
jtulach@7
   225
jglick@833
   226
                @SuppressWarnings("unchecked")
jtulach@548
   227
                private Pair<T> findNext() {
jtulach@548
   228
                    for (;;) {
jtulach@548
   229
                        if (next != null) {
jtulach@548
   230
                            return next;
jtulach@548
   231
                        }
jtulach@548
   232
                        if (!all.hasMoreElements()) {
jtulach@548
   233
                            return null;
jtulach@548
   234
                        }
jtulach@548
   235
                        Object o = all.nextElement();
jtulach@548
   236
                        boolean ok;
jtulach@548
   237
                        if (o instanceof AbstractLookup.Pair) {
jglick@833
   238
                            ok = (clazz == null) || ((AbstractLookup.Pair<?>) o).instanceOf(clazz);
jtulach@548
   239
                        } else {
jtulach@548
   240
                            ok = false;
jtulach@548
   241
                        }
jtulach@548
   242
jtulach@548
   243
                        next = ok ? (Pair<T>) o : null;
jtulach@548
   244
                    }
jtulach@548
   245
                }
jtulach@548
   246
                
jtulach@548
   247
                public boolean hasMoreElements() {
jtulach@548
   248
                    return findNext() != null;
jtulach@7
   249
                }
jtulach@7
   250
jtulach@548
   251
                public Pair<T> nextElement() {
jtulach@548
   252
                    Pair<T> r = findNext();
jtulach@548
   253
                    if (r == null) {
jtulach@548
   254
                        throw new NoSuchElementException();
jtulach@548
   255
                    }
jtulach@548
   256
                    next = null;
jtulach@548
   257
                    return r;
jtulach@548
   258
                }
jtulach@548
   259
            } // end of JustPairs
jtulach@548
   260
            return new JustPairs();
jtulach@7
   261
        } else {
jtulach@547
   262
            return InheritanceTree.emptyEn();
jtulach@7
   263
        }
jtulach@7
   264
    }
jtulach@7
   265
jtulach@7
   266
    /** Associates another result with this storage.
jtulach@7
   267
     */
jtulach@132
   268
    public AbstractLookup.ReferenceToResult registerReferenceToResult(AbstractLookup.ReferenceToResult<?> newRef) {
jtulach@7
   269
        AbstractLookup.ReferenceToResult prev = this.results;
jtulach@7
   270
        this.results = newRef;
jtulach@7
   271
jtulach@7
   272
        return prev;
jtulach@7
   273
    }
jtulach@7
   274
jtulach@7
   275
    /** Cleanup the references
jtulach@7
   276
     */
jtulach@132
   277
    public AbstractLookup.ReferenceToResult cleanUpResult(Lookup.Template<?> templ) {
jtulach@7
   278
        AbstractLookup.ReferenceIterator it = new AbstractLookup.ReferenceIterator(this.results);
jtulach@7
   279
jtulach@434
   280
        while (it.next()) {
jtulach@434
   281
            // empty
jtulach@434
   282
        }
jtulach@7
   283
jtulach@7
   284
        return this.results = it.first();
jtulach@7
   285
    }
jtulach@7
   286
jtulach@7
   287
    /** We use a hash set of all modified Pair to handle the transaction */
jtulach@132
   288
    public Transaction beginTransaction(int ensure) {
jtulach@7
   289
        return new Transaction(ensure, content);
jtulach@7
   290
    }
jtulach@7
   291
jtulach@7
   292
    /** Extract all results.
jtulach@7
   293
     */
jtulach@132
   294
    public void endTransaction(Transaction changed, Set<AbstractLookup.R> modified) {
jtulach@7
   295
        AbstractLookup.ReferenceIterator it = new AbstractLookup.ReferenceIterator(this.results);
jtulach@7
   296
jtulach@7
   297
        if (changed.arr == null) {
jtulach@7
   298
            // either add or remove, only check the content of check HashSet
jtulach@7
   299
            while (it.next()) {
jtulach@7
   300
                AbstractLookup.ReferenceToResult ref = it.current();
jtulach@132
   301
                Iterator<Pair<?>> pairs = changed.iterator();
jtulach@7
   302
jtulach@7
   303
                while (pairs.hasNext()) {
jtulach@7
   304
                    AbstractLookup.Pair p = (AbstractLookup.Pair) pairs.next();
jtulach@7
   305
jtulach@7
   306
                    if (AbstractLookup.matches(ref.template, p, true)) {
jtulach@7
   307
                        modified.add(ref.getResult());
jtulach@7
   308
                    }
jtulach@7
   309
                }
jtulach@7
   310
            }
jtulach@7
   311
        } else {
jtulach@7
   312
            // do full check of changes
jtulach@7
   313
            while (it.next()) {
jtulach@7
   314
                AbstractLookup.ReferenceToResult ref = it.current();
jtulach@7
   315
jtulach@7
   316
                int oldIndex = -1;
jtulach@7
   317
                int newIndex = -1;
jtulach@7
   318
jtulach@7
   319
                for (;;) {
jtulach@7
   320
                    oldIndex = findMatching(ref.template, changed.current, oldIndex);
jtulach@7
   321
                    newIndex = findMatching(ref.template, changed.arr, newIndex);
jtulach@7
   322
jtulach@7
   323
                    if ((oldIndex == -1) && (newIndex == -1)) {
jtulach@7
   324
                        break;
jtulach@7
   325
                    }
jtulach@7
   326
jtulach@7
   327
                    if (
jtulach@7
   328
                        (oldIndex == -1) || (newIndex == -1) ||
jtulach@7
   329
                            !changed.current[oldIndex].equals(changed.arr[newIndex])
jtulach@7
   330
                    ) {
jtulach@7
   331
                        modified.add(ref.getResult());
jtulach@7
   332
jtulach@7
   333
                        break;
jtulach@7
   334
                    }
jtulach@7
   335
                }
jtulach@7
   336
            }
jtulach@7
   337
        }
jtulach@7
   338
jtulach@7
   339
        this.results = it.first();
jtulach@271
   340
        this.content = changed.newContent(this.content);
jtulach@7
   341
    }
jtulach@7
   342
jtulach@7
   343
    private static int findMatching(Lookup.Template t, Object[] arr, int from) {
jtulach@7
   344
        while (++from < arr.length) {
jtulach@7
   345
            if (arr[from] instanceof AbstractLookup.Pair) {
jtulach@7
   346
                if (AbstractLookup.matches(t, (AbstractLookup.Pair) arr[from], true)) {
jtulach@7
   347
                    return from;
jtulach@7
   348
                }
jtulach@7
   349
            }
jtulach@7
   350
        }
jtulach@7
   351
jtulach@7
   352
        return -1;
jtulach@7
   353
    }
jtulach@7
   354
jtulach@7
   355
    /** HashSet with additional field for new array which is callocated
jtulach@7
   356
     * in case we are doing replace to hold all new items.
jtulach@7
   357
     */
jtulach@132
   358
    static final class Transaction extends HashSet<Pair<?>> {
jtulach@7
   359
        /** array with current objects */
jtulach@7
   360
        public final Object[] current;
jtulach@7
   361
jtulach@7
   362
        /** array with new objects */
jtulach@7
   363
        public final Object[] arr;
jtulach@7
   364
jtulach@7
   365
        /** number of objects in the array */
jtulach@7
   366
        private int cnt;
jtulach@7
   367
jtulach@7
   368
        public Transaction(int ensure, Object currentContent) {
jtulach@7
   369
            Integer trashold;
jglick@833
   370
            Object[] _arr;
jtulach@7
   371
jtulach@7
   372
            if (currentContent instanceof Integer) {
jtulach@7
   373
                trashold = (Integer) currentContent;
jglick@833
   374
                _arr = null;
jtulach@7
   375
            } else {
jglick@833
   376
                _arr = (Object[]) currentContent;
jtulach@7
   377
jglick@833
   378
                if (_arr[_arr.length - 1] instanceof Integer) {
jglick@833
   379
                    trashold = (Integer) _arr[_arr.length - 1];
jtulach@7
   380
                } else {
jtulach@7
   381
                    // nowhere to grow we have reached the limit
jtulach@7
   382
                    trashold = null;
jtulach@7
   383
                }
jtulach@7
   384
            }
jtulach@7
   385
jglick@833
   386
            int maxSize = (trashold == null) ? _arr.length : trashold.intValue();
jtulach@7
   387
jtulach@7
   388
            if (ensure > maxSize) {
jtulach@7
   389
                throw new UnsupportedOperationException();
jtulach@7
   390
            }
jtulach@7
   391
jtulach@7
   392
            if (ensure == -1) {
jtulach@7
   393
                // remove => it is ok
jtulach@271
   394
                this.current = currentContent instanceof Integer ? null : (Object[]) currentContent;
jtulach@7
   395
                this.arr = null;
jtulach@7
   396
jtulach@7
   397
                return;
jtulach@7
   398
            }
jtulach@7
   399
jtulach@7
   400
            if (ensure == -2) {
jtulach@7
   401
                // adding one
jglick@833
   402
                if (_arr == null) {
jtulach@7
   403
                    // first time add, let's allocate the array
jglick@833
   404
                    _arr = new Object[2];
jglick@833
   405
                    _arr[1] = trashold;
jtulach@7
   406
                } else {
jglick@833
   407
                    if (_arr[_arr.length - 1] instanceof AbstractLookup.Pair) {
jtulach@7
   408
                        // we are full
jtulach@7
   409
                        throw new UnsupportedOperationException();
jtulach@7
   410
                    } else {
jtulach@7
   411
                        // ensure we have allocated enough space
jglick@833
   412
                        if (_arr.length < 2 || _arr[_arr.length - 2] != null) {
jtulach@7
   413
                            // double the array
jglick@833
   414
                            int newSize = (_arr.length - 1) * 2;
jtulach@107
   415
                            
jtulach@107
   416
                            if (newSize <= 1) {
jtulach@107
   417
                                newSize = 2;
jtulach@107
   418
                            }
jtulach@7
   419
jtulach@7
   420
                            if (newSize > maxSize) {
jtulach@7
   421
                                newSize = maxSize;
jtulach@7
   422
jglick@833
   423
                                if (newSize <= _arr.length) {
jtulach@7
   424
                                    // no space to get in
jtulach@7
   425
                                    throw new UnsupportedOperationException();
jtulach@7
   426
                                }
jtulach@7
   427
jglick@833
   428
                                _arr = new Object[newSize];
jtulach@7
   429
                            } else {
jtulach@7
   430
                                // still a lot of space
jglick@833
   431
                                _arr = new Object[newSize + 1];
jglick@833
   432
                                _arr[newSize] = trashold;
jtulach@7
   433
                            }
jtulach@7
   434
jtulach@7
   435
                            // copy content of original array without the last Integer into 
jtulach@7
   436
                            // the new one
jglick@833
   437
                            System.arraycopy(currentContent, 0, _arr, 0, ((Object[]) currentContent).length - 1);
jtulach@7
   438
                        }
jtulach@7
   439
                    }
jtulach@7
   440
                }
jtulach@7
   441
jglick@833
   442
                this.current = _arr;
jtulach@7
   443
                this.arr = null;
jtulach@7
   444
            } else {
jtulach@7
   445
                // allocate array for complete replacement
jtulach@7
   446
                if (ensure == maxSize) {
jtulach@7
   447
                    this.arr = new Object[ensure];
jtulach@7
   448
                } else {
jtulach@7
   449
                    this.arr = new Object[ensure + 1];
jtulach@7
   450
                    this.arr[ensure] = trashold;
jtulach@7
   451
                }
jtulach@7
   452
jtulach@7
   453
                this.current = (currentContent instanceof Object[]) ? (Object[]) currentContent : new Object[0];
jtulach@7
   454
            }
jtulach@7
   455
        }
jtulach@7
   456
jtulach@132
   457
        public int addPair(AbstractLookup.Pair<?> p) {
jtulach@7
   458
            p.setIndex(null, cnt);
jtulach@7
   459
            arr[cnt++] = p;
jtulach@7
   460
jtulach@7
   461
            return p.getIndex();
jtulach@7
   462
        }
jtulach@7
   463
jtulach@271
   464
        public Object newContent(Object prev) {
jtulach@271
   465
            if (arr == null) {
jtulach@271
   466
                if (current == null) {
jtulach@271
   467
                    return prev;
jtulach@271
   468
                } else {
jtulach@271
   469
                    return current;
jtulach@271
   470
                }
jtulach@271
   471
            } else {
jtulach@271
   472
                return arr;
jtulach@271
   473
            }
jtulach@7
   474
        }
jtulach@7
   475
    }
jtulach@7
   476
     // end of Transaction
jtulach@7
   477
}