PageRenderTime 83ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/annotation-processor/src/main/java/com/google/code/facebookapi/apt/FacebookReturnTypeProcessor5.java

http://facebook-java-api.googlecode.com/
Java | 372 lines | 295 code | 65 blank | 12 comment | 50 complexity | ce31981da21a0bc0daf303943383e91d MD5 | raw file
  1. package com.google.code.facebookapi.apt;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import java.util.Collection;
  5. import java.util.Map;
  6. import com.sun.mirror.apt.AnnotationProcessor;
  7. import com.sun.mirror.apt.AnnotationProcessorEnvironment;
  8. import com.sun.mirror.declaration.AnnotationMirror;
  9. import com.sun.mirror.declaration.AnnotationTypeDeclaration;
  10. import com.sun.mirror.declaration.AnnotationTypeElementDeclaration;
  11. import com.sun.mirror.declaration.AnnotationValue;
  12. import com.sun.mirror.declaration.ClassDeclaration;
  13. import com.sun.mirror.declaration.ConstructorDeclaration;
  14. import com.sun.mirror.declaration.Declaration;
  15. import com.sun.mirror.declaration.MethodDeclaration;
  16. import com.sun.mirror.declaration.Modifier;
  17. import com.sun.mirror.declaration.ParameterDeclaration;
  18. import com.sun.mirror.type.ReferenceType;
  19. import com.sun.mirror.type.TypeMirror;
  20. import com.sun.mirror.util.SimpleDeclarationVisitor;
  21. @SuppressWarnings("restriction")
  22. public class FacebookReturnTypeProcessor5 implements AnnotationProcessor {
  23. private AnnotationProcessorEnvironment processingEnv;
  24. public FacebookReturnTypeProcessor5( AnnotationProcessorEnvironment processingEnv ) {
  25. this.processingEnv = processingEnv;
  26. }
  27. public static class CopyConstructorVisitor extends SimpleDeclarationVisitor {
  28. private String clientType;
  29. private PrintWriter out;
  30. public CopyConstructorVisitor( String clientType, PrintWriter out ) {
  31. this.clientType = clientType;
  32. this.out = out;
  33. }
  34. @Override
  35. public void visitConstructorDeclaration( ConstructorDeclaration e ) {
  36. out.print( " " );
  37. out.print( modifiers( e ) );
  38. out.print( " " );
  39. out.print( "Facebook" );
  40. out.print( clientType );
  41. out.print( "RestClient" );
  42. out.print( "( " );
  43. out.print( parametersIncludingTypes( e ) );
  44. out.print( " ) " );
  45. out.print( throwClause( e ) );
  46. out.println( " {" );
  47. out.print( " super( " );
  48. out.print( parametersExcludingTypes( e ) );
  49. out.println( " );" );
  50. out.println( " }" );
  51. out.println();
  52. }
  53. }
  54. private static CharSequence modifiers( ConstructorDeclaration e ) {
  55. StringBuilder modifiers = new StringBuilder();
  56. Collection<Modifier> modifierSet = e.getModifiers();
  57. boolean isFirstModifier = true;
  58. for ( Modifier m : modifierSet ) {
  59. if ( !isFirstModifier ) {
  60. modifiers.append( " " );
  61. }
  62. modifiers.append( m.toString() );
  63. }
  64. return modifiers;
  65. }
  66. private static CharSequence throwClause( ConstructorDeclaration e ) {
  67. StringBuilder throwClause = new StringBuilder();
  68. Collection<ReferenceType> thrownTypes = e.getThrownTypes();
  69. boolean isFirstThrows = true;
  70. for ( TypeMirror t : thrownTypes ) {
  71. if ( isFirstThrows ) {
  72. throwClause.append( "throws " );
  73. } else {
  74. throwClause.append( ", " );
  75. }
  76. throwClause.append( t.toString() );
  77. }
  78. return throwClause;
  79. }
  80. private static CharSequence throwClause( MethodDeclaration e ) {
  81. StringBuilder throwClause = new StringBuilder();
  82. Collection<ReferenceType> thrownTypes = e.getThrownTypes();
  83. boolean isFirstThrows = true;
  84. for ( TypeMirror t : thrownTypes ) {
  85. if ( isFirstThrows ) {
  86. throwClause.append( "throws " );
  87. } else {
  88. throwClause.append( ", " );
  89. }
  90. throwClause.append( t.toString() );
  91. }
  92. return throwClause;
  93. }
  94. private static CharSequence parametersIncludingTypes( ConstructorDeclaration e ) {
  95. StringBuilder methodCode = new StringBuilder();
  96. boolean isFirstParam = true;
  97. Collection<ParameterDeclaration> parameters = e.getParameters();
  98. for ( ParameterDeclaration param : parameters ) {
  99. if ( !isFirstParam ) {
  100. methodCode.append( ", " );
  101. }
  102. TypeMirror paramType = param.getType();
  103. methodCode.append( paramType.toString() );
  104. methodCode.append( " " );
  105. String paramName = param.toString();
  106. // For some reason, the name is "int myVar" if it's a primative type
  107. // Get rid of the "int" bit.
  108. if ( paramName.contains( " " ) ) {
  109. paramName = paramName.substring( paramName.indexOf( ' ' ) + 1 );
  110. }
  111. methodCode.append( paramName );
  112. isFirstParam = false;
  113. }
  114. return methodCode;
  115. }
  116. private static CharSequence parametersIncludingTypes( MethodDeclaration e ) {
  117. StringBuilder methodCode = new StringBuilder();
  118. boolean isFirstParam = true;
  119. Collection<ParameterDeclaration> parameters = e.getParameters();
  120. for ( ParameterDeclaration param : parameters ) {
  121. if ( !isFirstParam ) {
  122. methodCode.append( ", " );
  123. }
  124. TypeMirror paramType = param.getType();
  125. methodCode.append( paramType.toString() );
  126. methodCode.append( " " );
  127. String paramName = param.toString();
  128. // For some reason, the name is "int myVar" if it's a primative type
  129. // Get rid of the "int" bit.
  130. if ( paramName.contains( " " ) ) {
  131. paramName = paramName.substring( paramName.indexOf( ' ' ) + 1 );
  132. }
  133. methodCode.append( paramName );
  134. isFirstParam = false;
  135. }
  136. return methodCode;
  137. }
  138. private static CharSequence parametersExcludingTypes( ConstructorDeclaration e ) {
  139. StringBuilder paramListCode = new StringBuilder();
  140. boolean isFirstParam = true;
  141. Collection<ParameterDeclaration> parameters = e.getParameters();
  142. for ( ParameterDeclaration param : parameters ) {
  143. if ( !isFirstParam ) {
  144. paramListCode.append( ", " );
  145. }
  146. String paramName = param.toString();
  147. // For some reason, the name is "int myVar" if it's a primative type
  148. // Get rid of the "int" bit.
  149. if ( paramName.contains( " " ) ) {
  150. paramName = paramName.substring( paramName.indexOf( ' ' ) + 1 );
  151. }
  152. paramListCode.append( paramName );
  153. isFirstParam = false;
  154. }
  155. return paramListCode;
  156. }
  157. private static CharSequence parametersExcludingTypes( MethodDeclaration e ) {
  158. StringBuilder paramListCode = new StringBuilder();
  159. boolean isFirstParam = true;
  160. Collection<ParameterDeclaration> parameters = e.getParameters();
  161. for ( ParameterDeclaration param : parameters ) {
  162. if ( !isFirstParam ) {
  163. paramListCode.append( ", " );
  164. }
  165. String paramName = param.toString();
  166. // For some reason, the name is "int myVar" if it's a primative type
  167. // Get rid of the "int" bit.
  168. if ( paramName.contains( " " ) ) {
  169. paramName = paramName.substring( paramName.indexOf( ' ' ) + 1 );
  170. }
  171. paramListCode.append( paramName );
  172. isFirstParam = false;
  173. }
  174. return paramListCode;
  175. }
  176. public void process() {
  177. PrintWriter outJAXB = openClassFile( "Jaxb" );
  178. PrintWriter outJSON = openClassFile( "Json" );
  179. PrintWriter outXML = openClassFile( "Xml" );
  180. if ( outJAXB == null && outJSON == null && outXML == null ) {
  181. return;
  182. }
  183. final AnnotationTypeDeclaration annotationType = (AnnotationTypeDeclaration) processingEnv.getTypeDeclaration( "com.google.code.facebookapi.FacebookReturnType" );
  184. Collection<Declaration> elements = processingEnv.getDeclarationsAnnotatedWith( annotationType );
  185. AnnotationVisitor visitor = new AnnotationVisitor( outJAXB, outJSON, outXML );
  186. for ( Declaration element : elements ) {
  187. element.accept( visitor );
  188. }
  189. closeClassFile( outJAXB );
  190. closeClassFile( outJSON );
  191. closeClassFile( outXML );
  192. }
  193. private PrintWriter openClassFile( String type ) {
  194. final String codepackage = "com.google.code.facebookapi";
  195. final String mainClassName = "Facebook" + type + "RestClient";
  196. final String baseClassName = mainClassName + "Base";
  197. final String mainClass = codepackage + "." + mainClassName;
  198. final String baseClass = codepackage + "." + baseClassName;
  199. try {
  200. PrintWriter out = processingEnv.getFiler().createSourceFile( mainClass );
  201. out.println( String.format( "package %s;", codepackage ) );
  202. out.println();
  203. if ( type.equals( "Jaxb" ) ) {
  204. out.println( "@SuppressWarnings(\"unchecked\")" );
  205. }
  206. out.println( String.format( "public class %s extends %s {", mainClassName, baseClassName ) );
  207. out.println();
  208. CopyConstructorVisitor copyConstructors = new CopyConstructorVisitor( type, out );
  209. ClassDeclaration clientBase = (ClassDeclaration) processingEnv.getTypeDeclaration( baseClass );
  210. for ( ConstructorDeclaration cd : clientBase.getConstructors() ) {
  211. cd.accept( copyConstructors );
  212. }
  213. return out;
  214. }
  215. catch ( IOException ex ) {
  216. System.err.println( "Ignoring IOException during: " + type + "; " + ex );
  217. return null;
  218. }
  219. }
  220. private void closeClassFile( PrintWriter out ) {
  221. if ( out == null ) {
  222. return;
  223. }
  224. out.println( "}" );
  225. out.flush();
  226. out.close();
  227. }
  228. public static class AnnotationVisitor extends SimpleDeclarationVisitor {
  229. private PrintWriter outJAXB;
  230. private PrintWriter outJSON;
  231. private PrintWriter outXML;
  232. public AnnotationVisitor( PrintWriter outJAXB, PrintWriter outJSON, PrintWriter outXML ) {
  233. this.outJAXB = outJAXB;
  234. this.outJSON = outJSON;
  235. this.outXML = outXML;
  236. }
  237. /**
  238. * These two lines are required to ensure that the object tree actually bothers to parse the method parameters. Otherwise they're empty for every method!
  239. */
  240. private static void shakeEnclosingElementMethods( MethodDeclaration e ) {
  241. e.getDeclaringType().getMethods();
  242. }
  243. @Override
  244. public void visitMethodDeclaration( MethodDeclaration e ) {
  245. shakeEnclosingElementMethods( e );
  246. // Get JAXB and JSON return types - default to Object
  247. String jaxbReturnType = "Object";
  248. String jsonReturnType = "Object";
  249. String xmlReturnType = "org.w3c.dom.Document";
  250. Collection<AnnotationMirror> annotations = e.getAnnotationMirrors();
  251. AnnotationMirror firstAnnotation = annotations.iterator().next();
  252. Map<AnnotationTypeElementDeclaration,AnnotationValue> annotationParams = firstAnnotation.getElementValues();
  253. boolean jaxbAlreadySet = false;
  254. for ( AnnotationTypeElementDeclaration key : annotationParams.keySet() ) {
  255. String name = key.getSimpleName();
  256. String val = annotationParams.get( key ).toString();
  257. if ( name.contentEquals( "JAXBList" ) ) {
  258. if ( annotationParams.get( key ) != null ) {
  259. jaxbReturnType = "java.util.List<" + stripDotClass( val ) + ">";
  260. jaxbAlreadySet = true;
  261. }
  262. } else if ( !jaxbAlreadySet && name.contentEquals( "JAXB" ) ) {
  263. if ( annotationParams.get( key ) != null ) {
  264. jaxbReturnType = stripDotClass( val );
  265. }
  266. } else if ( name.contentEquals( "JSON" ) ) {
  267. if ( annotationParams.get( key ) != null ) {
  268. jsonReturnType = stripDotClass( val );
  269. }
  270. }
  271. }
  272. boolean deprecated = e.getAnnotation( Deprecated.class ) != null;
  273. String methName = e.getSimpleName();
  274. String methSig = " public %s %s( %s ) %s {";
  275. methSig = String.format( methSig, "%RETURNTYPE%", methName, parametersIncludingTypes( e ), throwClause( e ) );
  276. String methCall = " Object rawResponse = client.%s( %s );";
  277. methCall = String.format( methCall, methName, parametersExcludingTypes( e ) );
  278. String methRet = " return (%s)parseCallResult( rawResponse );";
  279. methRet = String.format( methRet, "%RETURNTYPE%" );
  280. String methRet2 = " return parseCallResult( %s.class, rawResponse );";
  281. methRet2 = String.format( methRet2, "%RETURNTYPE%" );
  282. printMethod( outJAXB, jaxbReturnType, deprecated, methSig, methCall, methRet );
  283. printMethod( outJSON, jsonReturnType, deprecated, methSig, methCall, methRet2 );
  284. printMethod( outXML, xmlReturnType, deprecated, methSig, methCall, methRet );
  285. }
  286. public static void printMethod( PrintWriter out, String returnType, boolean deprecated, String methSig, String methCall, String methRet ) {
  287. if ( out == null ) {
  288. return;
  289. }
  290. if ( deprecated ) {
  291. out.println( " @Deprecated" );
  292. }
  293. out.println( methSig.replace( "%RETURNTYPE%", returnType ) );
  294. out.println( methCall );
  295. out.println( methRet.replace( "%RETURNTYPE%", returnType ) );
  296. out.println( " }" );
  297. out.println();
  298. }
  299. public static String stripDotClass( String input ) {
  300. if ( !input.endsWith( ".class" ) ) {
  301. return input;
  302. } else {
  303. return input.substring( 0, input.length() - 6 );
  304. }
  305. }
  306. }
  307. }