1
+ import org .gradle .api .*;
2
+
3
+ import java .io .*;
4
+ import java .text .*;
5
+ import java .util .*;
6
+ import java .util .function .*;
7
+
8
+ /**
9
+ * The purpose of this helper is to convert UtSettings.kt source code
10
+ * to template resource file settings.properties (top-level entry in plugin JAR file).
11
+ * There are two stages: parsing of input to build models and then rendering models to output file
12
+ */
13
+
14
+ public class SettingsTemplateHelper {
15
+ private static final String [] apacheLines =
16
+ ("Copyright (c) " + new SimpleDateFormat ("yyyy" ).format (System .currentTimeMillis ()) + " utbot.org\n " +
17
+ "\n " +
18
+ "Licensed under the Apache License, Version 2.0 (the \" License\" );\n " +
19
+ "you may not use this file except in compliance with the License.\n " +
20
+ "You may obtain a copy of the License at\n " +
21
+ "\n " +
22
+ " http://www.apache.org/licenses/LICENSE-2.0\n " +
23
+ "\n " +
24
+ "Unless required by applicable law or agreed to in writing, software\n " +
25
+ "distributed under the License is distributed on an \" AS IS\" BASIS,\n " +
26
+ "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n " +
27
+ "See the License for the specific language governing permissions and\n " +
28
+ "limitations under the License." ).split ("\n " );
29
+
30
+ public static void proceed (Project project ) {
31
+ File settingsSourceDir = new File (project .getBuildDir ().getParentFile ().getParentFile (), "utbot-framework-api/src/main/kotlin/org/utbot/framework/" );
32
+ String sourceFileName = "UtSettings.kt" ;
33
+ File settingsResourceDir = new File (project .getBuildDir ().getParentFile ().getParentFile (), "utbot-intellij/src/main/resources/" );
34
+ String settingsFileName = "settings.properties" ;
35
+
36
+ Map <String , String > dictionary = new HashMap <>();
37
+ dictionary .put ("Int.MAX_VALUE" , String .valueOf (Integer .MAX_VALUE ));
38
+
39
+ List <PropertyModel > models = new ArrayList <>();
40
+ List <EnumInfo > enums = new ArrayList <>();
41
+ StringBuilder acc = new StringBuilder ();
42
+ List <String > docAcc = new ArrayList <>();
43
+ // Stage one: parsing sourcecode
44
+ try (BufferedReader reader = new BufferedReader (new FileReader (new File (settingsSourceDir , sourceFileName )))) {
45
+ for (String s = reader .readLine (); s != null ; s = reader .readLine ()) {
46
+ s = s .trim ();
47
+ if (s .startsWith ("enum class " )) {//Enum class declaration
48
+ enums .add (new EnumInfo (s .substring (11 , s .length () - 2 )));
49
+ } else if (s .matches ("[A-Z_]+,?" ) && !enums .isEmpty ()) {//Enum value
50
+ var enumValue = s .substring (0 , s .length ());
51
+ if (enumValue .endsWith ("," )) enumValue = enumValue .substring (0 , enumValue .length () - 1 );
52
+ enums .get (enums .size () - 1 ).docMap .put (enumValue , new ArrayList <>(docAcc ));
53
+ } else if (s .startsWith ("const val " )) {//Constand value to be substitute later if need
54
+ int pos = s .indexOf (" = " );
55
+ dictionary .put (s .substring (10 , pos ), s .substring (pos + 3 ));
56
+ } else if (s .equals ("/**" )) {//Start of docuemntation block
57
+ docAcc .clear ();
58
+ } else if (s .startsWith ("* " )) {
59
+ if (!s .contains ("href" )) {//Links are not supported, skip them
60
+ docAcc .add (s .substring (2 ));
61
+ }
62
+ } else if (s .startsWith ("var" ) || s .startsWith ("val" )) {//Restart accumulation
63
+ acc .delete (0 , acc .length ());
64
+ acc .append (s );
65
+ } else if (s .isEmpty () && acc .length () > 0 ) {//Build model from accumulated lines
66
+ s = acc .toString ();
67
+ acc .delete (0 , acc .length ());
68
+ if (s .startsWith ("var" ) || s .startsWith ("val" )) {
69
+ var i = s .indexOf (" by " , 3 );
70
+ if (i > 0 ) {
71
+ var key = s .substring (3 , i ).trim ();
72
+ if (key .contains (":" )) {
73
+ key = key .substring (0 , key .lastIndexOf (':' ));
74
+ }
75
+ PropertyModel model = new PropertyModel (key );
76
+ models .add (model );
77
+ s = s .substring (i + 7 );
78
+ i = s .indexOf ("Property" );
79
+ if (i > 0 ) model .type = s .substring (0 , i );
80
+ if (i == 0 ) {
81
+ i = s .indexOf ('<' , i );
82
+ if (i != -1 ) {
83
+ s = s .substring (i +1 );
84
+ i = s .indexOf ('>' );
85
+ if (i != -1 ) {
86
+ model .type = s .substring (0 , i );
87
+ }
88
+ }
89
+ }
90
+
91
+ i = s .indexOf ('(' , i );
92
+ if (i > 0 ) {
93
+ s = s .substring (i + 1 );
94
+ var defaultValue = s .substring (0 , s .indexOf (')' )).trim ();
95
+ if (defaultValue .contains ("," )) defaultValue = defaultValue .substring (0 , defaultValue .indexOf (',' ));
96
+ defaultValue = dictionary .getOrDefault (defaultValue , defaultValue );
97
+ if (defaultValue .matches ("[\\ d_]+L" )) {
98
+ defaultValue = defaultValue .substring (0 , defaultValue .length () - 1 ).replace ("_" , "" );
99
+ }
100
+ if (defaultValue .matches ("^\" .+\" $" )) {
101
+ defaultValue = defaultValue .substring (1 , defaultValue .length () - 1 );
102
+ }
103
+ model .defaultValue = defaultValue ;
104
+ model .docLines .addAll (docAcc );
105
+ }
106
+ }
107
+ }
108
+ } else if (acc .length () > 0 ) {
109
+ acc .append (" " + s );
110
+ }
111
+ }
112
+ } catch (IOException ioe ) {
113
+ System .err .println ("Unexpected error when processing " + sourceFileName );
114
+ ioe .printStackTrace ();
115
+ }
116
+
117
+ // Stage two: properties file rendering
118
+ try (PrintWriter writer = new PrintWriter (new File (settingsResourceDir , settingsFileName ))) {
119
+ for (String apacheLine : apacheLines ) {
120
+ writer .println ("# " + apacheLine );
121
+ }
122
+ for (PropertyModel model : models ) {
123
+ if (model .type .equals ("Enum" )) {
124
+ String [] split = model .defaultValue .split ("\\ ." );
125
+ if (split .length > 1 ) {
126
+ model .defaultValue = split [1 ];
127
+ EnumInfo enumInfo = enums .stream ().filter (new Predicate <EnumInfo >() {
128
+ @ Override
129
+ public boolean test (EnumInfo enumInfo ) {
130
+ return enumInfo .className .equals (split [0 ]);
131
+ }
132
+ }).findFirst ().orElse (null );
133
+ if (enumInfo != null ) {
134
+ model .docLines .add ("" );
135
+ for (Map .Entry <String , List <String >> entry : enumInfo .docMap .entrySet ()) {
136
+ String enumValue = entry .getKey ();
137
+ if (entry .getValue ().size () == 1 ) {
138
+ model .docLines .add (enumValue + ": " + entry .getValue ().get (0 ));
139
+ } else {
140
+ model .docLines .add (enumValue );
141
+ for (String line : entry .getValue ()) {
142
+ model .docLines .add (line );
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ writer .println ();
150
+ writer .println ("#" );
151
+ for (String docLine : model .docLines ) {
152
+ if (docLine .isEmpty ()) {
153
+ writer .println ("#" );
154
+ } else {
155
+ writer .println ("# " + docLine );
156
+ }
157
+ }
158
+ boolean defaultIsAlreadyMentioned = model .docLines .stream ().anyMatch (new Predicate <String >() {
159
+ @ Override
160
+ public boolean test (String s ) {
161
+ return s .toLowerCase (Locale .ROOT ).contains ("default" );
162
+ }
163
+ });
164
+ if (!defaultIsAlreadyMentioned ) {
165
+ writer .println ("#" );
166
+ writer .println ("# Default value is [" + model .defaultValue + "]" );
167
+ }
168
+ writer .println ("#" + model .key + "=" + model .defaultValue );
169
+ }
170
+ writer .flush ();
171
+ writer .close ();
172
+ } catch (IOException ioe ) {
173
+ System .err .println ("Unexpected error when saving " + settingsFileName );
174
+ ioe .printStackTrace ();
175
+ }
176
+ }
177
+
178
+ private static class PropertyModel {
179
+ final String key ;
180
+ String type = "" ;
181
+ String defaultValue = "" ;
182
+ List <String > docLines = new ArrayList <>();
183
+
184
+ PropertyModel (String key ) {
185
+ this .key = key ;
186
+ }
187
+ }
188
+
189
+ private static class EnumInfo {
190
+ final String className ;
191
+ Map <String , List <String >> docMap = new LinkedHashMap <>();
192
+
193
+ public EnumInfo (String className ) {
194
+ this .className = className ;
195
+ }
196
+ }
197
+ }
0 commit comments