blob: ec1c147fc675c7c07583243516c62bf39dc5d0e1 [file] [log] [blame]
/*
* 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);
}
}