org.rapidpm.ddi.implresolver.ImplementingClassResolver Maven / Gradle / Ivy
/*
* 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.rapidpm.ddi.implresolver;
import org.jetbrains.annotations.Nullable;
import org.rapidpm.ddi.DDIModelException;
import org.rapidpm.ddi.DI;
import org.rapidpm.ddi.ResponsibleFor;
import org.rapidpm.ddi.producer.ProducerLocator;
import org.rapidpm.proxybuilder.objectadapter.annotations.staticobjectadapter.IsStaticObjectAdapter;
import org.rapidpm.proxybuilder.staticgenerated.annotations.IsGeneratedProxy;
import org.rapidpm.proxybuilder.staticgenerated.annotations.IsMetricsProxy;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* one subtype - will return this class
* n subtypes - will search for classresolver to decide what will be the right implementation
*
* no subtype - will return the interface itself, maybe on the fly implementations are available
*
* Created by svenruppert on 23.07.15.
*/
//public class ImplementingClassResolver implements ClassResolver {
public class ImplementingClassResolver {
private final Map resolverCache = new ConcurrentHashMap<>();
public void clearCache() {
resolverCache.clear();
}
public Class resolve(Class interf) {
if (!resolverCache.containsKey(interf)) {
resolverCache.put(interf, resolveNewForClass(interf));
}
return resolverCache.get(interf);
// return resolveNewForClass(interf);
}
private Class resolveNewForClass(final Class interf) {
if (interf.isInterface()) {
final Set> subTypesOf = DI.getSubTypesOf(interf);
//remove subtypes that are interfaces
removeInterfacesFromSubTypes(subTypesOf);
if (subTypesOf.isEmpty()) return interf;
else if (subTypesOf.size() == 1) {
final Class implClass = handleOneSubType(interf, subTypesOf.toArray()[0]);
if (implClass != null) return implClass;
} else {
final Class clearedListOfResolvers = handleManySubTypes(interf, subTypesOf);
if (clearedListOfResolvers != null) return clearedListOfResolvers;
}
} else {
return interf;
}
throw new DDIModelException("this point should never been reached.. no decission possible for = " + interf);
}
private void removeInterfacesFromSubTypes(final Set> subTypesOf) {
final Iterator> iteratorOfSubTypes = subTypesOf.iterator();
while (iteratorOfSubTypes.hasNext()) {
final Class next = iteratorOfSubTypes.next();
if (next.isInterface()
|| next.isAnnotationPresent(IsStaticObjectAdapter.class)
|| next.isAnnotationPresent(IsMetricsProxy.class)
|| next.isAnnotationPresent(IsGeneratedProxy.class)
) iteratorOfSubTypes.remove();
}
}
@Nullable
private Class handleOneSubType(final Class interf, final Object o) {
final Class implClass = (Class) o;
final Set> producersForInterface = new ProducerLocator().findProducersFor(interf);
final Set> producersForImpl = new ProducerLocator().findProducersFor(implClass);
//@formatter:off
if (!producersForInterface.isEmpty() && !producersForImpl.isEmpty()) return interf;
if (producersForInterface.isEmpty() && producersForImpl.isEmpty()) return implClass;
if (producersForImpl.isEmpty()) return interf;
if (producersForInterface.isEmpty()) return implClass;
//@formatter:on
return null;
}
@Nullable
private Class handleManySubTypes(final Class interf, final Set> subTypesOf) {
final Set> subTypesOfClassResolver = DI.getSubTypesOf(ClassResolver.class);
final List clearedListOfResolvers = subTypesOfClassResolver
.stream()
.filter(aClassResolver -> aClassResolver.isAnnotationPresent(ResponsibleFor.class))
.filter(aClassResolver -> {
final ResponsibleFor responsibleFor = aClassResolver.getAnnotation(ResponsibleFor.class);
return interf.equals(responsibleFor.value());
})
.collect(Collectors.toList());
if (clearedListOfResolvers.size() == 1) {
final Class classResolver = handleOneResolver(interf, clearedListOfResolvers);
if (classResolver != null) return classResolver;
} else if (clearedListOfResolvers.isEmpty()) {
return handleNoResolvers(interf, subTypesOf);
} else {
return handleToManyResolvers(interf, clearedListOfResolvers);
}
return null;
}
@Nullable
// private Class handleOneResolver(final Class interf, final List>> clearedListOfResolvers) {
private Class handleOneResolver(final Class interf, final List clearedListOfResolvers) {
final Class> resolver = clearedListOfResolvers.get(0);
try {
final ClassResolver classResolver = resolver.newInstance();
return classResolver.resolve(interf);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
throw new DDIModelException(interf + " -- " + e);
}
}
private Class handleNoResolvers(final Class interf, final Set> subTypesOf) {
final Set> producersForInterface = new ProducerLocator().findProducersFor(interf);
if (producersForInterface.isEmpty()) {
final StringBuilder stringBuilder = new StringBuilder("interface with multiple implementations and no ClassResolver= " + interf);
final List implList = subTypesOf
.stream()
.map(c -> "impl. : " + c.getName()).collect(Collectors.toList());
stringBuilder.append(implList);
throw new DDIModelException(stringBuilder.toString());
}
if (producersForInterface.size() == 1) return interf;
final StringBuilder stringBuilder = new StringBuilder("interface with multiple implementations and no ClassResolver and n Producers f the interface = " + interf);
final List implList = subTypesOf.stream().map(c -> "impl. : " + c.getName()).collect(Collectors.toList());
final List prodList = producersForInterface.stream().map(c -> "producer. : " + c.getName()).collect(Collectors.toList());
stringBuilder.append(implList).append(prodList);
throw new DDIModelException(stringBuilder.toString());
}
private Class handleToManyResolvers(final Class interf, final List clearedListOfResolvers) {
final String message = "interface with multiple implementations and more as 1 ClassResolver = "
+ interf
+ " ClassResolver: " + clearedListOfResolvers;
throw new DDIModelException(message);
}
}