1+ /*******************************************************************************
2+ * Copyright (c) 2024 Broadcom, Inc.
3+ * All rights reserved. This program and the accompanying materials
4+ * are made available under the terms of the Eclipse Public License v1.0
5+ * which accompanies this distribution, and is available at
6+ * https://www.eclipse.org/legal/epl-v10.html
7+ *
8+ * Contributors:
9+ * Broadcom, Inc. - initial API and implementation
10+ *******************************************************************************/
11+ package org .springframework .ide .vscode .boot .java .conditionals ;
12+
13+ import java .util .ArrayList ;
14+ import java .util .Collections ;
15+ import java .util .List ;
16+ import java .util .Set ;
17+ import java .util .TreeSet ;
18+
19+ import org .eclipse .jdt .core .dom .ASTNode ;
20+ import org .eclipse .jdt .core .dom .MemberValuePair ;
21+ import org .eclipse .jdt .core .dom .NormalAnnotation ;
22+ import org .eclipse .jdt .core .dom .StringLiteral ;
23+ import org .springframework .ide .vscode .boot .java .annotations .AnnotationAttributeCompletionProvider ;
24+ import org .springframework .ide .vscode .boot .java .annotations .AnnotationAttributeProposal ;
25+ import org .springframework .ide .vscode .boot .java .utils .ASTUtils ;
26+ import org .springframework .ide .vscode .boot .metadata .ProjectBasedPropertyIndexProvider ;
27+ import org .springframework .ide .vscode .boot .metadata .PropertyInfo ;
28+ import org .springframework .ide .vscode .boot .metadata .SpringPropertyIndexProvider ;
29+ import org .springframework .ide .vscode .commons .java .IJavaProject ;
30+ import org .springframework .ide .vscode .commons .util .FuzzyMap ;
31+
32+ /**
33+ * @author Martin Lippert
34+ */
35+ public class ConditionalOnPropertyCompletionProcessor implements AnnotationAttributeCompletionProvider {
36+
37+ public enum Mode {
38+ PREFIX , PROPERTY
39+ }
40+
41+ private final SpringPropertyIndexProvider indexProvider ;
42+ private final ProjectBasedPropertyIndexProvider adHocIndexProvider ;
43+ private final Mode mode ;
44+
45+ public ConditionalOnPropertyCompletionProcessor (SpringPropertyIndexProvider indexProvider ,
46+ ProjectBasedPropertyIndexProvider adHocIndexProvider ,
47+ Mode mode ) {
48+ this .indexProvider = indexProvider ;
49+ this .adHocIndexProvider = adHocIndexProvider ;
50+ this .mode = mode ;
51+ }
52+
53+ @ Override
54+ public List <AnnotationAttributeProposal > getCompletionCandidates (IJavaProject project , ASTNode node ) {
55+ if (Mode .PROPERTY == this .mode ) {
56+ String prefix = getPrefixAttributeValue (node );
57+ return findProperties (project , prefix );
58+ }
59+ else if (Mode .PREFIX == this .mode ) {
60+ return findPrefixes (project );
61+ }
62+ else {
63+ return Collections .emptyList ();
64+ }
65+ }
66+
67+ private List <AnnotationAttributeProposal > findProperties (IJavaProject project , String prefix ) {
68+ List <AnnotationAttributeProposal > result = new ArrayList <>();
69+
70+ // First the 'real' properties, Then also add 'ad-hoc' properties
71+ addPropertyProposals (indexProvider .getIndex (project ).getProperties (), prefix , result );
72+ addPropertyProposals (adHocIndexProvider .getIndex (project ), prefix , result );
73+
74+ result .sort ((p1 , p2 ) -> p1 .getLabel ().compareTo (p2 .getLabel ()));
75+
76+ return result ;
77+ }
78+
79+ private List <AnnotationAttributeProposal > findPrefixes (IJavaProject project ) {
80+ Set <AnnotationAttributeProposal > prefixes = new TreeSet <>((p1 , p2 ) -> p1 .getLabel ().compareTo (p2 .getLabel ()));
81+
82+ // First the 'real' properties, then also add 'ad-hoc' properties
83+ addPrefixProposals (indexProvider .getIndex (project ).getProperties (), prefixes );
84+ addPrefixProposals (adHocIndexProvider .getIndex (project ), prefixes );
85+
86+ return new ArrayList <>(prefixes );
87+ }
88+
89+ private void addPropertyProposals (FuzzyMap <PropertyInfo > properties , String prefix , List <AnnotationAttributeProposal > result ) {
90+ properties .forEach (propertyInfo -> {
91+ String propID = propertyInfo .getId ();
92+
93+ if (prefix != null ) {
94+ if (prefix .length () > 0
95+ && prefix .length () < propID .length ()
96+ && propID .startsWith (prefix )) {
97+
98+ String remainingValue = propID .substring (prefix .length () + 1 );
99+ result .add (new AnnotationAttributeProposal (propID , propID , remainingValue ));
100+ }
101+ }
102+ else {
103+ result .add (new AnnotationAttributeProposal (propID ));
104+ }
105+ });
106+ }
107+
108+ private void addPrefixProposals (FuzzyMap <PropertyInfo > properties , Set <AnnotationAttributeProposal > prefixes ) {
109+ properties .forEach (propertyInfo -> {
110+ String prefix = getPrefix (propertyInfo .getId ());
111+ while (prefix != null ) {
112+ prefixes .add (new AnnotationAttributeProposal (prefix ));
113+ prefix = getPrefix (prefix );
114+ }
115+ });
116+ }
117+
118+ private String getPrefix (String key ) {
119+ int index = key .lastIndexOf ('.' );
120+ if (index >= 0 ) {
121+ return key .substring (0 , index );
122+ }
123+ else {
124+ return null ;
125+ }
126+ }
127+
128+ private String getPrefixAttributeValue (ASTNode node ) {
129+ ASTNode annotationNode = ASTUtils .getNearestAnnotationParent (node );
130+ if (annotationNode != null && annotationNode instanceof NormalAnnotation ) {
131+ NormalAnnotation annotation = (NormalAnnotation ) annotationNode ;
132+
133+ List <?> values = annotation .values ();
134+ for (Object value : values ) {
135+ if (value instanceof MemberValuePair ) {
136+ MemberValuePair valuePair = (MemberValuePair ) value ;
137+ String valuePairName = valuePair .getName () != null ? valuePair .getName ().toString () : null ;
138+
139+ if (valuePairName != null && "prefix" .equals (valuePairName )
140+ && valuePair .getValue () != null && valuePair .getValue () instanceof StringLiteral ) {
141+ StringLiteral prefixLiteral = (StringLiteral ) valuePair .getValue ();
142+ String valuePairValue = prefixLiteral .getLiteralValue ();
143+ return valuePairValue ;
144+ }
145+ }
146+
147+ }
148+ }
149+
150+ return null ;
151+ }
152+
153+ }
0 commit comments