blob: 74375c10851cf162a183e465a5252d4e9b876493 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2021 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
*******************************************************************************/
package org.eclipse.osgi.internal.container;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import org.eclipse.osgi.container.ModuleCapability;
import org.eclipse.osgi.container.ModuleRequirement;
import org.eclipse.osgi.container.ModuleWire;
/**
* An immutable list of elements for which each element has a name space. All
* elements are kept in a single list to avoid creating a list for each name
* space. The elements provided at construction are assumed to be ordered such
* that all elements with the same name space are ordered together in one
* continuous sequence An indexes list is used to keep track of the beginning
* indexes for each namespace contained in the list. Assuming the number of
* namespaces used by a bundle is relatively small compared to the overall
* number of elements this makes it faster to find the first index for a
* specific namespace without having to iterate over the complete list of
* elements.
*
* @param <E> The element type which will have a name space associated with it
*/
public class NamespaceList<E> {
public final static Function<ModuleWire, String> WIRE = wire -> {
return wire.getCapability().getNamespace();
};
public final static Function<ModuleCapability, String> CAPABILITY = ModuleCapability::getNamespace;
public final static Function<ModuleRequirement, String> REQUIREMENT = ModuleRequirement::getNamespace;
private final List<E> elements;
private final Map<String, List<E>> namespaces;
/**
* Create a new name space list with the specified elements. The elements must
* be sorted properly by name space.
*
* @param elements the ordered list of elements
* @param getNamespace a function that retrieves the namespace for each element
*/
public NamespaceList(List<E> elements, Function<E, String> getNamespace) {
List<E> unmodifiableElements = Collections.unmodifiableList(elements);
Map<String, List<E>> tmpNamespaces = new LinkedHashMap<>(5);
int size = unmodifiableElements.size();
int currentStart = 0;
String current = null;
boolean consolidated = false;
for (int i = 0; i < size; i++) {
String namespace = getNamespace.apply(unmodifiableElements.get(i));
if (current == null) {
current = namespace;
}
if (!current.equals(namespace)) {
int currentEnd = i;
consolidated |= addNamespaceList(tmpNamespaces, unmodifiableElements, current, currentStart,
currentEnd);
current = namespace;
currentStart = i;
}
}
if (size > 0) {
// add the last namespace
consolidated |= addNamespaceList(tmpNamespaces, unmodifiableElements, current, currentStart, size);
}
if (consolidated) {
List<E> tmpElements = new ArrayList<>(size);
for (Entry<String, List<E>> e : tmpNamespaces.entrySet()) {
tmpElements.addAll(e.getValue());
}
this.elements = Collections.unmodifiableList(tmpElements);
} else {
this.elements = unmodifiableElements;
}
this.namespaces = Collections.unmodifiableMap(tmpNamespaces);
}
private boolean addNamespaceList(Map<String, List<E>> tmpNamespaces, List<E> unmodifiableElements, String namespace,
int start, int end) {
List<E> namespaceList = unmodifiableElements.subList(start, end);
List<E> existing = tmpNamespaces.get(namespace);
boolean consolidated = false;
if (existing != null) {
// This should be an error, consolidate the lists
List<E> consolidateList = new ArrayList<>(existing);
consolidateList.addAll(namespaceList);
namespaceList = Collections.unmodifiableList(consolidateList);
consolidated = true;
}
tmpNamespaces.put(namespace, namespaceList);
return consolidated;
}
public boolean isEmpty() {
return elements.isEmpty();
}
/**
* returns an unmodifiable list of elements with the specified name space. An
* empty list is returned if there are no elements with the specified namespace.
* A {@code null} namespace can be used to get all elements.
*
* @param namespace the name space of the elements to return. May be
* {@code null}
* @return The list of elements found
*/
public List<E> getList(String namespace) {
if (namespace == null) {
return elements;
}
return namespaces.getOrDefault(namespace, Collections.emptyList());
}
/**
* Returns the beginning index (inclusively) and ending index (exclusively) or
* {@code null} if no element has the specified namespace
*
* @param namespace the name space to find the indexes for
* @return indexes found for the namespace or {@code null} if no elements exist
* with the name space.
*/
public Entry<Integer, Integer> getNamespaceIndex(String namespace) {
int startIndex = 0;
for (Entry<String, ? extends List<E>> entry : namespaces.entrySet()) {
if (entry.getKey().equals(namespace)) {
int end = startIndex + entry.getValue().size();
return new AbstractMap.SimpleEntry<>(startIndex, end);
}
startIndex += entry.getValue().size();
}
return null;
}
/**
* Returns a copy of all the elements in this list
*
* @return a copy of all the elements in this list
*/
public List<E> copyList() {
return new ArrayList<>(elements);
}
}