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