Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

PushinCollector.java 14KB

14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
14 anos atrás
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /* *******************************************************************
  2. * Copyright (c) 2010 Contributors
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v 2.0
  6. * which accompanies this distribution and is available at
  7. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  8. *
  9. * Contributors:
  10. * Andy Clement - SpringSource
  11. * ******************************************************************/
  12. package org.aspectj.ajdt.internal.compiler.lookup;
  13. import java.io.File;
  14. import java.io.FileWriter;
  15. import java.io.IOException;
  16. import java.util.ArrayList;
  17. import java.util.HashMap;
  18. import java.util.List;
  19. import java.util.Map;
  20. import java.util.Properties;
  21. import java.util.StringTokenizer;
  22. import org.aspectj.ajdt.internal.compiler.IOutputClassFileNameProvider;
  23. import org.aspectj.asm.internal.CharOperation;
  24. import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
  25. import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
  26. import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
  27. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
  28. import org.aspectj.weaver.ResolvedType;
  29. import org.aspectj.weaver.World;
  30. import org.aspectj.weaver.patterns.ExactTypePattern;
  31. import org.aspectj.weaver.patterns.TypePattern;
  32. /**
  33. * Collects up information about the application of ITDs and relevant declares - it can then output source code as if those ITDs had
  34. * been pushed in. Supports the simulated push-in of:
  35. * <ul>
  36. * <li>declare at_type
  37. * <li>itd method
  38. * <li>itd field
  39. * <li>itd ctor
  40. * <li>declare parents
  41. * </ul>
  42. *
  43. * @author Andy Clement
  44. * @since 1.6.9
  45. */
  46. public class PushinCollector {
  47. private final static String OPTION_SUFFIX = "suffix";
  48. private final static String OPTION_DIR = "dir";
  49. private final static String OPTION_PKGDIRS = "packageDirs";
  50. private final static String OPTION_DEBUG = "debug";
  51. private final static String OPTION_LINENUMS = "lineNums";
  52. private final static String OPTION_DUMPUNCHANGED = "dumpUnchanged";
  53. private World world;
  54. private boolean debug = false;
  55. private boolean dumpUnchanged = false;
  56. private IOutputClassFileNameProvider outputFileNameProvider;
  57. private String specifiedOutputDirectory;
  58. private boolean includePackageDirs;
  59. private boolean includeLineNumberComments;
  60. private String suffix;
  61. // This first collection stores the 'text' for the declarations.
  62. private Map<AbstractMethodDeclaration, RepresentationAndLocation> codeRepresentation = new HashMap<>();
  63. // This stores the new annotations
  64. private Map<SourceTypeBinding, List<String>> additionalAnnotations = new HashMap<>();
  65. // This stores the new parents
  66. private Map<SourceTypeBinding, List<ExactTypePattern>> additionalParents = new HashMap<>();
  67. // This indicates which types are affected by which intertype declarations
  68. private Map<SourceTypeBinding, List<AbstractMethodDeclaration>> newDeclarations = new HashMap<>();
  69. private PushinCollector(World world, Properties configuration) {
  70. this.world = world;
  71. // Configure the instance based on the input properties
  72. specifiedOutputDirectory = configuration.getProperty(OPTION_DIR);
  73. includePackageDirs = configuration.getProperty(OPTION_PKGDIRS, "true").equalsIgnoreCase("true");
  74. includeLineNumberComments = configuration.getProperty(OPTION_LINENUMS, "false").equalsIgnoreCase("true");
  75. debug = configuration.getProperty(OPTION_DEBUG, "false").equalsIgnoreCase("true");
  76. dumpUnchanged = configuration.getProperty(OPTION_DUMPUNCHANGED, "false").equalsIgnoreCase("true");
  77. String specifiedSuffix = configuration.getProperty(OPTION_SUFFIX, "pushedin");
  78. if (specifiedSuffix.length() > 0) {
  79. StringBuilder sb = new StringBuilder();
  80. sb.append(".").append(specifiedSuffix);
  81. suffix = sb.toString();
  82. } else {
  83. suffix = "";
  84. }
  85. if (debug) {
  86. System.out.println("Configured to create pushin side files:" + configuration);
  87. System.out.println("dumpUnchanged=" + dumpUnchanged + "\nincludePackageDirs=" + includePackageDirs);
  88. }
  89. }
  90. private String getName(CompilationUnitDeclaration cud) {
  91. if (cud == null) {
  92. return "UNKNOWN";
  93. }
  94. if (cud.scope == null) {
  95. return "UNKNOWN";
  96. }
  97. if (cud.scope.referenceContext == null) {
  98. return "UNKNOWN";
  99. }
  100. return new String(cud.scope.referenceContext.getFileName());
  101. }
  102. /**
  103. * @return true if the type is affected by something (itd/declare anno/declare parent)
  104. */
  105. private boolean hasChanged(SourceTypeBinding stb) {
  106. return newDeclarations.get(stb) != null || additionalParents.get(stb) != null || additionalAnnotations.get(stb) != null;
  107. }
  108. /**
  109. * Produce the modified source that looks like the itds and declares have been applied.
  110. */
  111. public void dump(CompilationUnitDeclaration compilationUnitDeclaration, String outputFileLocation) {
  112. if (compilationUnitDeclaration.scope.topLevelTypes == null || compilationUnitDeclaration.scope.topLevelTypes.length == 0) {
  113. return;
  114. }
  115. SourceTypeBinding[] types = compilationUnitDeclaration.scope.topLevelTypes;
  116. if (types == null || types.length == 0) {
  117. return;
  118. }
  119. // Process all types working from end to start as whatever we do (insert-wise) will affect locations later in the file
  120. StringBuilder sourceContents = new StringBuilder();
  121. // put the whole original file in the buffer
  122. boolean changed = false;
  123. sourceContents.append(compilationUnitDeclaration.compilationResult.compilationUnit.getContents());
  124. for (int t = types.length - 1; t >= 0; t--) {
  125. SourceTypeBinding sourceTypeBinding = compilationUnitDeclaration.scope.topLevelTypes[t];
  126. if (!hasChanged(sourceTypeBinding)) {
  127. if (debug) {
  128. System.out.println(getName(compilationUnitDeclaration) + " has nothing applied");
  129. }
  130. continue;
  131. }
  132. changed = true;
  133. int bodyEnd = sourceTypeBinding.scope.referenceContext.bodyEnd; // last '}' of the type
  134. List<AbstractMethodDeclaration> declarations = newDeclarations.get(sourceTypeBinding);
  135. if (declarations != null) {
  136. for (AbstractMethodDeclaration md : declarations) {
  137. RepresentationAndLocation ral = codeRepresentation.get(md);
  138. if (ral != null) {
  139. String s = ral.textualRepresentation;
  140. sourceContents.insert(bodyEnd, "\n" + s + "\n");
  141. if (includeLineNumberComments && ral.linenumber != -1) {
  142. sourceContents.insert(bodyEnd, "\n // " + ral.linenumber);
  143. }
  144. }
  145. }
  146. }
  147. // fix up declare parents - may need to attach them to existing ones
  148. TypeReference sr = sourceTypeBinding.scope.referenceContext.superclass;
  149. TypeReference[] trs = sourceTypeBinding.scope.referenceContext.superInterfaces;
  150. List<ExactTypePattern> newParents = additionalParents.get(sourceTypeBinding);
  151. StringBuilder extendsString = new StringBuilder();
  152. StringBuilder implementsString = new StringBuilder();
  153. if (newParents != null && newParents.size() > 0) {
  154. for (ExactTypePattern newParent : newParents) {
  155. ResolvedType newParentType = newParent.getExactType().resolve(world);
  156. if (newParentType.isInterface()) {
  157. if (implementsString.length() > 0) {
  158. implementsString.append(",");
  159. }
  160. implementsString.append(newParentType.getName());
  161. } else {
  162. extendsString.append(newParentType.getName());
  163. }
  164. }
  165. if (trs == null && sr == null) {
  166. // nothing after the class declaration, let's insert what we need to
  167. // Find the position just before the type opening '{'
  168. int beforeOpeningCurly = sourceTypeBinding.scope.referenceContext.bodyStart - 1;
  169. if (implementsString.length() != 0) {
  170. implementsString.insert(0, "implements ");
  171. implementsString.append(" ");
  172. sourceContents.insert(beforeOpeningCurly, implementsString);
  173. }
  174. if (extendsString.length() != 0) {
  175. extendsString.insert(0, "extends ");
  176. extendsString.append(" ");
  177. sourceContents.insert(beforeOpeningCurly, extendsString);
  178. }
  179. }
  180. }
  181. List<String> annos = additionalAnnotations.get(sourceTypeBinding);
  182. if (annos != null && annos.size() > 0) {
  183. for (String anno : annos) {
  184. sourceContents.insert(sourceTypeBinding.scope.referenceContext.declarationSourceStart, anno + " ");
  185. }
  186. }
  187. }
  188. if (changed || (!changed && dumpUnchanged)) {
  189. try {
  190. if (debug) {
  191. System.out.println("Pushed in output file being written to " + outputFileLocation);
  192. System.out.println(sourceContents);
  193. }
  194. FileWriter fos = new FileWriter(new File(outputFileLocation));
  195. fos.write(sourceContents.toString());
  196. fos.close();
  197. } catch (IOException e) {
  198. e.printStackTrace();
  199. }
  200. }
  201. }
  202. /**
  203. * Encapsulates a text representation (source code) for a member and the line where it was declared.
  204. */
  205. private static class RepresentationAndLocation {
  206. String textualRepresentation;
  207. int linenumber;
  208. public RepresentationAndLocation(String textualRepresentation, int linenumber) {
  209. this.textualRepresentation = textualRepresentation;
  210. this.linenumber = linenumber;
  211. }
  212. }
  213. public void recordInterTypeMethodDeclarationCode(AbstractMethodDeclaration md, String s, int line) {
  214. codeRepresentation.put(md, new RepresentationAndLocation(s, line));
  215. }
  216. public void recordInterTypeFieldDeclarationCode(AbstractMethodDeclaration md, String s, int line) {
  217. codeRepresentation.put(md, new RepresentationAndLocation(s, line));
  218. }
  219. public void recordInterTypeConstructorDeclarationCode(AbstractMethodDeclaration md, String s, int line) {
  220. codeRepresentation.put(md, new RepresentationAndLocation(s, line));
  221. }
  222. // public void recordDeclareAnnotationDeclarationCode(AbstractMethodDeclaration md, String value) {
  223. // codeRepresentation.put(md, new RepresentationAndLocation(value, -1));
  224. // }
  225. public void tagAsMunged(SourceTypeBinding sourceType, AbstractMethodDeclaration sourceMethod) {
  226. if (sourceMethod == null) {
  227. // seen when an ITD field is made onto an interface. It matches, but the sourceMethod is null.
  228. // can be null for binary weave (there is no source method)
  229. return;
  230. }
  231. List<AbstractMethodDeclaration> amds = newDeclarations.computeIfAbsent(sourceType, k -> new ArrayList<>());
  232. amds.add(sourceMethod);
  233. }
  234. public void tagAsMunged(SourceTypeBinding sourceType, String annotationString) {
  235. List<String> annos = additionalAnnotations.computeIfAbsent(sourceType, k -> new ArrayList<>());
  236. annos.add(annotationString);
  237. }
  238. public void dump(CompilationUnitDeclaration unit) {
  239. String outputFile = getOutputFileFor(unit);
  240. if (debug) {
  241. System.out
  242. .println("Output location is " + outputFile + " for " + new String(unit.scope.referenceContext.getFileName()));
  243. }
  244. dump(unit, outputFile);
  245. }
  246. private String getOutputFileFor(CompilationUnitDeclaration unit) {
  247. StringBuilder sb = new StringBuilder();
  248. // Create the directory portion of the output location
  249. if (specifiedOutputDirectory != null) {
  250. sb.append(specifiedOutputDirectory).append(File.separator);
  251. } else {
  252. String sss = outputFileNameProvider.getOutputClassFileName("A".toCharArray(), unit.compilationResult);
  253. sb.append(sss, 0, sss.length() - 7);
  254. }
  255. // Create the subdirectory structure matching the package declaration
  256. if (includePackageDirs) {
  257. char[][] packageName = unit.compilationResult.packageName;
  258. if (packageName != null) {
  259. sb.append(CharOperation.concatWith(unit.compilationResult.packageName, File.separatorChar));
  260. sb.append(File.separator);
  261. }
  262. }
  263. new File(sb.toString()).mkdirs();
  264. // Create the filename portion
  265. String filename = new String(unit.getFileName()); // gives 'n:\A.java'
  266. int index = filename.lastIndexOf('/');
  267. int index2 = filename.lastIndexOf('\\');
  268. if (index > index2) {
  269. sb.append(filename.substring(index + 1));
  270. } else if (index2 > index) {
  271. sb.append(filename.substring(index2 + 1));
  272. } else {
  273. sb.append(filename);
  274. }
  275. // Add the suffix (may be an empty string)
  276. sb.append(suffix);
  277. return sb.toString();
  278. }
  279. public void tagAsMunged(SourceTypeBinding sourceType, TypePattern typePattern) {
  280. if (typePattern instanceof ExactTypePattern) {
  281. List<ExactTypePattern> annos = additionalParents.computeIfAbsent(sourceType, k -> new ArrayList<>());
  282. annos.add((ExactTypePattern) typePattern);
  283. }
  284. }
  285. /**
  286. * Checks if the aspectj.pushin property is set - this is the main condition for triggering the creation of pushed-in source
  287. * files. If not set just to 'true', the value of the property is processed as configuration. Configurable options are:
  288. * <ul>
  289. * <li>dir=XXXX - to set the output directory for the pushed in files
  290. * <li>suffix=XXX - to set the suffix, can be blank to get just '.java'
  291. * </ul>
  292. */
  293. public static PushinCollector createInstance(World world) {
  294. try {
  295. String property = System.getProperty("aspectj.pushin");
  296. if (property == null) {
  297. return null;
  298. }
  299. Properties configuration = new Properties();
  300. StringTokenizer tokenizer = new StringTokenizer(property, ",");
  301. while (tokenizer.hasMoreElements()) {
  302. String token = tokenizer.nextToken();
  303. // Simplest thing to do is turn it on 'aspectj.pushin=true'
  304. if (token.equalsIgnoreCase("true")) {
  305. continue;
  306. }
  307. int positionOfEquals = token.indexOf("=");
  308. if (positionOfEquals != -1) {
  309. // it is an option
  310. String optionName = token.substring(0, positionOfEquals);
  311. String optionValue = token.substring(positionOfEquals + 1);
  312. configuration.put(optionName, optionValue);
  313. } else {
  314. // it is a flag
  315. configuration.put(token, "true");
  316. }
  317. }
  318. return new PushinCollector(world, configuration);
  319. } catch (Exception e) {
  320. // unable to read system properties...
  321. }
  322. return null;
  323. }
  324. public void setOutputFileNameProvider(IOutputClassFileNameProvider outputFileNameProvider) {
  325. this.outputFileNameProvider = outputFileNameProvider;
  326. }
  327. }