/*
* Copyright (C) 2015 Google, Inc.
*
* 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.google.callbuilder;
import com.google.callbuilder.Unification.Atom;
import com.google.callbuilder.Unification.Sequence;
import com.google.callbuilder.Unification.Unifiable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
/**
* Maintains a mapping of elements that must be resolved ({@link TypeElement}s and
* {@link TypeParameterElements}) to objects the {@link Unification} class works with. Can also
* convert the result of a successful unification into the type string that the variable expresses.
*/
final class AtomAndVarRegistry {
/**
* Mapping from Atom objects to the string representation of the item in code to which they
* correspond.
*/
private final Map<Atom, String> atomsToCode = new HashMap<>();
/**
* The inverse of {@link #atomsToCode}.
*/
private final Map<String, Atom> codeToAtoms = new HashMap<>();
String toType(Unifiable resolution) {
if (resolution instanceof Atom) {
return atomsToCode.get(resolution);
}
Sequence sequence = (Sequence) resolution;
StringBuilder typeReference = new StringBuilder()
.append(toType(sequence.items().get(0)));
if (sequence.items().size() > 1) {
String prefix = "<";
for (Unifiable unifiable : sequence.items().subList(1, sequence.items().size())) {
typeReference.append(prefix);
prefix = ", ";
typeReference.append(toType(unifiable));
}
typeReference.append(">");
}
return typeReference.toString();
}
private Atom atom(String codeRepresentation) {
Atom atom = codeToAtoms.get(codeRepresentation);
if (atom == null) {
atom = new Atom();
atomsToCode.put(atom, codeRepresentation);
codeToAtoms.put(codeRepresentation, atom);
}
return atom;
}
Unifiable encode(TypeMirror type, Map<String, ? extends Unifiable> overridenTypeVariables) {
switch (type.getKind()) {
case DECLARED:
List<Unifiable> types = new ArrayList<>();
DeclaredType declaredType = (DeclaredType) type;
types.add(atom(CallBuilderProcessor.qualifiedName(declaredType)));
for (TypeMirror typeArgument : declaredType.getTypeArguments()) {
types.add(encode(typeArgument, overridenTypeVariables));
}
return new Sequence(types);
case ARRAY:
case BOOLEAN:
case BYTE:
case CHAR:
case DOUBLE:
case FLOAT:
case INT:
case LONG:
case SHORT:
case TYPEVAR:
String stringRep = type.toString();
if (overridenTypeVariables.containsKey(stringRep)) {
return overridenTypeVariables.get(stringRep);
}
return atom(stringRep);
default:
throw new RuntimeException("type is not supported for use in CallBuilder: " + type);
}
}
}