| /******************************************************************************* |
| * Copyright (c) 2009, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Cloudsmith Inc. - rewrite for smaller memory footprint |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.metadata; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.NoSuchElementException; |
| import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils; |
| import org.eclipse.equinox.p2.core.IPool; |
| import org.eclipse.equinox.p2.metadata.IInstallableUnit; |
| import org.eclipse.equinox.p2.metadata.Version; |
| import org.eclipse.equinox.p2.query.CollectionResult; |
| import org.eclipse.equinox.p2.query.Collector; |
| import org.eclipse.equinox.p2.query.IQuery; |
| import org.eclipse.equinox.p2.query.IQueryResult; |
| import org.eclipse.equinox.p2.query.QueryUtil; |
| |
| /** |
| * A map that stores {@link IInstallableUnit} instances in a way that is efficient to query |
| */ |
| public class IUMap implements Cloneable { |
| /** |
| * Iterator over all the {@link IInstallableUnit} instances in the map. |
| */ |
| public class MapIterator implements Iterator<IInstallableUnit> { |
| //iterator over the keys in UIMap |
| private final Iterator<Object> unitIterator; |
| private IInstallableUnit[] currentBucket; |
| private int bucketIndex = 0; |
| private IInstallableUnit nextElement = null; |
| |
| MapIterator() { |
| super(); |
| unitIterator = units.values().iterator(); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return positionNext(); |
| } |
| |
| @Override |
| public IInstallableUnit next() { |
| if (!positionNext()) |
| throw new NoSuchElementException(); |
| |
| IInstallableUnit nxt = nextElement; |
| nextElement = null; |
| return nxt; |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private boolean positionNext() { |
| if (nextElement != null) |
| return true; |
| |
| if (currentBucket != null) { |
| nextElement = currentBucket[bucketIndex]; |
| if (++bucketIndex == currentBucket.length) { |
| currentBucket = null; |
| bucketIndex = -1; |
| } |
| return true; |
| } |
| |
| if (!unitIterator.hasNext()) |
| return false; |
| |
| Object val = unitIterator.next(); |
| if (val instanceof IInstallableUnit) |
| nextElement = (IInstallableUnit) val; |
| else { |
| currentBucket = (IInstallableUnit[]) val; |
| nextElement = currentBucket[0]; |
| bucketIndex = 1; |
| } |
| return true; |
| } |
| } |
| |
| /** |
| * Map<String,Object> mapping IU id to either arrays of iu's or a single iu with that id. |
| */ |
| final Map<String, Object> units = new HashMap<>(); |
| |
| public IUMap() { |
| // |
| } |
| |
| private IUMap(IUMap cloneSource) { |
| units.putAll(cloneSource.units); |
| } |
| |
| public void add(IInstallableUnit unit) { |
| String key = unit.getId(); |
| Object matching = units.get(key); |
| if (matching == null) { |
| units.put(key, unit); |
| return; |
| } |
| |
| // We already had something at this key position. It must be |
| // preserved. |
| if (matching.getClass().isArray()) { |
| // Entry is an array. Add unique |
| IInstallableUnit[] iuArr = (IInstallableUnit[]) matching; |
| int idx = iuArr.length; |
| while (--idx >= 0) |
| if (iuArr[idx].equals(unit)) |
| // This unit has already been added |
| return; |
| |
| IInstallableUnit[] iuArrPlus = new IInstallableUnit[iuArr.length + 1]; |
| System.arraycopy(iuArr, 0, iuArrPlus, 0, iuArr.length); |
| iuArrPlus[iuArr.length] = unit; |
| units.put(unit.getId(), iuArrPlus); |
| } else { |
| IInstallableUnit old = (IInstallableUnit) matching; |
| if (!old.equals(unit)) |
| units.put(key, new IInstallableUnit[] {old, unit}); |
| } |
| } |
| |
| public void addAll(IInstallableUnit[] toAdd) { |
| for (IInstallableUnit toAdd1 : toAdd) { |
| add(toAdd1); |
| } |
| } |
| |
| public void addAll(Collection<IInstallableUnit> toAdd) { |
| for (IInstallableUnit unit : toAdd) { |
| add(unit); |
| } |
| } |
| |
| public void clear() { |
| units.clear(); |
| } |
| |
| @Override |
| public IUMap clone() { |
| return new IUMap(this); |
| } |
| |
| public Iterator<IInstallableUnit> iterator() { |
| return new MapIterator(); |
| } |
| |
| public boolean contains(IInstallableUnit unit) { |
| return !internalGet(unit.getId(), unit.getVersion()).isEmpty(); |
| } |
| |
| /** |
| * Returns a collection of units that has the given <code>id</code>. |
| * @param id The id of the desired units. Must not be <code>null</code>. |
| * @return The units corresponding to the given <code>id</code>. |
| */ |
| public Collection<IInstallableUnit> getUnits(String id) { |
| Object bucket = units.get(id); |
| if (bucket == null) |
| return Collections.emptyList(); |
| return bucket.getClass().isArray() ? CollectionUtils.unmodifiableList((IInstallableUnit[]) bucket) : Collections.singletonList((IInstallableUnit) bucket); |
| } |
| |
| public IQueryResult<IInstallableUnit> get(String id) { |
| return internalGet(id, null); |
| } |
| |
| private IQueryResult<IInstallableUnit> internalGet(String id, Version version) { |
| if (id == null) { |
| IQuery<IInstallableUnit> query = version == null ? QueryUtil.createIUAnyQuery() : QueryUtil.createIUQuery(null, version); |
| return query.perform(iterator()); |
| } |
| |
| Collection<IInstallableUnit> idUnits = getUnits(id); |
| if (idUnits.isEmpty()) |
| return Collector.emptyCollector(); |
| return version == null ? new CollectionResult<>(idUnits) : QueryUtil.createIUQuery(id, version).perform(idUnits.iterator()); |
| } |
| |
| public IInstallableUnit get(String id, Version version) { |
| IQueryResult<IInstallableUnit> result = internalGet(id, version); |
| return result.isEmpty() ? null : result.iterator().next(); |
| } |
| |
| public void remove(IInstallableUnit unit) { |
| String key = unit.getId(); |
| Object matching = units.get(key); |
| if (matching == null) |
| return; |
| |
| if (matching instanceof IInstallableUnit) { |
| if (matching.equals(unit)) |
| units.remove(key); |
| return; |
| } |
| |
| IInstallableUnit[] array = (IInstallableUnit[]) matching; |
| int idx = array.length; |
| while (--idx >= 0) { |
| if (unit.equals(array[idx])) { |
| if (array.length == 2) { |
| // We no longer need this array. Replace it with the |
| // entry that we keep. |
| units.put(key, idx == 0 ? array[1] : array[0]); |
| break; |
| } |
| |
| // Shrink the array |
| IInstallableUnit[] newArray = new IInstallableUnit[array.length - 1]; |
| if (idx > 0) |
| System.arraycopy(array, 0, newArray, 0, idx); |
| if (idx + 1 < array.length) |
| System.arraycopy(array, idx + 1, newArray, idx, array.length - (idx + 1)); |
| units.put(key, newArray); |
| break; |
| } |
| } |
| } |
| |
| public void removeAll(Collection<IInstallableUnit> toRemove) { |
| for (IInstallableUnit iu : toRemove) |
| remove(iu); |
| } |
| |
| /** |
| * Replace all instances of the IInstallableUnits in the receiver |
| * with the shared IInstallableUnits from the provided iuPool. |
| * This operation is a no-op if iuPool is null. |
| * |
| * @param iuPool an IPool containing the shared IInstallableUnits |
| */ |
| public void compress(IPool<IInstallableUnit> iuPool) { |
| if (iuPool == null) { |
| return; |
| } |
| |
| Iterator<Entry<String, Object>> entries = units.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Entry<String, Object> entry = entries.next(); |
| Object value = entry.getValue(); |
| if (value.getClass().isArray()) { |
| IInstallableUnit[] array = (IInstallableUnit[]) value; |
| for (int i = 0; i < array.length; i++) { |
| array[i] = iuPool.add(array[i]); |
| } |
| } else { |
| entry.setValue(iuPool.add((IInstallableUnit) value)); |
| } |
| } |
| } |
| } |