/**
* 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.util.freemarker;
import com.webcohesion.enunciate.EnunciateContext;
import com.webcohesion.enunciate.metadata.ClientName;
import freemarker.ext.beans.BeansWrapperBuilder;
import freemarker.template.Configuration;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Gets the qualified package name for a package or type.
*
* @author Ryan Heaton
*/
public class ClientPackageForMethod implements TemplateMethodModelEx {
protected final TreeMap<String, String> conversions;
protected final EnunciateContext context;
/**
* @param conversions The conversions.
*/
public ClientPackageForMethod(Map<String, String> conversions, EnunciateContext context) {
this.conversions = new TreeMap<String, String>(new Comparator<String>() {
public int compare(String package1, String package2) {
return package2.length() == package1.length() ? package1.compareTo(package2) : package2.length() - package1.length();
}
});
if (conversions != null) {
this.conversions.putAll(conversions);
}
this.context = context;
}
/**
* Gets the client-side package for the type, type declaration, package, or their string values.
*
* @param list The arguments.
* @return The string value of the client-side package.
*/
public Object exec(List list) throws TemplateModelException {
if (list.size() < 1) {
throw new TemplateModelException("The convertPackage method must have the class or package as a parameter.");
}
Object unwrapped = unwrap(list.get(0));
return convertUnwrappedObject(unwrapped);
}
protected Object unwrap(Object wrapped) throws TemplateModelException {
return wrapped instanceof TemplateModel ? new BeansWrapperBuilder(Configuration.getVersion()).build().unwrap((TemplateModel) wrapped) : wrapped;
}
/**
* Converts an unwrapped object.
*
* @param unwrapped The unwrapped object to convert.
* @return The conversion.
*/
public String convertUnwrappedObject(Object unwrapped) throws TemplateModelException {
String conversion;
if (unwrapped instanceof TypeMirror) {
conversion = convert((TypeMirror) unwrapped);
}
else if (unwrapped instanceof TypeElement) {
conversion = convert((TypeElement) unwrapped);
}
else if (unwrapped instanceof PackageElement) {
conversion = convert((PackageElement) unwrapped);
}
else {
conversion = convert(String.valueOf(unwrapped));
}
return conversion;
}
/**
* Returns the client-side package value for the given type.
*
* @param typeMirror The type.
* @return The client-side package value for the type.
* @throws TemplateModelException If the type mirror cannot be converted for some reason.
*/
public String convert(TypeMirror typeMirror) throws TemplateModelException {
String conversion;
if (typeMirror instanceof DeclaredType) {
conversion = convert((TypeElement) ((DeclaredType) typeMirror).asElement());
}
else if (typeMirror instanceof ArrayType) {
conversion = convert(((ArrayType) typeMirror).getComponentType());
}
else if (typeMirror instanceof TypeVariable) {
conversion = "Object";
VariableElement parameterDeclaration = (VariableElement) ((TypeVariable) typeMirror).asElement();
if (parameterDeclaration != null && ((TypeVariable) typeMirror).getUpperBound() != null) {
conversion = convert(((TypeVariable) typeMirror).getUpperBound());
}
}
else {
conversion = String.valueOf(typeMirror);
}
return conversion;
}
/**
* Returns the client-side package value for the given type declaration.
*
* @param declaration The declaration.
* @return The client-side package value for the declaration.
*/
public String convert(TypeElement declaration) throws TemplateModelException {
return convert(this.context.getProcessingEnvironment().getElementUtils().getPackageOf(declaration));
}
/**
* Converts the package declaration to its client-side package value.
*
* @param packageDeclaration The package declaration.
* @return The package declaration.
*/
public String convert(PackageElement packageDeclaration) {
if (packageDeclaration == null) {
return "";
}
ClientName specifiedName = packageDeclaration.getAnnotation(ClientName.class);
return specifiedName == null ? convert(packageDeclaration.getQualifiedName().toString()) : specifiedName.value();
}
/**
* Converts the possible package to the specified client-side package, if any conversions are specified.
*
* @param fqn The package to convert.
* @return The converted package, or the original if no conversions were specified for this value.
*/
public String convert(String fqn) {
//todo: support for regular expressions or wildcards?
if (this.conversions.containsKey(fqn)) {
return this.conversions.get(fqn);
}
for (String pkg : this.conversions.keySet()) {
if (fqn.startsWith(pkg)) {
String conversion = conversions.get(pkg);
return conversion + fqn.substring(pkg.length());
}
}
return fqn;
}
}