99
1010import java .time .Duration ;
1111import java .util .Collections ;
12+ import java .util .HashMap ;
1213import java .util .Map ;
1314import java .util .Objects ;
1415import java .util .Optional ;
1516
1617import org .apache .commons .lang3 .StringUtils ;
1718import org .checkerframework .checker .nullness .qual .NonNull ;
19+ import org .checkerframework .checker .nullness .qual .Nullable ;
1820import org .reactfx .value .SuspendableVar ;
1921import org .reactfx .value .Val ;
2022import org .reactfx .value .Var ;
21-
23+ import org .slf4j .event .Level ;
24+
25+ import net .sourceforge .pmd .lang .JvmLanguagePropertyBundle ;
26+ import net .sourceforge .pmd .lang .Language ;
27+ import net .sourceforge .pmd .lang .LanguageProcessor ;
28+ import net .sourceforge .pmd .lang .LanguageProcessorRegistry ;
29+ import net .sourceforge .pmd .lang .LanguagePropertyBundle ;
30+ import net .sourceforge .pmd .lang .LanguageRegistry ;
2231import net .sourceforge .pmd .lang .LanguageVersion ;
23- import net .sourceforge .pmd .lang .LanguageVersionHandler ;
2432import net .sourceforge .pmd .lang .ast .Node ;
2533import net .sourceforge .pmd .lang .ast .Parser .ParserTask ;
2634import net .sourceforge .pmd .lang .ast .RootNode ;
3341import net .sourceforge .pmd .util .fxdesigner .model .ParseAbortedException ;
3442import net .sourceforge .pmd .util .fxdesigner .util .AuxLanguageRegistry ;
3543import net .sourceforge .pmd .util .fxdesigner .util .Tuple3 ;
44+ import net .sourceforge .pmd .util .log .MessageReporter ;
3645
3746
3847/**
4352 */
4453public class ASTManagerImpl implements ASTManager {
4554
55+ public static final MessageReporter NOOP_REPORTER = new MessageReporter () { // todo replace with MessageReporter.noop
56+ @ Override
57+ public boolean isLoggable (Level level ) {
58+ return false ;
59+ }
60+
61+
62+ @ Override
63+ public void logEx (Level level , @ Nullable String s , Object [] objects , @ Nullable Throwable throwable ) {
64+ // noop
65+ }
66+
67+
68+ @ Override
69+ public int numErrors () {
70+ return 0 ;
71+ }
72+ };
73+
4674 private final DesignerRoot designerRoot ;
4775
4876 private final Var <ClassLoader > auxclasspathClassLoader = Var .newSimpleVar (null );
@@ -60,6 +88,7 @@ public class ASTManagerImpl implements ASTManager {
6088 */
6189 private final SuspendableVar <String > sourceCode = Var .newSimpleVar ("" ).suspendable ();
6290 private final SuspendableVar <TextDocument > sourceDocument = Var .newSimpleVar (TextDocument .readOnlyString ("" , languageVersion .getValue ())).suspendable ();
91+ private final SuspendableVar <LanguageProcessorRegistry > lpRegistry = Var .<LanguageProcessorRegistry >newSimpleVar (null ).suspendable ();
6392
6493 private final Var <ParseAbortedException > currentException = Var .newSimpleVar (null );
6594
@@ -93,7 +122,7 @@ public ASTManagerImpl(DesignerRoot owner) {
93122
94123 Node updated ;
95124 try {
96- updated = refreshAST (this , source , version , classLoader ).orElse (null );
125+ updated = refreshAST (this , source , version , refreshRegistry ( version , classLoader ) ).orElse (null );
97126 currentException .setValue (null );
98127 } catch (ParseAbortedException e ) {
99128 updated = null ;
@@ -158,16 +187,27 @@ public Var<Map<String, String>> ruleProperties() {
158187 return ruleProperties ;
159188 }
160189
190+
161191 @ Override
162192 public Var <LanguageVersion > languageVersionProperty () {
163193 return languageVersion ;
164194 }
165195
166196
197+ @ Override
198+ public Val <LanguageProcessor > languageProcessorProperty () {
199+ return lpRegistry .mapDynamic (
200+ languageVersionProperty ().map (LanguageVersion ::getLanguage )
201+ .map (l -> lp -> lp .getProcessor (l ))
202+ );
203+ }
204+
205+
167206 public LanguageVersion getLanguageVersion () {
168207 return languageVersion .getValue ();
169208 }
170209
210+
171211 public void setLanguageVersion (LanguageVersion version ) {
172212 this .languageVersion .setValue (version );
173213 }
@@ -181,11 +221,51 @@ public Val<Node> compilationUnitProperty() {
181221 return nodeVal ;
182222 }
183223
224+
184225 @ Override
185226 public Var <ParseAbortedException > currentExceptionProperty () {
186227 return currentException ;
187228 }
188229
230+
231+ private LanguageProcessorRegistry refreshRegistry (LanguageVersion version , ClassLoader classLoader ) {
232+ LanguageProcessorRegistry current = lpRegistry .getValue ();
233+ if (current == null ) {
234+ Map <Language , LanguagePropertyBundle > langProperties = new HashMap <>();
235+ LanguagePropertyBundle bundle = version .getLanguage ().newPropertyBundle ();
236+ bundle .setLanguageVersion (version .getVersion ());
237+ if (bundle instanceof JvmLanguagePropertyBundle ) {
238+ ((JvmLanguagePropertyBundle ) bundle ).setClassLoader (classLoader );
239+ }
240+
241+ langProperties .put (version .getLanguage (), bundle );
242+
243+ LanguageRegistry languages =
244+ AuxLanguageRegistry .supportedLangs ()
245+ .getDependenciesOf (version .getLanguage ());
246+
247+ LanguageProcessorRegistry newRegistry =
248+ LanguageProcessorRegistry .create (languages ,
249+ langProperties ,
250+ NOOP_REPORTER );
251+ lpRegistry .setValue (newRegistry );
252+ return newRegistry ;
253+ }
254+
255+ // already created, need to check that the version is the same
256+ if (!current .getLanguages ().getLanguages ().contains (version .getLanguage ())
257+ || !current .getProcessor (version .getLanguage ()).getLanguageVersion ().equals (version )) {
258+ // current is invalid, recreate it
259+ current .close ();
260+ lpRegistry .setValue (null );
261+ return refreshRegistry (version , classLoader );
262+ }
263+
264+ // the current one is fine
265+ return current ;
266+ }
267+
268+
189269 /**
190270 * Refreshes the compilation unit given the current state of the model.
191271 *
@@ -194,28 +274,29 @@ public Var<ParseAbortedException> currentExceptionProperty() {
194274 private static Optional <Node > refreshAST (ApplicationComponent component ,
195275 String source ,
196276 LanguageVersion version ,
197- ClassLoader classLoader ) throws ParseAbortedException {
277+ LanguageProcessorRegistry lpRegistry ) throws ParseAbortedException {
198278
199279 String dummyFilePath = "dummy." + version .getLanguage ().getExtensions ().get (0 );
200280 TextDocument textDocument = TextDocument .readOnlyString (source , dummyFilePath , version );
201- LanguageVersionHandler handler = version . getLanguageVersionHandler ();
281+
202282 ParserTask task = new ParserTask (
203283 textDocument ,
204284 SemanticErrorReporter .noop (),
205- classLoader
285+ lpRegistry
206286 );
207287
288+ LanguageProcessor processor = lpRegistry .getProcessor (version .getLanguage ());
208289 RootNode node ;
209290 try {
210- node = handler .getParser ().parse (task );
291+ node = processor . services () .getParser ().parse (task );
211292 } catch (Exception e ) {
212293 component .logUserException (e , Category .PARSE_EXCEPTION );
213294 throw new ParseAbortedException (e );
214295 }
215296
216297 // Notify that the parse went OK so we can avoid logging very recent exceptions
217298
218- component .raiseParsableSourceFlag (() -> "Param hash: " + Objects .hash (source , version , classLoader ));
299+ component .raiseParsableSourceFlag (() -> "Param hash: " + Objects .hash (source , version , lpRegistry ));
219300
220301 return Optional .of (node );
221302 }
0 commit comments