/*
* Copyright 2008-2011 the original author or authors.
*
* 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.nominanuda.hyperapi;
import static com.nominanuda.zen.common.Str.STR;
import static com.nominanuda.zen.seq.Seq.SEQ;
import java.io.IOException;
import java.io.Writer;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.Completion;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import com.nominanuda.zen.common.Check;
public class JQueryAptProcessor implements Processor {
private ProcessingEnvironment env;
String prologue1 = //"(function() {var dummy = function(){};var uritpl = function(spec, model) {var pathAndQuery = spec.split('?');var re = /\\{[^\\}]+\\}/g;var result = pathAndQuery[0].replace(re,function(str, p1, p2, offset, s) {return model[str.replace(/\\{(\\w+).*/, '$1')];});if(pathAndQuery.length > 1) {result += '?' + pathAndQuery[1].replace(re,function(str, offset) {var x = str.replace(/\\{(\\w+).*/, '$1');var p = model[x];var isPname = offset === 0 || pathAndQuery[1].charAt(offset - 1) ==! '=';return ''+ (isPname ? x+'='+p : p);});};return result;};";
"(function() {\n"+
"var dummy = function(){};\n"+
"var uritpl = function(spec, model) {\n"+
" var globalObj = window.";
String prologue2 = ";\n" +
" var uriPrefix = globalObj.globalPrefix || '';\n" +
" var pathAndQuery = spec.split('?');\n"+
" var re = /\\(?\\{[^\\}]+\\}\\)?/g;\n"+
" var result = pathAndQuery[0].replace(re,function(str, p1, p2, offset, s) {\n"+
" return model[str.replace(/\\{(\\w+).*/, '$1')];\n"+
" });\n"+
" if(pathAndQuery.length > 1) {\n"+
" result += '?' + pathAndQuery[1].replace(re,function(str, offset) {\n"+
" var x = str.replace(/\\(?\\{(\\w+).*/, '$1');\n"+
" var p = model[x];\n"+
" if(!((p === null||typeof p === 'undefined') && pathAndQuery[1].charAt(offset)==='(')){\n"+
" var isPname = offset === 0 || pathAndQuery[1].charAt(offset - 1) ==! '=';\n"+
" return ''+ (isPname ? x+'='+p : p);\n"+
" }}).replace(/\\s/g,'&');\n"+
" }\n"+
" return uriPrefix+result;\n"+
"};\n";
String epilogue = "})();";
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
log("process");
for(Element el : roundEnv.getElementsAnnotatedWith(HyperApi.class)) {
processApi((TypeElement)el, roundEnv);
//log(el.getSimpleName().toString());
}
return true;
}
// JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// StandardJavaFileManager fileManager = compiler.
// getStandardFileManager(null, null, null);
//window.hyperapi_<type> = {
// <method> : function(x,y,z,okcb,errcb) {
// var uri = uritpl('<tpl>',{x:x,y:y...});//TODO nested...
// var _errcb = errcb || dummy;
////TODO uri PREFIX
// jQuery.ajax({
// url:uri,
//
// });
// },
//}
private void processApi(TypeElement el, RoundEnvironment roundEnv) {
try {
String targetType = el.getQualifiedName().toString();
log("targetType:"+targetType);
String pkg = targetType.substring(0, targetType.lastIndexOf('.'));
String ns = "hyperapi_"+targetType.replace('.', '_');
log("detected package:"+pkg);
FileObject jsSrc = env.getFiler().createResource(StandardLocation.CLASS_OUTPUT, pkg, ns+".js", el);
Writer w = jsSrc.openWriter();
w.write(prologue1);
w.write(ns);
w.write(prologue2);
w.write("window."+ns+" = {");
for(Element member : el.getEnclosedElements()) {
if(member.getKind() == ElementKind.METHOD) {
log(member.getSimpleName().toString());
writeMethodCall((ExecutableElement)member, w, ns);
}
}
w.write("};\n");
w.write(epilogue);
w.close();
log("generating:"+jsSrc.toUri().toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void writeMethodCall(ExecutableElement member, Writer w, String ns) throws IOException {
List<? extends VariableElement> params = member.getParameters();
Path uriTpl = member.getAnnotation(Path.class);
if(uriTpl == null) {
log("skipping "+member.getSimpleName()+" because misses @Path annotation");
return;
}
String method = member.getAnnotation(GET.class) != null
? "GET" : member.getAnnotation(POST.class) != null
? "POST" : member.getAnnotation(PUT.class) != null
? "PUT" : member.getAnnotation(DELETE.class) != null
? "DELETE" : (String)Check.illegalstate.fail();
List<String> pNames = new LinkedList<String>();
StringBuilder uriTplParams = new StringBuilder("{");
String jsonBody = null;
for(VariableElement p: params) {
String jsonPath = null;
PathParam pp = p.getAnnotation(PathParam.class);
if(pp != null) {
jsonPath = pp.value();
} else {
QueryParam qp = p.getAnnotation(QueryParam.class);
if(qp != null) {
jsonPath = qp.value();
}
}
@SuppressWarnings("unused")
String clsType = null;//p.asType().getClass().getName();
TypeMirror tm = p.asType();
if(tm instanceof DeclaredType) {
clsType = ((DeclaredType)tm).asElement().getSimpleName().toString();
} else if(tm instanceof PrimitiveType) {
clsType = "PRIMITIVE";//((PrimitiveType)tm).asElement().getSimpleName().toString();
} else {
Check.illegalstate.fail();
}
String pname = p.getSimpleName().toString();
pNames.add(pname);
if(jsonPath != null) {
//TODO a.b.c
uriTplParams.append(jsonPath+":"+pname+",");
} else {
jsonBody = pname;
}
}
uriTplParams.append("}");
String sig = STR.join(", ", pNames);
if(pNames.size() > 0) {
sig = sig + ", ";
}
String content = "";
if(jsonBody != null && ("POST".equals(method) || "PUT".equals(method))) {
content = ",contentType:'application/json;charset=UTF-8',data:JSON.stringify("+jsonBody+")";
}
w.write("\n"+member.getSimpleName().toString()+" : function("+sig+"okcb,errcb) {\n" +
"var uri = uritpl('"+uriTpl.value()+"',"+uriTplParams.toString()+");\n" +
"var _errcb = errcb || dummy;\n" +
"jQuery.ajax({type:'"+method+"',\n url:uri,\n success:okcb,\n error:_errcb" + content +
"\n});\n},");
}
public void init(ProcessingEnvironment processingEnv) {
log("init");
env = processingEnv;
}
public Set<String> getSupportedOptions() {
log("getSupportedOtions");
return SEQ.hashSet();
}
public Set<String> getSupportedAnnotationTypes() {
log("getSupportedAnnotationTypes");
return SEQ.hashSet(HyperApi.class.getName());
}
public SourceVersion getSupportedSourceVersion() {
log("getSupportedSourceVersion");
return SourceVersion.RELEASE_6;
}
public Iterable<? extends Completion> getCompletions(Element element,
AnnotationMirror annotation, ExecutableElement member,
String userText) {
log("getCompletions");
return SEQ.hashSet();
}
private void log(String msg) {
System.err.println(msg);
//System.out.println(msg);
}
}
//(function() {
//var dummy = function(){};
//var uritpl = function(spec, model) {
// var pathAndQuery = spec.split('?');
// var re = /\{[^\}]+\}/g;
// var result = pathAndQuery[0].replace(re,function(str, p1, p2, offset, s) {
// return model[str.replace(/\{(\w+).*/, '$1')];
// });
// if(pathAndQuery.length > 1) {
// result += '?' + pathAndQuery[1].replace(re,function(str, offset) {
// var x = str.replace(/\{(\w+).*/, '$1');
// var p = model[x];
// var isPname = offset === 0 || pathAndQuery[1].charAt(offset - 1) ==! '=';
// return ''+ (isPname ? x+'='+p : p);
// });
// }
// return result;
//};
//window.hyperapi_<type> = {
// <method> : function(x,y,z,okcb,errcb) {
// var uri = uritpl('<tpl>',{x:x,y:y...});//TODO nested...
// var _errcb = errcb || dummy;
////TODO uri PREFIX
// jQuery.ajax({
// url:uri,
//
// });
// },
//}
//})();