Skip to content

Commit 0dfd858

Browse files
committed
Choice EIP short implementation
1 parent 050cde0 commit 0dfd858

File tree

5 files changed

+272
-5
lines changed

5 files changed

+272
-5
lines changed

src/main/java/com/github/cameltooling/lsp/internal/CamelTextDocumentService.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
import java.util.Collection;
2020
import java.util.Collections;
2121
import java.util.HashMap;
22+
import java.util.LinkedList;
2223
import java.util.List;
2324
import java.util.Map;
2425
import java.util.concurrent.CompletableFuture;
26+
import java.util.stream.Collectors;
2527

2628
import com.github.cameltooling.lsp.internal.completion.modeline.CamelKModelineInsertionProcessor;
29+
import com.github.cameltooling.lsp.internal.parser.CamelEIPParser;
2730
import com.github.cameltooling.lsp.internal.parser.CamelKModelineInsertionParser;
2831
import org.apache.camel.catalog.CamelCatalog;
2932
import org.apache.camel.catalog.DefaultCamelCatalog;
@@ -161,15 +164,34 @@ public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completio
161164
TextDocumentItem textDocumentItem = openedDocuments.get(uri);
162165

163166
if (textDocumentItem != null) {
164-
if (uri.endsWith(".properties")){
167+
if (uri.endsWith(".properties")) {
165168
return new CamelPropertiesCompletionProcessor(textDocumentItem, getCamelCatalog(), getCamelKafkaConnectorManager()).getCompletions(completionParams.getPosition(), getSettingsManager(), getKameletsCatalogManager()).thenApply(Either::forLeft);
166-
} else if (new CamelKModelineInsertionParser(textDocumentItem).canPutCamelKModeline(completionParams.getPosition())){
167-
return new CamelKModelineInsertionProcessor(textDocumentItem).getCompletions().thenApply(Either::forLeft);
169+
}
170+
171+
List<CompletableFuture<List<CompletionItem>>> completions = new LinkedList<>();
172+
173+
if (new CamelEIPParser(textDocumentItem).canPutEIP(completionParams.getPosition())){
174+
completions.add(new CamelEIPParser(textDocumentItem).getCompletions());
175+
}
176+
177+
if (new CamelKModelineInsertionParser(textDocumentItem).canPutCamelKModeline(completionParams.getPosition())){
178+
completions.add(new CamelKModelineInsertionProcessor(textDocumentItem).getCompletions());
168179
} else if (new CamelKModelineParser().isOnCamelKModeline(completionParams.getPosition().getLine(), textDocumentItem)){
169-
return new CamelKModelineCompletionprocessor(textDocumentItem, getCamelCatalog()).getCompletions(completionParams.getPosition()).thenApply(Either::forLeft);
180+
completions.add(new CamelKModelineCompletionprocessor(textDocumentItem, getCamelCatalog()).getCompletions(completionParams.getPosition()));
170181
} else {
171-
return new CamelEndpointCompletionProcessor(textDocumentItem, getCamelCatalog(), getKameletsCatalogManager()).getCompletions(completionParams.getPosition(), getSettingsManager()).thenApply(Either::forLeft);
182+
completions.add(new CamelEndpointCompletionProcessor(textDocumentItem, getCamelCatalog(), getKameletsCatalogManager()).getCompletions(completionParams.getPosition(), getSettingsManager()));
172183
}
184+
185+
//Combining all completable futures completion lists
186+
//Source: https://nurkiewicz.com/2013/05/java-8-completablefuture-in-action.html
187+
return CompletableFuture
188+
.allOf(completions.toArray(new CompletableFuture[completions.size()]))
189+
.thenApply(
190+
v -> completions.stream()
191+
.map(future -> future.join())
192+
.flatMap(x -> x.stream())
193+
.collect(Collectors.toList())
194+
).thenApply(Either::forLeft);
173195
} else {
174196
LOGGER.warn("The document with uri {} has not been found in opened documents. Cannot provide completion.", uri);
175197
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.github.cameltooling.lsp.internal.parser;
2+
3+
import org.eclipse.lsp4j.CompletionItem;
4+
import org.eclipse.lsp4j.Position;
5+
import org.eclipse.lsp4j.TextDocumentItem;
6+
7+
import java.util.List;
8+
import java.util.concurrent.CompletableFuture;
9+
import java.util.regex.Pattern;
10+
11+
public class CamelEIPParser {
12+
13+
private final TextDocumentItem document;
14+
15+
public CamelEIPParser(TextDocumentItem textDocumentItem) {
16+
this.document = textDocumentItem;
17+
}
18+
19+
public boolean canPutEIP(Position position) {
20+
Pattern CHOICE_EIP_PATTERN = Pattern.compile("\\)\\s*.?c?h?o?i?c?e?\\(?$", Pattern.MULTILINE);
21+
ParserFileHelperUtil util = new ParserFileHelperUtil();
22+
String textUntilPosition = util.getTextUntilPosition(document, position);
23+
24+
return CHOICE_EIP_PATTERN.matcher(textUntilPosition).find();
25+
}
26+
27+
public CompletableFuture<List<CompletionItem>> getCompletions() {
28+
return CompletableFuture.completedFuture(
29+
List.of(choiceEIPcompletion())
30+
);
31+
}
32+
33+
private CompletionItem choiceEIPcompletion() {
34+
String newLine = System.getProperty("line.separator");
35+
CompletionItem completion = new CompletionItem("Content Based Router");
36+
completion.setDocumentation(
37+
"Read more: https://camel.apache.org/components/3.11.x/eips/content-based-router-eip.html"
38+
);
39+
completion.setInsertText(
40+
".choice()" + newLine +
41+
".when()" + newLine +
42+
".to()" + newLine +
43+
".when()" + newLine +
44+
".to()" + newLine +
45+
".otherwise()" + newLine +
46+
".to()"
47+
);
48+
49+
return completion;
50+
}
51+
}

src/main/java/com/github/cameltooling/lsp/internal/parser/ParserFileHelperUtil.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,27 @@ public String getLine(String text, int line) {
3636
}
3737
return null;
3838
}
39+
40+
public String getTextUntilPosition(TextDocumentItem document, Position position) {
41+
String newLine = System.getProperty("line.separator");
42+
String[] lines = document.getText().split(newLine);
43+
String textUntilPosition = "";
44+
45+
if(document.getText().startsWith(newLine)) {
46+
textUntilPosition += newLine;
47+
}
48+
49+
for(int i = 0; i<position.getLine(); i++) {
50+
textUntilPosition += lines[i] + newLine;
51+
}
52+
53+
if(position.getLine() != lines.length) {
54+
//Edge case: end of file with a character return
55+
textUntilPosition += lines[position.getLine()].substring(0, position.getCharacter());
56+
}
57+
58+
return textUntilPosition;
59+
60+
}
3961

4062
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.github.cameltooling.lsp.internal.completion.eip;
2+
3+
import com.github.cameltooling.lsp.internal.AbstractCamelLanguageServerTest;
4+
import com.github.cameltooling.lsp.internal.CamelLanguageServer;
5+
import com.github.cameltooling.lsp.internal.util.RouteTextBuilder;
6+
import org.eclipse.lsp4j.CompletionItem;
7+
import org.eclipse.lsp4j.CompletionList;
8+
import org.eclipse.lsp4j.Position;
9+
import org.eclipse.lsp4j.jsonrpc.messages.Either;
10+
import org.junit.jupiter.api.Test;
11+
12+
import java.util.List;
13+
import java.util.concurrent.CompletableFuture;
14+
import java.util.stream.Collectors;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
18+
19+
public class EIPChoiceCompletionTest extends AbstractCamelLanguageServerTest {
20+
21+
private static final Position BEGINNING_OF_FILE = new Position(0,0);
22+
23+
private static final String FROM_ROUTE = "from(\"timer:foo?period={{timer.period}}\")";
24+
25+
@Test
26+
void testProvideInsertionOnEmptyJavaFile() throws Exception {
27+
String contents = "";
28+
Position position = new Position(0,0);
29+
30+
List<CompletionItem> completionItems = getCompletionsFor(contents, position);
31+
completionItems = completionItems.stream().filter(
32+
completionItem -> completionItem.getLabel().startsWith("Content Based Router")
33+
).collect(Collectors.toList());
34+
35+
assertThat(completionItems).hasSize(0); //Removing camel-k modeline insertion. Refactor later
36+
}
37+
38+
@Test
39+
void testProvideInsertionOnEmptyJavaClass() throws Exception {
40+
RouteTextBuilder.BlueprintContentWithPosition blueprint =
41+
RouteTextBuilder.createJavaBlueprintClass("");
42+
43+
List<CompletionItem> completionItems = getCompletionsFor(blueprint.content, blueprint.position);
44+
completionItems = completionItems.stream().filter(
45+
completionItem -> completionItem.getLabel().startsWith("Content Based Router")
46+
).collect(Collectors.toList());
47+
48+
49+
assertThat(completionItems).hasSize(0);
50+
}
51+
52+
@Test
53+
void testProvideInsertionOnEmptyJavaCamelRoute() throws Exception {
54+
RouteTextBuilder.BlueprintContentWithPosition blueprint =
55+
RouteTextBuilder.createJavaBlueprintCamelRoute("");
56+
57+
List<CompletionItem> completionItems = getCompletionsFor(blueprint.content, blueprint.position);
58+
completionItems = completionItems.stream().filter(
59+
completionItem -> completionItem.getLabel().startsWith("Content Based Router")
60+
).collect(Collectors.toList());
61+
62+
assertThat(completionItems).hasSize(0);
63+
}
64+
65+
@Test
66+
void testProvideInsertionAfterFromOnCamelRoute() throws Exception {
67+
RouteTextBuilder.BlueprintContentWithPosition blueprint =
68+
RouteTextBuilder.createJavaBlueprintCamelRoute(FROM_ROUTE);
69+
70+
List<CompletionItem> completionItems = getCompletionsFor(blueprint.content, blueprint.position);
71+
completionItems = completionItems.stream().filter(
72+
completionItem -> completionItem.getLabel().startsWith("Content Based Router")
73+
).collect(Collectors.toList());
74+
75+
76+
assertThat(completionItems).hasSize(1);
77+
}
78+
79+
80+
private List<CompletionItem> getCompletionsFor(String contents, Position position) throws Exception {
81+
CamelLanguageServer camelLanguageServer = initializeLanguageServer(contents, ".java");
82+
83+
CompletableFuture<Either<List<CompletionItem>, CompletionList>> completions = getCompletionFor(
84+
camelLanguageServer, position);
85+
86+
return completions.get().getLeft();
87+
}
88+
}

src/test/java/com/github/cameltooling/lsp/internal/util/RouteTextBuilder.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@
1616
*/
1717
package com.github.cameltooling.lsp.internal.util;
1818

19+
import org.eclipse.lsp4j.Position;
20+
1921
public class RouteTextBuilder {
2022

2123
public static final String XML_PREFIX_FROM = "<from uri=\"";
2224
private static final String XML_SUFFIX_FROM_SPRING = "\" xmlns=\"http://camel.apache.org/schema/spring\"/>\n";
2325
private static final String XML_SUFFIX_FROM_BLUEPRINT = "\" xmlns=\"http://camel.apache.org/schema/blueprint\"/>\n";
26+
27+
private static final String JAVA_CLASS_BLUEPRINT_CAMEL_ROUTEBUILDER_IMPORT
28+
= "import org.apache.camel.builder.RouteBuilder;";
29+
30+
private static final String JAVA_CLASS_BLUEPRINT_CLASS_DECLARATION = "public class TestRoute";
31+
32+
private static final String JAVA_CLASS_BLUEPRINT_CAMEL_ROUTEBUILDER_EXTEMD = "extends RouteBuilder";
33+
34+
private static final String JAVA_CLASS_BLUEPRINT_CONFIGURE_METHOD_DECLARATION = "public void configure()";
35+
2436

2537
/**
2638
* @param camelUri
@@ -38,4 +50,76 @@ public static String createXMLBlueprintRoute(String camelUri) {
3850
return XML_PREFIX_FROM + camelUri + XML_SUFFIX_FROM_BLUEPRINT;
3951
}
4052

53+
/**
54+
* @param javaClassContent
55+
* @return builds an empty Java class with the specified content and the cursor position inside and after
56+
* contents
57+
*/
58+
public static BlueprintContentWithPosition createJavaBlueprintClass(String javaClassContent) {
59+
String newLine = System.getProperty("line.separator");
60+
String[] contentSplit = javaClassContent.split(newLine);
61+
int lineOffset = contentSplit.length;
62+
int characterOffset = contentSplit[contentSplit.length-1].length();
63+
64+
if (javaClassContent.startsWith(newLine)) {
65+
lineOffset += 1;
66+
}
67+
68+
if (javaClassContent.endsWith(newLine)) {
69+
lineOffset +=1;
70+
characterOffset = 0;
71+
}
72+
73+
return new BlueprintContentWithPosition(
74+
JAVA_CLASS_BLUEPRINT_CLASS_DECLARATION + newLine +
75+
"{" + newLine
76+
+ javaClassContent + newLine
77+
+ "}" + newLine
78+
, 2+lineOffset, characterOffset);
79+
}
80+
81+
/**
82+
* @param camelRoute
83+
* @return builds an empty Java class with the specified content and the cursor position placed inside configure
84+
* method and after content.
85+
*/
86+
public static BlueprintContentWithPosition createJavaBlueprintCamelRoute(String camelRoute) {
87+
String newLine = System.getProperty("line.separator");
88+
String[] contentSplit = camelRoute.split(newLine);
89+
int lineOffset = contentSplit.length - 1;
90+
int characterOffset = contentSplit[contentSplit.length-1].length();
91+
92+
if (camelRoute.startsWith(newLine)) {
93+
lineOffset += 1;
94+
}
95+
96+
if (camelRoute.endsWith(newLine)) {
97+
lineOffset +=1;
98+
characterOffset = 0;
99+
}
100+
101+
return new BlueprintContentWithPosition(
102+
JAVA_CLASS_BLUEPRINT_CAMEL_ROUTEBUILDER_IMPORT + newLine +
103+
JAVA_CLASS_BLUEPRINT_CLASS_DECLARATION + newLine +
104+
JAVA_CLASS_BLUEPRINT_CAMEL_ROUTEBUILDER_EXTEMD + newLine +
105+
"{" + newLine +
106+
camelRoute + newLine +
107+
"}" + newLine
108+
, 4 + lineOffset,characterOffset);
109+
}
110+
111+
public static class BlueprintContentWithPosition {
112+
public String content;
113+
public Position position;
114+
115+
public BlueprintContentWithPosition(String content, Position position) {
116+
this.content = content;
117+
this.position = position;
118+
}
119+
120+
public BlueprintContentWithPosition(String content, int line, int character) {
121+
this.content = content;
122+
this.position = new Position(line, character);
123+
}
124+
}
41125
}

0 commit comments

Comments
 (0)