blob: b8b8a0bc6b2fff3581db5269af1514b52867da3b [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.apache.commons.weaver.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import org.apache.commons.lang3.Validate;
import org.apache.commons.weaver.WeaveProcessor;
import org.apache.commons.weaver.spi.Weaver;
/**
* <p>Encapsulates the result of scanning based on a {@link ScanRequest}. The
* scan results are available in a structure corresponding to the Java class
* hierarchy; i.e.:
* </p>
* <pre>
* package
* |_class
* |_field
* |_method
* | |_method parameter
* |_constructor
* |_constructor parameter
* </pre>
* <p>
* The tree of results can be iterated in this manner using
* {@link #getPackages()}. However, if a given {@link Weaver} is known not to
* handle packages but some other element, convenience methods are provided
* here giving direct access to the various elements that may have been
* discovered.
* </p>
*/
public class ScanResult {
private abstract static class Projection<PARENT, CHILD extends AnnotatedElement> implements
AnnotatedElements<CHILD> {
private final Iterable<PARENT> parents;
Projection(final Iterable<PARENT> parents) {
super();
this.parents = parents;
}
protected abstract Iterable<CHILD> childrenOf(PARENT parent);
@Override
public Iterator<CHILD> iterator() {
final Iterator<PARENT> parentIterator = parents.iterator();
return new Iterator<CHILD>() {
private Iterator<CHILD> children = nextChildren();
@Override
public synchronized boolean hasNext() {
return children != null;
}
@Override
public synchronized CHILD next() {
if (children == null) {
throw new NoSuchElementException();
}
try {
return children.next();
} finally {
if (!children.hasNext()) {
children = nextChildren();
}
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private Iterator<CHILD> nextChildren() {
while (parentIterator.hasNext()) {
final Iterator<CHILD> prospect = childrenOf(parentIterator.next()).iterator();
if (prospect.hasNext()) {
return prospect;
}
}
return null;
}
};
}
@Override
public AnnotatedElements<CHILD> with(final Class<? extends Annotation> annotationType) {
return new AnnotatedWith<>(this, annotationType);
}
}
private static class AnnotatedWith<W extends AnnotatedElement> implements AnnotatedElements<W> {
final Iterable<W> wrapped;
final Class<? extends Annotation> annotationType;
AnnotatedWith(final Iterable<W> wrapped, final Class<? extends Annotation> annotationType) {
super();
this.wrapped = wrapped;
this.annotationType = annotationType;
}
@Override
public Iterator<W> iterator() {
final Iterator<W> iter = wrapped.iterator();
return new Iterator<W>() {
W next = read();
private W read() {
while (iter.hasNext()) {
final W element = iter.next();
if (element.isAnnotationPresent(annotationType)) {
return element;
}
}
return null;
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public W next() {
if (next == null) {
throw new NoSuchElementException();
}
try {
return next;
} finally {
next = read();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public AnnotatedElements<W> with(final Class<? extends Annotation> annotationType) {
return new AnnotatedWith<>(this, annotationType);
}
}
/**
* Weavable packages by name.
*/
final ConcurrentNavigableMap<String, WeavablePackage> packages = new ConcurrentSkipListMap<>();
/**
* Public for use by {@link WeaveProcessor}.
* @param pkg to wrap
* @return {@link WeavablePackage}
*/
public WeavablePackage getWeavable(final Package pkg) {
final String key =
Optional.ofNullable(pkg).map(Package::getName).orElse("");
if (packages.containsKey(key)) {
return packages.get(key);
}
final WeavablePackage result = new WeavablePackage(pkg);
final WeavablePackage faster = packages.putIfAbsent(key, result);
return faster == null ? result : faster;
}
/**
* Public for use by {@link WeaveProcessor}.
* @param cls to wrap
* @param <T> type
* @return {@link WeavableClass}
*/
public <T> WeavableClass<T> getWeavable(final Class<T> cls) {
return getWeavable(cls.getPackage()).getWeavable(cls);
}
/**
* Public for use by {@link WeaveProcessor}.
* @param fld to wrap
* @return {@link WeavableField}
*/
public WeavableField<?> getWeavable(final Field fld) {
return getWeavable(fld.getDeclaringClass()).getWeavable(fld);
}
/**
* Public for use by {@link WeaveProcessor}.
* @param methd to wrap
* @return {@link WeavableMethod}
*/
public WeavableMethod<?> getWeavable(final Method methd) {
return getWeavable(methd.getDeclaringClass()).getWeavable(methd);
}
/**
* Public for use by {@link WeaveProcessor}.
* @param ctor to wrap
* @param <T> type
* @return {@link WeavableConstructor}
*/
public <T> WeavableConstructor<T> getWeavable(final Constructor<T> ctor) {
return getWeavable(ctor.getDeclaringClass()).getWeavable(ctor);
}
/**
* Iterate or filter {@link WeavablePackage}s.
* @return {@link AnnotatedElements}
*/
public AnnotatedElements<WeavablePackage> getPackages() {
return new AnnotatedElements<WeavablePackage>() {
@Override
public Iterator<WeavablePackage> iterator() {
return packages.values().iterator();
}
@Override
public AnnotatedElements<WeavablePackage> with(final Class<? extends Annotation> annotationType) {
return new AnnotatedWith<>(packages.values(), annotationType);
}
};
}
/**
* Iterate or filter {@link WeavableClass}es.
* @return {@link AnnotatedElements}
*/
public AnnotatedElements<WeavableClass<?>> getClasses() {
return new Projection<WeavablePackage, WeavableClass<?>>(getPackages()) {
@Override
protected Iterable<WeavableClass<?>> childrenOf(final WeavablePackage parent) {
return parent.getClasses();
}
};
}
/**
* Iterate or filter {@link WeavableClass}es assignable to {@code supertype}.
* @param supertype {@link Class} whose subtypes are sought
* @return {@link AnnotatedElements}
*/
public AnnotatedElements<WeavableClass<?>> getClassesAssignableTo(final Class<?> supertype) {
Validate.notNull(supertype, "supertype");
return new Projection<WeavablePackage, WeavableClass<?>>(getPackages()) {
@Override
protected Iterable<WeavableClass<?>> childrenOf(final WeavablePackage parent) {
return parent.getClasses();
}
@Override
public Iterator<WeavableClass<?>> iterator() {
final Iterator<WeavableClass<?>> toWrap = super.iterator();
return new Iterator<WeavableClass<?>>() {
{
read();
}
private WeavableClass<?> next;
private void read() {
while (toWrap.hasNext()) {
final WeavableClass<?> test = toWrap.next();
if (supertype.isAssignableFrom(test.getTarget())) {
next = test;
return;
}
}
next = null;
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public WeavableClass<?> next() {
try {
return next;
} finally {
read();
}
}
@Override
public void remove() {
toWrap.remove();
}
};
}
};
}
/**
* Iterate or filter {@link WeavableField}s.
* @return {@link AnnotatedElements}
*/
public AnnotatedElements<WeavableField<?>> getFields() {
return new Projection<WeavableClass<?>, WeavableField<?>>(getClasses()) {
@Override
protected Iterable<WeavableField<?>> childrenOf(final WeavableClass<?> parent) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Iterable<WeavableField<?>> result = ((WeavableClass) parent).getFields();
return result;
}
};
}
/**
* Iterate or filter {@link WeavableConstructor}s.
* @return {@link AnnotatedElements}
*/
public AnnotatedElements<WeavableConstructor<?>> getConstructors() {
return new Projection<WeavableClass<?>, WeavableConstructor<?>>(getClasses()) {
@Override
protected Iterable<WeavableConstructor<?>> childrenOf(final WeavableClass<?> parent) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Iterable<WeavableConstructor<?>> result = ((WeavableClass) parent).getConstructors();
return result;
}
};
}
/**
* Iterate or filter {@link WeavableMethod}s.
* @return {@link AnnotatedElements}
*/
public AnnotatedElements<WeavableMethod<?>> getMethods() {
return new Projection<WeavableClass<?>, WeavableMethod<?>>(getClasses()) {
@Override
protected Iterable<WeavableMethod<?>> childrenOf(final WeavableClass<?> parent) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Iterable<WeavableMethod<?>> result = ((WeavableClass) parent).getMethods();
return result;
}
};
}
/**
* Iterate or filter {@link WeavableMethodParameter}s.
* @return {@link AnnotatedElements}
*/
public AnnotatedElements<WeavableMethodParameter<?>> getMethodParameters() {
return new Projection<WeavableMethod<?>, WeavableMethodParameter<?>>(getMethods()) {
@Override
protected Iterable<WeavableMethodParameter<?>> childrenOf(final WeavableMethod<?> parent) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Iterable<WeavableMethodParameter<?>> result = ((WeavableMethod) parent).getParameters();
return result;
}
};
}
/**
* Iterate or filter {@link WeavableConstructorParameter}s.
* @return {@link AnnotatedElements}
*/
public AnnotatedElements<WeavableConstructorParameter<?>> getConstructorParameters() {
return new Projection<WeavableConstructor<?>, WeavableConstructorParameter<?>>(getConstructors()) {
@Override
protected Iterable<WeavableConstructorParameter<?>> childrenOf(final WeavableConstructor<?> parent) {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Iterable<WeavableConstructorParameter<?>> result = ((WeavableConstructor) parent).getParameters();
return result;
}
};
}
}