| /* |
| * 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.netbeans; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.LinkedHashSet; |
| import java.util.Set; |
| |
| /** Encapsulates operations on parents. |
| * |
| * @author Jaroslav Tulach <jtulach@netbeans.org> |
| */ |
| final class ProxyClassParents { |
| /** All parents of this classloader, including their parents recursively */ |
| private final Set<ProxyClassLoader> parentSet; |
| private final boolean transitive; |
| private final ClassLoader systemCL; |
| |
| private ProxyClassParents( |
| ClassLoader systemCL, |
| Set<ProxyClassLoader> parentSet, |
| boolean transitive |
| ) { |
| this.systemCL = systemCL; |
| this.parentSet = parentSet; |
| // = new LinkedHashSet<ProxyClassLoader>(); |
| this.transitive = transitive; |
| } |
| |
| |
| /** Coalesce parent classloaders into an optimized set. |
| * This means that all parents of the specified classloaders |
| * are also added recursively, removing duplicates along the way. |
| * Search order should be preserved (parents before children, stable w.r.t. inputs). |
| * @param loaders list of suggested parents (no nulls or duplicates permitted) |
| * @return optimized list of parents (no nulls or duplicates) |
| * @throws IllegalArgumentException if there are cycles |
| */ |
| static ProxyClassParents coalesceParents( |
| ClassLoader root, |
| ClassLoader[] loaders, ClassLoader systemCL, boolean transitive |
| ) throws IllegalArgumentException { |
| final ClassLoader[] arr = { systemCL }; |
| final Set<ProxyClassLoader> parents = coalesceAppend( |
| root, Collections.<ProxyClassLoader>emptySet(), loaders, arr |
| ); |
| return new ProxyClassParents(arr[0], parents,transitive); |
| } |
| |
| /** Coalesce a new set of loaders into the existing ones. |
| */ |
| private static Set<ProxyClassLoader> coalesceAppend( |
| ClassLoader root, |
| Set<ProxyClassLoader> existing, ClassLoader[] appended, ClassLoader[] systemCL |
| ) throws IllegalArgumentException { |
| int likelySize = existing.size() + appended.length; |
| |
| LinkedHashSet<ClassLoader> uniq = new LinkedHashSet<ClassLoader>(likelySize); |
| uniq.addAll(existing); |
| |
| if (uniq.containsAll(Arrays.asList(appended))) { |
| return existing; |
| } // No change required. |
| |
| for (ClassLoader l : appended) { |
| addRec(root, uniq, l); |
| } // add all loaders (maybe recursively) |
| |
| // validate the configuration |
| // it is valid if all heading non-ProxyClassLoaders are parents of the last one |
| boolean head = true; |
| Set<ProxyClassLoader> pcls = new LinkedHashSet<ProxyClassLoader>(uniq.size()); |
| for (ClassLoader l : uniq) { |
| if (head) { |
| if (l instanceof ProxyClassLoader) { |
| // only PCLs after this point |
| head = false; |
| pcls.add((ProxyClassLoader)l); |
| } else { |
| if (isParentOf(systemCL[0], l)) { |
| systemCL[0] = l; |
| } else { |
| throw new IllegalArgumentException("Bad ClassLoader ordering: " + Arrays.asList(appended)); |
| } |
| } |
| } else { |
| if (l instanceof ProxyClassLoader) { |
| pcls.add((ProxyClassLoader)l); |
| } else { |
| throw new IllegalArgumentException("Bad ClassLoader ordering: " + Arrays.asList(appended)); |
| |
| } |
| } |
| } |
| return pcls; |
| } |
| |
| private static void addRec( |
| ClassLoader root, |
| Set<ClassLoader> resultingUnique, |
| ClassLoader loader |
| ) throws IllegalArgumentException { |
| if (loader == root) { |
| throw new IllegalArgumentException("cycle in parents");// NOI18N |
| } |
| if (resultingUnique.contains(loader)) { |
| return; |
| } |
| if (loader instanceof ProxyClassLoader && ((ProxyClassLoader)loader).parents.transitive) { |
| for (ProxyClassLoader lpar : ((ProxyClassLoader)loader).parents.loaders()) { |
| addRec(root, resultingUnique, lpar); |
| } |
| } |
| resultingUnique.add(loader); |
| } |
| |
| boolean contains(ProxyClassLoader pcl) { |
| return parentSet.contains(pcl); |
| } |
| |
| Iterable<ProxyClassLoader> loaders() { |
| boolean assertOn = false; |
| assert assertOn = true; |
| return assertOn ? Collections.unmodifiableSet(parentSet) : parentSet; |
| } |
| |
| int size() { |
| return parentSet.size(); |
| } |
| |
| ProxyClassParents append(ClassLoader root, ClassLoader[] nueparents) { |
| ClassLoader[] arr = { systemCL }; |
| Set<ProxyClassLoader> parents = coalesceAppend(root, parentSet, nueparents, arr); |
| return new ProxyClassParents(arr[0], parents, transitive); |
| } |
| |
| private static boolean isParentOf(ClassLoader parent, ClassLoader child) { |
| while (child != null) { |
| if (child == parent) { |
| return true; |
| } |
| child = child.getParent(); |
| } |
| return false; |
| } |
| |
| ClassLoader systemCL() { |
| return systemCL; |
| } |
| |
| boolean isTransitive() { |
| return transitive; |
| } |
| |
| ProxyClassParents changeSystemClassLoader(ClassLoader s) { |
| return new ProxyClassParents(s, parentSet, transitive); |
| } |
| } |