All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.osgi.internal.container.NamespaceList Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * 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
 *     Hannes Wellmann - Bug 573025 & 573026: introduce and apply NamespaceList.Builder
 *******************************************************************************/
package org.eclipse.osgi.internal.container;

import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
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 namespace.
 * 

* The elements are stored in a map where each key is a namespace and the * associated value is the list of all elements with that namespace in this * NamespaceList. Within one namespace the element's order is stable. Due to * this internal structure access to the elements of off one or all namespace(s) * has always constant runtime regardless of the number of namespaces present. *

* * @param the type of elements in this list, which have a name-space * associated */ public class NamespaceList { public final static Function WIRE = new Function() { public String apply(ModuleWire wire) { return wire.getCapability().getNamespace(); } }; public final static Function CAPABILITY = new Function() { public String apply(ModuleCapability capability) { return capability.getNamespace(); } }; public final static Function REQUIREMENT = new Function() { public String apply(ModuleRequirement requirement) { return requirement.getNamespace(); } }; /** * Returns an empty NamespaceList. *

* The required argument is used to derive the type of elements and if a builder * is created from the returned list. *

* * @param the type of elements in the NamespaceList * @param getNamespace the function to compute the namespace of an element * @return an empty NamespaceList */ public static NamespaceList empty(Function getNamespace) { return new NamespaceList<>(getNamespace, Collections.emptyMap(), Collections.emptyList()); } private final List elements; private final Map> namespaces; private final Function getNamespace; NamespaceList(Function getNamespace, Map> namespaces, List fullList) { this.getNamespace = getNamespace; this.namespaces = namespaces; this.elements = fullList; } Map> namespaces() { return namespaces; } /** * Returns {@code true} if this NamespaceList contains no elements. * * @return {@code true} if this list contains no elements */ public boolean isEmpty() { return elements.isEmpty(); } /** * Returns an immutable list of elements with the specified namespace. *

* An empty list is returned if there are no elements with the specified * namespace. For the {@code null} namespace the elements of all namespaces are * returned as flat. *

* * @param namespace the namespace of the elements to return. May be {@code null} * @return The list of elements found */ public List getList(String namespace) { if (namespace == null) { return elements; } return namespaces.getOrDefault(namespace, Collections.emptyList()); } /** * Returns a new {@link Builder NamespaceList.Builder} that contains all * elements of this NamespaceList. *

* The returned builder uses the same function to compute the namespace of an * element like this NamespaceList. *

* * @return a new builder containing all elements of this list */ public Builder createBuilder() { Builder builder = Builder.create(getNamespace); builder.addAll(this); return builder; } /** * A reusable builder to create {@link NamespaceList NamespaceLists}. * * @param the type of elements in this builder * @author Hannes Wellmann */ public static class Builder extends AbstractCollection { /** * Returns a new {@link Builder NamespaceList.Builder} that uses the specified * function to compute the namespace of its elements. * * @param the type of elements in this builder * @param getNamespace the function to compute the namespace of an element * @return a new builder */ public static Builder create(Function getNamespace) { return new Builder<>(getNamespace, 3); } private final Function getNamespace; private LinkedHashMap> namespaceElements; private int size = 0; private List lastBuildElements; private Builder(Function getNamespace, int expectedNamespaces) { this.getNamespace = getNamespace; this.namespaceElements = new LinkedHashMap<>(expectedNamespaces * 4 / 3 + 1); } @Override public int size() { return size; } @Override public Iterator iterator() { prepareModification(); final Iterator> outer = namespaceElements.values().iterator(); return new Iterator() { Iterator inner = Collections.emptyIterator(); List lastInnerList = null; @Override public boolean hasNext() { while (!inner.hasNext() && outer.hasNext()) { lastInnerList = outer.next(); inner = lastInnerList.iterator(); } return inner.hasNext(); } public E next() { if (!hasNext()) { throw new NoSuchElementException(); } return inner.next(); } @Override @SuppressWarnings("synthetic-access") public void remove() { inner.remove(); Builder.this.size--; if (lastInnerList.isEmpty()) { outer.remove(); } } }; } @Override public void clear() { namespaceElements = new LinkedHashMap<>(); // could have been build before, so map must not be cleared lastBuildElements = null; size = 0; } /** * Returns an immutable list of elements with the specified namespace in this * builder. *

* An empty list is returned if there are no elements with the specified * namespace. For the {@code null} namespace the elements of all namespaces are * returned as flat. *

* * @param namespace the namespace of the elements to return. May be {@code null} * @return the list of element with the specified namespace */ public List getNamespaceElements(String namespace) { if (namespace == null) { List list = new ArrayList<>(size); for (List es : namespaceElements.values()) { list.addAll(es); } return Collections.unmodifiableList(list); } List namespaceList = namespaceElements.get(namespace); return namespaceList != null ? Collections.unmodifiableList(new ArrayList<>(namespaceList)) : Collections.emptyList(); } /** * Returns a new builder whose content is the result of applying the specified * transformation to each element of this builder. *

* It is assumed that the transformation does not change the element's * namespace, so the namespace of original and transformed element are the same! * This builder is not modified. *

* * @param the type of elements in the returned builder * @param transformation the transformation applied to each element * @param newGetNamespace the function to compute the namespace of a transformed * element * @return a new builder containing the result of applying the transformation to * each element in this builder */ public Builder transformIntoCopy(Function transformation, Function newGetNamespace) { Builder transformedBuilder = new Builder<>(newGetNamespace, this.namespaceElements.size()); transformedBuilder.size = this.size; for (Map.Entry> entry : namespaceElements.entrySet()) { List es = entry.getValue(); List transformedElements = new ArrayList<>(es.size()); for (E e : es) { transformedElements.add(transformation.apply(e)); } transformedBuilder.namespaceElements.put(entry.getKey(), transformedElements); } return transformedBuilder; } // --- addition --- @Override public boolean add(E e) { prepareModification(); String namespace = getNamespace.apply(e); getNamespaceList(namespace).add(e); this.size++; return true; } @Override public boolean addAll(Collection c) { if (c.isEmpty()) { return false; } prepareModification(); if (c instanceof Builder) { @SuppressWarnings("unchecked") Builder builder = (Builder) c; return addAll(builder.namespaceElements); } String currentNamespace = null; // $NON-NLS-1$ List currentNamespaceList = null; for (E e : c) { String namespace = getNamespace.apply(e); // optimization if elements are already grouped per namespace if (currentNamespace == null || !currentNamespace.equals(namespace)) { currentNamespace = namespace; currentNamespaceList = getNamespaceList(namespace); } currentNamespaceList.add(e); } this.size += c.size(); return true; } /** * Adds all elements in the specified NamespaceList to this builder. * * @param list the NamespaceList containing elements to be added * @return true if any element was added to this builder */ public boolean addAll(NamespaceList list) { if (list.isEmpty()) { return false; } prepareModification(); return addAll(list.namespaces()); } private boolean addAll(Map> perNamespaceElements) { for (Map.Entry> entry : perNamespaceElements.entrySet()) { List es = entry.getValue(); getNamespaceList(entry.getKey()).addAll(es); this.size += es.size(); } return true; } private List getNamespaceList(String namespace) { return namespaceElements.computeIfAbsent(namespace, new Function>() { public List apply(String n) { return new ArrayList<>(); } }); } /** * Appends all elements in the specified NamespaceList to this builder that * satisfy both specified predicates for themselves and their namespace. *

* For an element to be added both predicates, the one for the namespace as well * as the one for the element itself must be satisfied. *

* * @param list the NamespaceList containing elements to be added * @param namespaceFilter the predicate that returns true for namespaces whose * elements should not be excluded from being added * @param elementFilter the predicate that returns true for elements to be * added * */ public void addAllFiltered(NamespaceList list, Predicate namespaceFilter, Predicate elementFilter) { addAllFilteredAfterLastMatch(list, namespaceFilter, elementFilter, null); } /** * Inserts all elements in the specified NamespaceList to this builder that * satisfy both specified predicates for themselves and their namespace, after * the last element in this builder for the corresponding namespace that * satisfies the specified bi-predicate together with the corresponding element * to be added. *

* For an element to be added both predicates, the one for the namespace as well * as the one for the element itself must be satisfied. If both predicates are * satisfied by an element of the specified list, it is added after the * last element with the same namespace in this builder that satisfies the * specified bi-predicate together with the element to add. *

* * @param list the NamespaceList containing elements to be added * @param namespaceFilter the predicate that returns true for namespaces whose * elements should not be excluded from being added * @param elementFilter the predicate that returns true for elements to be * added * @param insertionMatcher the bi-predicate whose first argument is the element * to add and second argument is an element for the same * namespace in this builder, which returns true if the * element to add can be added after this builder's * element */ public void addAllFilteredAfterLastMatch(NamespaceList list, Predicate namespaceFilter, Predicate elementFilter, BiPredicate insertionMatcher) { if (list.isEmpty()) { return; } prepareModification(); for (Map.Entry> entry : list.namespaces().entrySet()) { String namespace = entry.getKey(); if (namespaceFilter.test(namespace)) { List targetList = getNamespaceList(namespace); List elementsToAdd = entry.getValue(); for (E toAdd : elementsToAdd) { if (elementFilter.test(toAdd)) { if (insertionMatcher == null) { targetList.add(toAdd); } else { addAfterLastMatch(toAdd, targetList, new Predicate() { public boolean test(E e) { return insertionMatcher.test(toAdd, e); } }); } this.size++; } } if (targetList.isEmpty()) { // maybe no elements are added namespaceElements.remove(namespace); } } } } private void addAfterLastMatch(E e, List list, Predicate matcher) { for (int i = list.size() - 1; 0 <= i; i--) { if (matcher.test(list.get(i))) { list.add(i + 1, e); return; } } list.add(0, e); } // --- removal --- @Override public boolean remove(Object o) { @SuppressWarnings("unchecked") E e = (E) o; String namespace; try { namespace = getNamespace.apply(e); } catch (ClassCastException ex) { return false; // e does not seem to be of type E after all } prepareModification(); int sizeBefore = this.size; removeNamespaceElement(namespace, e); return this.size < sizeBefore; } private void removeNamespaceElement(String namespace, E element) { namespaceElements.computeIfPresent(namespace, new BiFunction, List>() { public List apply (String n, List es) { if (es.remove(element)) { Builder.this.size--; } return es.isEmpty() ? null : es; } }); } @Override public boolean removeAll(Collection c) { if (c.isEmpty()) { return false; } prepareModification(); // this is more efficient than the super implementation boolean removed = false; for (Object e : c) { removed |= remove(e); } return removed; } /** * Removes from this builder all elements of each namespace that satisfies the * specified predicate. * * @param filter the predicate which returns true for a namespace to remove */ public void removeNamespaceIf(Predicate filter) { prepareModification(); namespaceElements.entrySet().removeIf(new Predicate>>() { public boolean test(Map.Entry> e) { if (filter.test(e.getKey())) { Builder.this.size -= e.getValue().size(); return true; } return false; } }); } @Override public boolean removeIf(Predicate filter) { prepareModification(); int s = size; namespaceElements.values().removeIf(new Predicate>() { public boolean test(List es) { return removeElementsIf(es, filter) == null; } }); return size < s; } /** * Removes from this builder those elements of the specified namespace that * satisfy the specified predicate. * * @param namespace the namespace of * @param filter the predicate which returns true for elements to remove */ public void removeElementsOfNamespaceIf(String namespace, Predicate filter) { prepareModification(); namespaceElements.computeIfPresent(namespace, new BiFunction, List>() { public List apply(String n, List es) { return removeElementsIf(es, filter); } }); } private List removeElementsIf(List list, Predicate filter) { int sizeBefore = list.size(); list.removeIf(filter); this.size -= sizeBefore - list.size(); return list.isEmpty() ? null : list; } // --- build --- /** * Returns an immutable {@link NamespaceList} containing a snapshot of the * current elements of this builder. *

* The content of this builder is not changed by this call and subsequent * modifications to this builder do not reflect into the returned list (the * returned list is not connected to this builder at all). *

* * @return a {@link NamespaceList} reflecting the current state of this builder */ public NamespaceList build() { if (size == 0) { return empty(getNamespace); } if (lastBuildElements == null) { lastBuildElements = new ArrayList<>(size); for (List es : namespaceElements.values()) { lastBuildElements.addAll(es); } lastBuildElements = Collections.unmodifiableList(lastBuildElements); int[] start = new int[] { 0 }; namespaceElements.replaceAll(new BiFunction, List>() { public List apply(String n, List es) { int from = start[0]; int to = start[0] += es.size(); return lastBuildElements.subList(from, to); } }); } return new NamespaceList<>(getNamespace, namespaceElements, lastBuildElements); } private void prepareModification() { if (lastBuildElements != null) { // this builder was build before. Create a copy of the Map and their // namespace-lists for subsequent modification namespaceElements = new LinkedHashMap<>(namespaceElements); namespaceElements.replaceAll(new BiFunction, List>() { public List apply(String n, List es) { return new ArrayList<>(es); } }); lastBuildElements = null; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy