• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.hilt.processor.internal.root;
18 
19 import com.squareup.javapoet.JavaFile;
20 import java.io.IOException;
21 import java.io.Writer;
22 import java.util.regex.MatchResult;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 import javax.annotation.processing.Filer;
26 import javax.lang.model.element.Element;
27 import javax.tools.JavaFileObject;
28 
29 /**
30  * Typically we would just use {@code JavaFile#writeTo()} to write files. However, this formatter
31  * exists to add new lines inbetween interfaces. This can be important for classes with many
32  * interfaces (e.g. Dagger components) to avoid spamming the entire list of interfaces when
33  * reporting errors to the user.
34  *
35  * <p>See b/33108646.
36  */
37 final class RootFileFormatter {
38   private static final Pattern CLASS_PATERN = Pattern.compile("(\\h*)(.*class.*implements)(.*\\{)");
39 
40   /** Formats the {@link JavaFile} java source file. */
write(JavaFile javaFile, Filer filer)41   static void write(JavaFile javaFile, Filer filer) throws IOException {
42     String fileName =
43         javaFile.packageName.isEmpty()
44             ? javaFile.typeSpec.name
45             : javaFile.packageName + "." + javaFile.typeSpec.name;
46 
47     Element[] originatingElements = javaFile.typeSpec.originatingElements.toArray(new Element[0]);
48 
49     StringBuilder sb = new StringBuilder("");
50     javaFile.writeTo(sb);
51     String fileContent = formatInterfaces(sb.toString(), CLASS_PATERN);
52 
53     JavaFileObject filerSourceFile = filer.createSourceFile(fileName, originatingElements);
54     try (Writer writer = filerSourceFile.openWriter()) {
55       writer.write(fileContent);
56     } catch (Exception e) {
57       try {
58         filerSourceFile.delete();
59       } catch (Exception ignored) {
60         // Nothing to do.
61       }
62       throw e;
63     }
64   }
65 
formatInterfaces(String content, Pattern pattern)66   private static String formatInterfaces(String content, Pattern pattern) {
67     Matcher matcher = pattern.matcher(content);
68     StringBuffer sb = new StringBuffer(content.length());
69     while (matcher.find()) {
70       MatchResult result = matcher.toMatchResult();
71       String spaces = result.group(1);
72       String prefix = result.group(2);
73       String interfaces = result.group(3);
74       String formattedInterfaces = formatInterfaces(spaces, interfaces);
75       matcher.appendReplacement(
76           sb, Matcher.quoteReplacement(spaces + prefix + formattedInterfaces));
77     }
78     matcher.appendTail(sb);
79     return sb.toString();
80   }
81 
formatInterfaces(String prefixSpaces, String interfaces)82   private static String formatInterfaces(String prefixSpaces, String interfaces) {
83     StringBuilder sb = new StringBuilder(interfaces);
84     String newLine = String.format("\n%s   ", prefixSpaces);
85 
86     // Add a line break after each interface so that there's only 1 interface per line.
87     int i = 0;
88     int bracketCount = 0;
89     while (i >= 0 && i < sb.length()) {
90       char c = sb.charAt(i++);
91       if (c == '<') {
92         bracketCount++;
93       } else if (c == '>') {
94         bracketCount--;
95       } else if (c == ',' && bracketCount == 0) {
96         sb.insert(i++, newLine);
97       }
98     }
99     return sb.toString();
100   }
101 
RootFileFormatter()102   private RootFileFormatter() {}
103 }
104