7d7d84d1cf3fd18733079e1c00ea246663ab5153
[mdsal.git] / binding2 / mdsal-binding2-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / javav2 / java / api / generator / util / JavaCodePrettyPrint.java
1 /*
2  * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.javav2.java.api.generator.util;
9
10 import java.util.ArrayList;
11 import java.util.List;
12 import javax.annotation.RegEx;
13 import org.apache.commons.lang3.StringUtils;
14
15 /**
16  * Pretty-print utility for generated Java code.
17  */
18 public final class JavaCodePrettyPrint {
19
20     // JDoc
21     private static final String JDOC_START = "/**";
22     private static final String JDOC_PART = " *";
23     private static final String JDOC_END = "*/";
24
25     // Comments
26     private static final String COMMENTS = "//";
27
28     // Body
29     private static final char START_BODY = '{';
30     private static final char END_BODY = '}';
31
32     // Whitespaces
33     @RegEx
34     private static final String NEW_LINE_REGEX = "\\r?\\n";
35     @RegEx
36     private static final String WS_REGEX = "\\s+";
37     private static final char SPACE = ' ';
38     private static final char NEW_LINE = '\n';
39
40     // Indention
41     private static final int INDENT = 4;
42
43     // Specific keywords
44     private static final String PACKAGE = "package";
45     private static final String IMPORT = "import";
46
47     // Line
48     private static final char END_LINE = ';';
49     private static final char AT = '@';
50
51     private JavaCodePrettyPrint() {
52         throw new UnsupportedOperationException("Util class");
53     }
54
55     /**
56      * Pretty-print generated Java code.
57      *
58      * @param unformedJavaFile
59      *            - unformed Java file from generator
60      * @return formed Java file
61      */
62     public static String perform(final String unformedJavaFile) {
63         final StringBuilder sb = new StringBuilder();
64         String[] splittedByNewLine = unformedJavaFile.split(NEW_LINE_REGEX);
65         // remove excessive whitespaces
66         splittedByNewLine = phaseOne(splittedByNewLine);
67         // merge or divide lines which need it && setup base new lines at the
68         // end of lines
69         splittedByNewLine = phaseTwo(splittedByNewLine);
70         // fix indents
71         splittedByNewLine = phaseThree(splittedByNewLine);
72
73         for (final String line : splittedByNewLine) {
74             sb.append(line);
75         }
76         return sb.toString();
77     }
78
79     /**
80      * Set up indents.
81      *
82      * @param splittedByNewLine
83      *            - cleaned and merged/divided lines
84      * @return fixed intents in lines
85      */
86     private static String[] phaseThree(final String[] splittedByNewLine) {
87         int indentCount = 0;
88         final List<String> lines = new ArrayList<>();
89         for (int i = 0; i < splittedByNewLine.length; i++) {
90             final StringBuilder sb = new StringBuilder();
91             indentCount = lineIndent(sb, indentCount, splittedByNewLine[i]);
92             sb.append(splittedByNewLine[i]);
93             if ((splittedByNewLine[i].contains(String.valueOf(END_BODY))
94                     || splittedByNewLine[i].contains(String.valueOf(END_LINE))) && indentCount == 1) {
95                 sb.append(NEW_LINE);
96             }
97             lines.add(sb.toString());
98         }
99
100         return lines.toArray(new String[lines.size()]);
101     }
102
103     private static int lineIndent(final StringBuilder sb, final int indentCount, final String line) {
104         int newIndentCount = indentCount;
105         if (line.contains(String.valueOf(END_BODY)) && !line.startsWith(JDOC_PART)) {
106             newIndentCount--;
107         }
108         for (int i = 0; i < (newIndentCount * INDENT); i++) {
109             sb.append(SPACE);
110         }
111
112         if (line.contains(String.valueOf(START_BODY)) && !line.startsWith(JDOC_PART)) {
113             newIndentCount++;
114         }
115
116         return newIndentCount;
117     }
118
119     /**
120      * Join or split lines if necessary.
121      *
122      * @param splittedByNewLine
123      *            - cleaned lines from whitespaces around them
124      * @return fixed lines
125      */
126     private static String[] phaseTwo(final String[] splittedByNewLine) {
127         final List<String> fixedLines = new ArrayList<>();
128
129         // prepare package part
130         if (splittedByNewLine[0].startsWith(PACKAGE)) {
131             fixedLines.add(new StringBuilder(splittedByNewLine[0]).append(NEW_LINE).append(NEW_LINE).toString());
132         }
133
134         // prepare imports
135         int importsEndAt;
136         if (splittedByNewLine[1].startsWith(IMPORT)) {
137             importsEndAt = 1;
138             for (int i = 1; i < splittedByNewLine.length - 1; i++) {
139                 if (!splittedByNewLine[i + 1].startsWith(IMPORT)) {
140                     fixedLines.add(new StringBuilder(splittedByNewLine[i]).append(NEW_LINE).append(NEW_LINE).toString());
141                     importsEndAt = i;
142                     break;
143                 } else {
144                     fixedLines.add(new StringBuilder(splittedByNewLine[i]).append(NEW_LINE).toString());
145                 }
146             }
147         } else {
148             importsEndAt = 0;
149         }
150
151         // prepare class
152         StringBuilder sbLineClass = new StringBuilder();
153         int classStartEnd = 0;
154         for (int i = importsEndAt + 1; i < splittedByNewLine.length; i++) {
155             i = appendJDoc(splittedByNewLine, fixedLines, i);
156             if (!splittedByNewLine[i].contains(String.valueOf(START_BODY))) {
157                 sbLineClass.append(splittedByNewLine[i]).append(SPACE);
158             } else {
159                 fixedLines.add(sbLineClass.append(splittedByNewLine[i]).append(NEW_LINE).append(NEW_LINE).toString());
160                 classStartEnd = i + 1;
161                 break;
162             }
163         }
164
165         for(int i = classStartEnd; i < splittedByNewLine.length; i++){
166             i = appendJDoc(splittedByNewLine, fixedLines, i);
167             if (!splittedByNewLine[i].startsWith(COMMENTS)
168                     && !splittedByNewLine[i].endsWith(String.valueOf(END_LINE))
169                     && !splittedByNewLine[i].endsWith(String.valueOf(START_BODY))
170                     && !splittedByNewLine[i].endsWith(String.valueOf(END_BODY))
171                     && !splittedByNewLine[i].startsWith(String.valueOf(AT))) {
172                 sbLineClass = new StringBuilder();
173                 for (int j = i; j < splittedByNewLine.length; j++) {
174                     if (!splittedByNewLine[j].contains(String.valueOf(START_BODY))
175                             && !splittedByNewLine[j].contains(String.valueOf(END_LINE))) {
176                         final String str = splittedByNewLine[j];
177                         sbLineClass.append(str).append(SPACE);
178                     } else {
179                         fixedLines.add(sbLineClass.append(splittedByNewLine[j]).append(NEW_LINE).toString());
180                         i = j;
181                         break;
182                     }
183                 }
184                 continue;
185             }
186             final String splStri = splittedByNewLine[i];
187             final String stringSB = String.valueOf(START_BODY);
188             final String stringEB = String.valueOf(END_BODY);
189             if (splStri.contains(stringSB) && splStri.endsWith(stringEB)) {
190                 final StringBuilder sb = new StringBuilder();
191                 for (int j = 0; j < splittedByNewLine[i].length(); j++) {
192                     if (splittedByNewLine[i].charAt(j) == END_BODY) {
193                         sb.append(NEW_LINE);
194                     }
195                     sb.append(splittedByNewLine[i].charAt(j));
196                     if (splittedByNewLine[i].charAt(j) == START_BODY) {
197                         sb.append(NEW_LINE);
198                     }
199                 }
200                 final String[] split = sb.toString().split(NEW_LINE_REGEX);
201                 for (final String s : split) {
202                     fixedLines.add(new StringBuilder(s).append(NEW_LINE).toString());
203                 }
204                 continue;
205             }
206             fixedLines.add(new StringBuilder(splittedByNewLine[i]).append(NEW_LINE).toString());
207         }
208
209         return fixedLines.toArray(new String[fixedLines.size()]);
210     }
211
212     private static int appendJDoc(final String[] splittedByNewLine, final List<String> fixedLines, int i) {
213         if (splittedByNewLine[i].contains(JDOC_START)) {
214             fixedLines.add(new StringBuilder(splittedByNewLine[i]).append(NEW_LINE).toString());
215             for (int j = i + 1; j < splittedByNewLine.length - 1; j++) {
216                 if (splittedByNewLine[j].contains(JDOC_END)) {
217                     fixedLines.add(new StringBuilder(SPACE)
218                             .append(SPACE).append(splittedByNewLine[j]).append(NEW_LINE).toString());
219                     i = j + 1;
220                     break;
221                 } else {
222                     fixedLines.add(new StringBuilder(SPACE)
223                             .append(SPACE).append(splittedByNewLine[j]).append(NEW_LINE).toString());
224                 }
225             }
226         }
227         return i;
228     }
229
230     /**
231      * Remove empty lines and whitespaces adjacent lines.
232      *
233      * @param splittedByNewLine
234      *            - lines with whitespaces around them
235      * @return cleaned lines from whitespaces
236      */
237     private static String[] phaseOne(final String[] splittedByNewLine) {
238         final List<String> linesWithoutWhitespaces = new ArrayList<>();
239         for (final String line : splittedByNewLine) {
240             if (!StringUtils.isBlank(line)) {
241                 int lineStart = 0;
242                 for (int i = 0; i < line.length(); i++) {
243                     if (StringUtils.isWhitespace(String.valueOf(line.charAt(i)))) {
244                         lineStart++;
245                     } else {
246                         break;
247                     }
248                 }
249                 int lineEnd = line.length() - 1;
250                 while (StringUtils.isWhitespace(String.valueOf(line.charAt(lineEnd)))) {
251                     lineEnd--;
252                 }
253                 linesWithoutWhitespaces.add(line.substring(lineStart, lineEnd + 1));
254             }
255         }
256         return linesWithoutWhitespaces.toArray(new String[linesWithoutWhitespaces.size()]);
257     }
258 }