/**
* Copyright © 2006-2016 Web Cohesion (info@webcohesion.com)
*
* 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 com.webcohesion.enunciate.javac.decorations.type;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import java.util.ArrayList;
import java.util.List;
/**
* @author Ryan Heaton
*/
public class TypeVariableContext {
private final TypeVariableContext stack;
private final List<? extends TypeParameterElement> params;
private final List<? extends TypeMirror> variables;
public TypeVariableContext() {
this(null, new ArrayList<TypeParameterElement>(), new ArrayList<TypeMirror>());
}
private TypeVariableContext(TypeVariableContext stack, List<? extends TypeParameterElement> params, List<? extends TypeMirror> variables) {
this.stack = stack;
this.params = params;
this.variables = variables;
}
public TypeVariableContext push(List<? extends TypeParameterElement> params, List<? extends TypeMirror> variables) {
return new TypeVariableContext(this, params, variables);
}
public TypeMirror resolveTypeVariables(TypeMirror var, ProcessingEnvironment env) {
TypeMirror resolved = resolveTypeVariable(var);
if (resolved.getKind() == TypeKind.DECLARED) {
//if we resolved to a declared type, we need to resolve the type arguments, too.
List<? extends TypeMirror> args = ((DeclaredType) resolved).getTypeArguments();
TypeMirror[] resolvedArgs = new TypeMirror[args.size()];
for (int i = 0; i < args.size(); i++) {
resolvedArgs[i] = resolveTypeVariables(args.get(i), env);
}
resolved = env.getTypeUtils().getDeclaredType((TypeElement) ((DeclaredType) resolved).asElement(), resolvedArgs);
}
return resolved;
}
public TypeMirror resolveTypeVariable(TypeMirror typeVariable) {
if (typeVariable.getKind() == TypeKind.TYPEVAR) {
int argIndex = -1;
Name name = ((TypeVariable) typeVariable).asElement().getSimpleName();
for (int i = 0; i < this.params.size(); i++) {
TypeParameterElement elementParam = this.params.get(i);
if (elementParam.getSimpleName().equals(name)) {
argIndex = i;
break;
}
}
if (argIndex < 0 || this.variables.size() != this.params.size()) {
//best we can do is get the upper bound. should this maybe be an illegal state?
typeVariable = ((TypeVariable) typeVariable).getUpperBound();
}
else {
typeVariable = this.variables.get(argIndex);
}
}
if (typeVariable.getKind() == TypeKind.TYPEVAR) {
if (this.stack == null) {
//end of the stack; return the upper bound, I guess..
typeVariable = ((TypeVariable) typeVariable).getUpperBound();
}
else {
typeVariable = this.stack.resolveTypeVariable(typeVariable);
}
}
return typeVariable;
}
}