| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.openide.util.lookup; |
| |
| import org.openide.util.Lookup; |
| |
| |
| |
| import java.util.*; |
| import java.util.logging.Level; |
| import org.openide.util.lookup.AbstractLookup.Pair; |
| |
| |
| /** ArrayStorage of Pairs from AbstractLookup. |
| * @author Jaroslav Tulach |
| */ |
| final class ArrayStorage extends Object |
| implements AbstractLookup.Storage<ArrayStorage.Transaction> { |
| |
| /** list of items */ |
| private Object content; |
| |
| /** linked list of refernces to results */ |
| private transient AbstractLookup.ReferenceToResult<?> results; |
| |
| /** Constructor |
| */ |
| public ArrayStorage() { |
| this(/*default threshold*/11); |
| } |
| |
| /** Constructs new ArrayStorage */ |
| public ArrayStorage(Integer treshhold) { |
| this.content = treshhold; |
| } |
| |
| /** Adds an item into the tree. |
| * @param item to add |
| * @return true if the Item has been added for the first time or false if some other |
| * item equal to this one already existed in the lookup |
| */ |
| public boolean add(AbstractLookup.Pair<?> item, Transaction changed) { |
| Object[] arr = changed.current; |
| |
| if (changed.arr == null) { |
| // just simple add of one item |
| for (int i = 0; i < arr.length; i++) { |
| if (arr[i] == null) { |
| arr[i] = item; |
| changed.add(item); |
| |
| return true; |
| } |
| |
| if (arr[i].equals(item)) { |
| // reassign the item number |
| item.setIndex(null, ((AbstractLookup.Pair) arr[i]).getIndex()); |
| |
| // already there, but update it |
| arr[i] = item; |
| |
| return false; |
| } |
| } |
| |
| // cannot happen as the beginTransaction ensured we can finish |
| // correctly |
| throw new IllegalStateException( |
| "current objects: " + Arrays.toString(changed.current) + |
| "\nnew objects: " + Arrays.toString(changed.arr) + |
| "\ncnt: " + changed.cnt |
| ); |
| } else { |
| // doing remainAll after that, let Transaction hold the new array |
| int newIndex = changed.addPair(item); |
| |
| for (int i = 0; i < arr.length; i++) { |
| if (arr[i] == null) { |
| changed.add(item); |
| |
| return true; |
| } |
| |
| if (arr[i].equals(item)) { |
| // already there |
| if (i != newIndex) { |
| // change in index |
| changed.add(item); |
| |
| return false; |
| } else { |
| // no change |
| return false; |
| } |
| } |
| } |
| |
| // if not found in the original array |
| changed.add(item); |
| |
| return true; |
| } |
| } |
| |
| /** Removes an item. |
| */ |
| public void remove(AbstractLookup.Pair item, Transaction changed) { |
| Object[] arr = changed.current; |
| if (arr == null) { |
| return; |
| } |
| |
| int found = -1; |
| |
| for (int i = 0; i < arr.length;) { |
| if (arr[i] == null) { |
| // end of task |
| return; |
| } |
| |
| if ((found == -1) && arr[i].equals(item)) { |
| // already there |
| Pair<?> p = (Pair<?>)arr[i]; |
| p.setIndex(null, -1); |
| changed.add(p); |
| found = i; |
| } |
| |
| i++; |
| |
| if (found != -1) { |
| if (i < arr.length && !(arr[i] instanceof Integer)) { |
| // moving the array |
| arr[i - 1] = arr[i]; |
| } else { |
| arr[i - 1] = null; |
| } |
| } |
| } |
| } |
| |
| /** Removes all items that are not present in the provided collection. |
| * @param retain Pair -> AbstractLookup.Info map |
| * @param notify set of Classes that has possibly changed |
| */ |
| public void retainAll(Map retain, Transaction changed) { |
| Object[] arr = changed.current; |
| |
| for (int from = 0; from < arr.length; from++) { |
| if (!(arr[from] instanceof AbstractLookup.Pair)) { |
| // end of content |
| break; |
| } |
| |
| AbstractLookup.Pair p = (AbstractLookup.Pair) arr[from]; |
| |
| AbstractLookup.Info info = (AbstractLookup.Info) retain.get(p); |
| |
| if (info == null) { |
| // was removed |
| |
| /* |
| if (info != null) { |
| if (info.index < arr.length) { |
| newArr[info.index] = p; |
| } |
| |
| if (p.getIndex() != info.index) { |
| p.setIndex (null, info.index); |
| changed.add (p); |
| } |
| } else { |
| // removed |
| */ |
| changed.add(p); |
| } |
| } |
| } |
| |
| /** Queries for instances of given class. |
| * @param clazz the class to check |
| * @return enumeration of Item |
| * @see #unsorted |
| */ |
| public <T> Enumeration<Pair<T>> lookup(final Class<T> clazz) { |
| if (content instanceof Object[]) { |
| final Enumeration<Object> all = InheritanceTree.arrayEn((Object[]) content); |
| class JustPairs implements Enumeration<Pair<T>> { |
| private Pair<T> next; |
| |
| @SuppressWarnings("unchecked") |
| private Pair<T> findNext() { |
| for (;;) { |
| if (next != null) { |
| return next; |
| } |
| if (!all.hasMoreElements()) { |
| return null; |
| } |
| Object o = all.nextElement(); |
| boolean ok; |
| if (o instanceof AbstractLookup.Pair) { |
| ok = (clazz == null) || ((AbstractLookup.Pair<?>) o).instanceOf(clazz); |
| } else { |
| ok = false; |
| } |
| |
| next = ok ? (Pair<T>) o : null; |
| } |
| } |
| |
| public boolean hasMoreElements() { |
| return findNext() != null; |
| } |
| |
| public Pair<T> nextElement() { |
| Pair<T> r = findNext(); |
| if (r == null) { |
| throw new NoSuchElementException(); |
| } |
| next = null; |
| return r; |
| } |
| } // end of JustPairs |
| return new JustPairs(); |
| } else { |
| return InheritanceTree.emptyEn(); |
| } |
| } |
| |
| @Override |
| public <T> Lookup.Result<T> findResult(Lookup.Template<T> t) { |
| AbstractLookup.ReferenceIterator it = new AbstractLookup.ReferenceIterator(this.results); |
| while (it.next()) { |
| if (it.current().template.equals(t)) { |
| return (Lookup.Result<T>) it.current().getResult(); |
| } |
| } |
| return null; |
| } |
| |
| /** Associates another result with this storage. |
| */ |
| public AbstractLookup.ReferenceToResult registerReferenceToResult(AbstractLookup.ReferenceToResult<?> newRef) { |
| AbstractLookup.ReferenceToResult prev = this.results; |
| this.results = newRef; |
| return prev; |
| } |
| |
| /** Cleanup the references |
| */ |
| @Override |
| public AbstractLookup.ReferenceToResult cleanUpResult(Lookup.Template<?> templ) { |
| long now = System.currentTimeMillis(); |
| AbstractLookup.ReferenceIterator it = new AbstractLookup.ReferenceIterator(this.results); |
| |
| int cnt = 0; |
| while (it.next()) { |
| cnt++; |
| } |
| |
| long took = System.currentTimeMillis() - now; |
| if (took > 500 && AbstractLookup.LOG != null) { |
| AbstractLookup.LOG.log(Level.WARNING, |
| "Too long ({0} ms and {1} references) cleanUpResult for {2}", |
| new Object[]{took, cnt, templ != null ? templ.getType() : null} |
| ); |
| } |
| |
| return this.results = it.first(); |
| } |
| |
| /** We use a hash set of all modified Pair to handle the transaction */ |
| public Transaction beginTransaction(int ensure) { |
| return new Transaction(ensure, content); |
| } |
| |
| /** Extract all results. |
| */ |
| public void endTransaction(Transaction changed, Set<AbstractLookup.R> modified) { |
| AbstractLookup.ReferenceIterator it = new AbstractLookup.ReferenceIterator(this.results); |
| |
| if (changed.arr == null) { |
| // either add or remove, only check the content of check HashSet |
| while (it.next()) { |
| AbstractLookup.ReferenceToResult ref = it.current(); |
| Iterator<Pair<?>> pairs = changed.iterator(); |
| |
| while (pairs.hasNext()) { |
| AbstractLookup.Pair p = (AbstractLookup.Pair) pairs.next(); |
| |
| if (AbstractLookup.matches(ref.template, p, true)) { |
| modified.add(ref.getResult()); |
| } |
| } |
| } |
| } else { |
| // do full check of changes |
| while (it.next()) { |
| AbstractLookup.ReferenceToResult ref = it.current(); |
| |
| int oldIndex = -1; |
| int newIndex = -1; |
| |
| for (;;) { |
| oldIndex = findMatching(ref.template, changed.current, oldIndex); |
| newIndex = findMatching(ref.template, changed.arr, newIndex); |
| |
| if ((oldIndex == -1) && (newIndex == -1)) { |
| break; |
| } |
| |
| if ( |
| (oldIndex == -1) || (newIndex == -1) || |
| !changed.current[oldIndex].equals(changed.arr[newIndex]) |
| ) { |
| modified.add(ref.getResult()); |
| |
| break; |
| } |
| } |
| } |
| } |
| |
| this.results = it.first(); |
| this.content = changed.newContent(this.content); |
| } |
| |
| private static int findMatching(Lookup.Template t, Object[] arr, int from) { |
| while (++from < arr.length) { |
| if (arr[from] instanceof AbstractLookup.Pair) { |
| if (AbstractLookup.matches(t, (AbstractLookup.Pair) arr[from], true)) { |
| return from; |
| } |
| } |
| } |
| |
| return -1; |
| } |
| |
| /** HashSet with additional field for new array which is callocated |
| * in case we are doing replace to hold all new items. |
| */ |
| static final class Transaction extends HashSet<Pair<?>> { |
| /** array with current objects */ |
| public final Object[] current; |
| |
| /** array with new objects */ |
| public final Object[] arr; |
| |
| /** number of objects in the array */ |
| private int cnt; |
| |
| public Transaction(int ensure, Object currentContent) { |
| Integer trashold; |
| Object[] _arr; |
| |
| if (currentContent instanceof Integer) { |
| trashold = (Integer) currentContent; |
| _arr = null; |
| } else { |
| _arr = (Object[]) currentContent; |
| |
| if (_arr[_arr.length - 1] instanceof Integer) { |
| trashold = (Integer) _arr[_arr.length - 1]; |
| } else { |
| // nowhere to grow we have reached the limit |
| trashold = null; |
| } |
| } |
| |
| int maxSize = (trashold == null) ? _arr.length : trashold.intValue(); |
| |
| if (ensure > maxSize) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| if (ensure == -1) { |
| // remove => it is ok |
| this.current = currentContent instanceof Integer ? null : (Object[]) currentContent; |
| this.arr = null; |
| |
| return; |
| } |
| |
| if (ensure == -2) { |
| // adding one |
| if (_arr == null) { |
| // first time add, let's allocate the array |
| _arr = new Object[2]; |
| _arr[1] = trashold; |
| } else { |
| if (_arr[_arr.length - 1] instanceof AbstractLookup.Pair) { |
| // we are full |
| throw new UnsupportedOperationException(); |
| } else { |
| // ensure we have allocated enough space |
| if (_arr.length < 2 || _arr[_arr.length - 2] != null) { |
| // double the array |
| int newSize = (_arr.length - 1) * 2; |
| |
| if (newSize <= 1) { |
| newSize = 2; |
| } |
| |
| if (newSize > maxSize) { |
| newSize = maxSize; |
| |
| if (newSize <= _arr.length) { |
| // no space to get in |
| throw new UnsupportedOperationException(); |
| } |
| |
| _arr = new Object[newSize]; |
| } else { |
| // still a lot of space |
| _arr = new Object[newSize + 1]; |
| _arr[newSize] = trashold; |
| } |
| |
| // copy content of original array without the last Integer into |
| // the new one |
| System.arraycopy(currentContent, 0, _arr, 0, ((Object[]) currentContent).length - 1); |
| } |
| } |
| } |
| |
| this.current = _arr; |
| this.arr = null; |
| } else { |
| // allocate array for complete replacement |
| if (ensure == maxSize) { |
| this.arr = new Object[ensure]; |
| } else { |
| this.arr = new Object[ensure + 1]; |
| this.arr[ensure] = trashold; |
| } |
| |
| this.current = (currentContent instanceof Object[]) ? (Object[]) currentContent : new Object[0]; |
| } |
| } |
| |
| public int addPair(AbstractLookup.Pair<?> p) { |
| p.setIndex(null, cnt); |
| arr[cnt++] = p; |
| |
| return p.getIndex(); |
| } |
| |
| public Object newContent(Object prev) { |
| if (arr == null) { |
| if (current == null) { |
| return prev; |
| } else { |
| return current; |
| } |
| } else { |
| return arr; |
| } |
| } |
| } |
| // end of Transaction |
| } |