/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed 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.jboss.errai.ioc.rebind.ioc.graph.impl;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import org.jboss.errai.codegen.meta.HasAnnotations;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.ioc.rebind.ioc.graph.api.DependencyGraphBuilder.Dependency;
import org.jboss.errai.ioc.rebind.ioc.graph.api.DependencyGraphBuilder.DependencyType;
import org.jboss.errai.ioc.rebind.ioc.graph.api.DependencyGraphBuilder.InjectableType;
import org.jboss.errai.ioc.rebind.ioc.graph.api.DependencyGraphBuilder.ProducerInstanceDependency;
import org.jboss.errai.ioc.rebind.ioc.graph.api.Injectable;
import org.jboss.errai.ioc.rebind.ioc.graph.api.Qualifier;
import org.jboss.errai.ioc.rebind.ioc.injector.api.WiringElementType;
/**
* Concrete here does not mean "for a concrete class". Rather, it means that
* this injectable was for bean that we know how to produce (either because it
* is a scoped type or from producer member). In contrast,
* {@link InjectableReference abstract injectables} are used in unresolved
* dependencies to represent a injectable that we do not yet know how to
* construct.
*
* When the {@link DependencyGraphImpl} is constructed, resolution has occurred
* and it will contain only concrete injecables.
*
* @author Max Barkley <mbarkley@redhat.com>
*/
class InjectableImpl extends InjectableBase implements Injectable {
final InjectableType injectableType;
final Collection<WiringElementType> wiringTypes;
final List<BaseDependency> dependencies = new ArrayList<>();
final Class<? extends Annotation> literalScope;
Boolean proxiable = null;
boolean requiresProxy = false;
Integer hashContent = null;
final String factoryName;
final Predicate<List<InjectableHandle>> pathPredicate;
InjectableImpl(final MetaClass type,
final Qualifier qualifier,
final Predicate<List<InjectableHandle>> pathPredicate,
final String factoryName,
final Class<? extends Annotation> literalScope,
final InjectableType injectorType,
final Collection<WiringElementType> wiringTypes) {
super(type, qualifier);
this.pathPredicate = pathPredicate;
this.factoryName = factoryName;
this.literalScope = literalScope;
this.wiringTypes = wiringTypes;
this.injectableType = injectorType;
}
@Override
public String getFactoryName() {
return factoryName;
}
@Override
public Class<? extends Annotation> getScope() {
return literalScope;
}
@Override
public InjectableType getInjectableType() {
return injectableType;
}
@Override
public Collection<Dependency> getDependencies() {
return Collections.<Dependency>unmodifiableCollection(dependencies);
}
@Override
public boolean loadAsync() {
return wiringTypes.contains(WiringElementType.LoadAsync);
}
@Override
public boolean requiresProxy() {
switch (injectableType) {
case ContextualProvider:
case Provider:
return false;
case Producer:
case Type:
case JsType:
case ExtensionProvided:
return requiresProxy || wiringTypes.contains(WiringElementType.NormalScopedBean);
case Extension:
default:
throw new RuntimeException("Not yet implemented!");
}
}
@Override
public Optional<HasAnnotations> getAnnotatedObject() {
switch (injectableType) {
case Type:
return Optional.of(type);
case Producer:
case Static:
case Provider:
case ContextualProvider:
return dependencies
.stream()
.filter(dep -> DependencyType.ProducerMember.equals(dep.dependencyType))
.map(dep -> (HasAnnotations) ((ProducerInstanceDependency) dep).getProducingMember())
.findFirst();
case ExtensionProvided:
case Extension:
case JsType:
case Disabled:
default:
return Optional.empty();
}
}
@Override
public Collection<WiringElementType> getWiringElementTypes() {
return Collections.unmodifiableCollection(wiringTypes);
}
@Override
public boolean isContextual() {
return InjectableType.ContextualProvider.equals(injectableType);
}
@Override
public void setRequiresProxyTrue() {
requiresProxy = true;
}
@Override
public boolean isExtension() {
return false;
}
@Override
public int hashContent() {
if (hashContent == null) {
hashContent = computeHashContent();
}
return hashContent;
}
private int computeHashContent() {
int hashContent = type.hashContent();
for (final BaseDependency dep: dependencies) {
hashContent ^= dep.injectable.resolution.getInjectedType().hashContent();
}
return hashContent;
}
@Override
public String toString() {
final String injectableDescriptor;
switch (injectableType) {
case ContextualProvider:
injectableDescriptor = "ContextualProvider";
break;
case Disabled:
injectableDescriptor = "Disabled";
break;
case Extension:
injectableDescriptor = "Extension";
break;
case ExtensionProvided:
injectableDescriptor = "ExtensionProvided";
break;
case JsType:
injectableDescriptor = "@JsType";
break;
case Producer:
injectableDescriptor = "@Produces";
break;
case Provider:
injectableDescriptor = "Provider";
break;
case Static:
injectableDescriptor = "@Produces";
break;
case Type:
injectableDescriptor = "Class";
break;
default:
injectableDescriptor = "";
break;
}
return String.format("%s %s %s", injectableDescriptor, getQualifier(), getInjectedType().getFullyQualifiedNameWithTypeParms());
}
}