diff --git a/headless-services/spring-boot-language-server/.classpath b/headless-services/spring-boot-language-server/.classpath index cb7220e87..5a94da1a3 100644 --- a/headless-services/spring-boot-language-server/.classpath +++ b/headless-services/spring-boot-language-server/.classpath @@ -34,19 +34,16 @@ - - - - - - - - - + + + + + + diff --git a/headless-services/spring-boot-language-server/pom.xml b/headless-services/spring-boot-language-server/pom.xml index 5a42bf130..4595a2f50 100644 --- a/headless-services/spring-boot-language-server/pom.xml +++ b/headless-services/spring-boot-language-server/pom.xml @@ -22,6 +22,17 @@ ${project.name}-repo file://${project.basedir}/repo + + + oss-jfrog-snapshots + https://oss.jfrog.org/artifactory/libs-snapshot + + + true + + @@ -78,12 +89,24 @@ commons-io ${commons-io-version} - - + + org.lsp4xml + org.eclipse.lsp4xml + 0.3.0-SNAPSHOT + + + org.eclipse.lsp4j + org.eclipse.lsp4j + + + org.eclipse.lsp4j + org.eclipse.lsp4j.jsonrpc + + + + + org.springframework.boot diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/DocumentUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/DocumentUtils.java new file mode 100644 index 000000000..dbd822582 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/DocumentUtils.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2019 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.utils; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.atomic.AtomicReference; + +import org.springframework.ide.vscode.commons.util.text.LanguageId; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +public class DocumentUtils { + + public static TextDocument getTempTextDocument(String docURI, AtomicReference docRef, String content) throws Exception { + TextDocument doc = docRef.get(); + if (doc == null) { + doc = createTempTextDocument(docURI, content); + docRef.set(doc); + } + return doc; + } + + private static TextDocument createTempTextDocument(String docURI, String content) throws Exception { + if (content == null) { + Path path = Paths.get(new URI(docURI)); + content = new String(Files.readAllBytes(path)); + } + + TextDocument doc = new TextDocument(docURI, LanguageId.PLAINTEXT, 0, content); + return doc; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java index 8a0d76076..a66b44820 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java @@ -11,9 +11,7 @@ package org.springframework.ide.vscode.boot.java.utils; import java.io.File; -import java.net.URI; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.List; @@ -46,7 +44,6 @@ import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.UriUtil; -import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; /** @@ -222,7 +219,7 @@ public class SpringIndexerJava implements SpringIndexer { private void extractSymbolInformation(IJavaProject project, TypeDeclaration typeDeclaration, String docURI, AtomicReference docRef, String content) throws Exception { Collection providers = symbolProviders.getAll(); if (!providers.isEmpty()) { - TextDocument doc = getTempTextDocument(docURI, docRef, content); + TextDocument doc = DocumentUtils.getTempTextDocument(docURI, docRef, content); for (SymbolProvider provider : providers) { Collection sbls = provider.getSymbols(typeDeclaration, doc); if (sbls != null) { @@ -237,7 +234,7 @@ public class SpringIndexerJava implements SpringIndexer { private void extractSymbolInformation(IJavaProject project, MethodDeclaration methodDeclaration, String docURI, AtomicReference docRef, String content) throws Exception { Collection providers = symbolProviders.getAll(); if (!providers.isEmpty()) { - TextDocument doc = getTempTextDocument(docURI, docRef, content); + TextDocument doc = DocumentUtils.getTempTextDocument(docURI, docRef, content); for (SymbolProvider provider : providers) { Collection sbls = provider.getSymbols(methodDeclaration, doc); if (sbls != null) { @@ -256,7 +253,7 @@ public class SpringIndexerJava implements SpringIndexer { Collection providers = symbolProviders.get(typeBinding); Collection metaAnnotations = AnnotationHierarchies.getMetaAnnotations(typeBinding, symbolProviders::containsKey); if (!providers.isEmpty()) { - TextDocument doc = getTempTextDocument(docURI, docRef, content); + TextDocument doc = DocumentUtils.getTempTextDocument(docURI, docRef, content); for (SymbolProvider provider : providers) { Collection sbls = provider.getSymbols(node, typeBinding, metaAnnotations, doc); if (sbls != null) { @@ -280,7 +277,7 @@ public class SpringIndexerJava implements SpringIndexer { if (type != null) { String qualifiedName = type.getQualifiedName(); if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) { - TextDocument doc = getTempTextDocument(docURI, docRef, content); + TextDocument doc = DocumentUtils.getTempTextDocument(docURI, docRef, content); return DefaultSymbolProvider.provideDefaultSymbol(node, doc); } } @@ -301,23 +298,4 @@ public class SpringIndexerJava implements SpringIndexer { .toArray(String[]::new); } - private TextDocument getTempTextDocument(String docURI, AtomicReference docRef, String content) throws Exception { - TextDocument doc = docRef.get(); - if (doc == null) { - doc = createTempTextDocument(docURI, content); - docRef.set(doc); - } - return doc; - } - - private TextDocument createTempTextDocument(String docURI, String content) throws Exception { - if (content == null) { - Path path = Paths.get(new URI(docURI)); - content = new String(Files.readAllBytes(path)); - } - - TextDocument doc = new TextDocument(docURI, LanguageId.PLAINTEXT, 0, content); - return doc; - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java index 116cddb8f..34e786e47 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXML.java @@ -10,27 +10,23 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.events.Characters; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; - +import org.apache.commons.io.FileUtils; +import org.eclipse.lsp4xml.dom.DOMDocument; +import org.eclipse.lsp4xml.dom.DOMNode; +import org.eclipse.lsp4xml.dom.DOMParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.UriUtil; +import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert @@ -39,11 +35,11 @@ public class SpringIndexerXML implements SpringIndexer { private static final Logger log = LoggerFactory.getLogger(SpringIndexerJava.class); - private final SymbolHandler handler; + private final SymbolHandler symbolHandler; private final Map namespaceHandler; public SpringIndexerXML(SymbolHandler handler, Map namespaceHandler) { - this.handler = handler; + this.symbolHandler = handler; this.namespaceHandler = namespaceHandler; } @@ -70,7 +66,7 @@ public class SpringIndexerXML implements SpringIndexer { @Override public void updateFile(IJavaProject project, String docURI, String content) throws Exception { - scanFile(project, new ByteArrayInputStream(content.getBytes()), docURI); + scanFile(project, content, docURI); } private void scanProject(IJavaProject project, String[] files) { @@ -79,52 +75,55 @@ public class SpringIndexerXML implements SpringIndexer { } } - private void scanFile(IJavaProject project, String file) { - log.debug("starting to parse XML file for Spring symbol indexing: ", file); + private void scanFile(IJavaProject project, String fileName) { + log.debug("starting to parse XML file for Spring symbol indexing: ", fileName); try { - String docURI = UriUtil.toUri(new File(file)).toString(); + File file = new File(fileName); - InputStream xmlInputStream = new FileInputStream(file); - scanFile(project, xmlInputStream, docURI); + String docURI = UriUtil.toUri(file).toString(); + String fileContent = FileUtils.readFileToString(file); + + scanFile(project, fileContent, docURI); } catch (Exception e) { log.error("error parsing XML file: ", e); } } - private void scanFile(IJavaProject project, InputStream xmlInput, String docURI) throws Exception { - XMLInputFactory inputFactory = XMLInputFactory.newInstance(); - XMLEventReader eventReader = inputFactory.createXMLEventReader(xmlInput); + private void scanFile(IJavaProject project, String fileContent, String docURI) throws Exception { + DOMParser parser = DOMParser.getInstance(); + DOMDocument document = parser.parse(fileContent, "", null); - while (eventReader.hasNext()) { - XMLEvent event = eventReader.nextEvent(); - switch (event.getEventType()) { - - case XMLEvent.START_ELEMENT: - StartElement startElement = event.asStartElement(); - - String namespaceURI = startElement.getName().getNamespaceURI(); - if (namespaceURI != null && this.namespaceHandler.containsKey(namespaceURI)) { - SpringIndexerXMLNamespaceHandler namespaceHandler = this.namespaceHandler.get(namespaceURI); - namespaceHandler.processStartElement(project, docURI, startElement, this.handler); - } - - break; - - case XMLEvent.CHARACTERS: - Characters characters = event.asCharacters(); - log.debug(characters.getData()); - break; - case XMLEvent.END_ELEMENT: - EndElement endElement = event.asEndElement(); - log.debug(""); - break; - default: - // do nothing - break; - } - } + AtomicReference docRef = new AtomicReference<>(); + scanNode(document, project, docURI, docRef, fileContent); } + private void scanNode(DOMNode node, IJavaProject project, String docURI, AtomicReference docRef, String content) throws Exception { + String namespaceURI = node.getNamespaceURI(); + + if (namespaceURI != null && this.namespaceHandler.containsKey(namespaceURI)) { + SpringIndexerXMLNamespaceHandler namespaceHandler = this.namespaceHandler.get(namespaceURI); + + TextDocument document = DocumentUtils.getTempTextDocument(docURI, docRef, content); + namespaceHandler.processNode(node, project, docURI, document, this.symbolHandler); + } + + +// if ("http://www.springframework.org/schema/beans".equals(namespaceURI)) { +// List attributeNodes = node.getAttributeNodes(); +// if (attributeNodes != null) { +// for (DOMAttr attribute : attributeNodes) { +// System.out.println(attribute.getName() + " - " + attribute.getValue()); +// } +// } +// } + + List children = node.getChildren(); + for (DOMNode child : children) { + scanNode(child, project, docURI, docRef, content); + } + + + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandler.java index de06859a9..cd020a079 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandler.java @@ -10,15 +10,15 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; -import javax.xml.stream.events.StartElement; - +import org.eclipse.lsp4xml.dom.DOMNode; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert */ public interface SpringIndexerXMLNamespaceHandler { - void processStartElement(IJavaProject project, String docURI, StartElement startElement, SymbolHandler handler); + void processNode(DOMNode node, IJavaProject project, String docURI, TextDocument document, SymbolHandler symbolHandler) throws Exception; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java index 2a356b62d..e92851c0b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerXMLNamespaceHandlerBeans.java @@ -10,18 +10,18 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; -import java.util.Iterator; - -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.StartElement; +import java.util.List; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; +import org.eclipse.lsp4xml.dom.DOMAttr; +import org.eclipse.lsp4xml.dom.DOMNode; import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.text.TextDocument; /** * @author Martin Lippert @@ -29,24 +29,28 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; public class SpringIndexerXMLNamespaceHandlerBeans implements SpringIndexerXMLNamespaceHandler { @Override - public void processStartElement(IJavaProject project, String docURI, StartElement startElement, SymbolHandler handler) { - String localPart = startElement.getName().getLocalPart(); - if (localPart != null && "bean".equals(localPart)) { - createBeanSymbol(project, docURI, startElement, handler); + public void processNode(DOMNode node, IJavaProject project, String docURI, TextDocument document, SymbolHandler symbolHandler) throws Exception { + String localName = node.getLocalName(); + if (localName != null && "bean".equals(localName)) { + createBeanSymbol(node, project, docURI, document, symbolHandler); } } - private void createBeanSymbol(IJavaProject project, String docURI, StartElement startElement, SymbolHandler handler) { + private void createBeanSymbol(DOMNode node, IJavaProject project, String docURI, TextDocument document, SymbolHandler symbolHandler) throws Exception { String beanID = null; + int beanIDStart = 0; + int beanIDEnd = 0; + String beanClass = null; - Iterator attributes = startElement.getAttributes(); - while (attributes.hasNext()) { - Attribute attribute = (Attribute) attributes.next(); + List attributes = node.getAttributeNodes(); + for (DOMAttr attribute : attributes) { - String name = attribute.getName().getLocalPart(); + String name = attribute.getName(); if (name != null && name.equals("id")) { beanID = attribute.getValue(); + beanIDStart = attribute.getStart(); + beanIDEnd = attribute.getEnd(); } else if (name != null && name.equals("class")) { String value = attribute.getValue(); @@ -55,9 +59,15 @@ public class SpringIndexerXMLNamespaceHandlerBeans implements SpringIndexerXMLNa } if (beanID != null && beanClass != null) { + int lineStart = document.getLineOfOffset(beanIDStart); + int lineEnd = document.getLineOfOffset(beanIDEnd); + + int startInLine = beanIDStart - document.getLineOffset(lineStart); + int endInLine = beanIDEnd - document.getLineOffset(lineEnd); + Range range = new Range(); - range.setStart(new Position(startElement.getLocation().getLineNumber(), startElement.getLocation().getColumnNumber())); - range.setEnd(new Position(startElement.getLocation().getLineNumber(), startElement.getLocation().getColumnNumber() + 1)); + range.setStart(new Position(lineStart + 1, startInLine)); + range.setEnd(new Position(lineEnd + 1, endInLine)); Location location = new Location(); location.setUri(docURI); @@ -66,7 +76,7 @@ public class SpringIndexerXMLNamespaceHandlerBeans implements SpringIndexerXMLNa SymbolInformation symbol = new SymbolInformation("@+ '" + beanID + "' " + beanClass, SymbolKind.Interface, new Location(docURI, range)); EnhancedSymbolInformation fullSymbol = new EnhancedSymbolInformation(symbol, null); - handler.addSymbol(project, docURI, fullSymbol); + symbolHandler.addSymbol(project, docURI, fullSymbol); } } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java index 5cc57a32f..e4d3f918b 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/SpringIndexerXMLProjectTest.java @@ -73,9 +73,9 @@ public class SpringIndexerXMLProjectTest { assertEquals(3, allSymbols.size()); String docUri = directory.toPath().resolve("config/simple-spring-config.xml").toUri().toString(); - assertTrue(containsSymbol(allSymbols, "@+ 'transactionManager' DataSourceTransactionManager", docUri, 7, 143, 7, 144)); - assertTrue(containsSymbol(allSymbols, "@+ 'jdbcTemplate' JdbcTemplate", docUri, 9, 84, 9, 85)); - assertTrue(containsSymbol(allSymbols, "@+ 'namedParameterJdbcTemplate' NamedParameterJdbcTemplate", docUri, 14, 91, 14, 92)); + assertTrue(containsSymbol(allSymbols, "@+ 'transactionManager' DataSourceTransactionManager", docUri, 7, 14, 7, 37)); + assertTrue(containsSymbol(allSymbols, "@+ 'jdbcTemplate' JdbcTemplate", docUri, 9, 14, 9, 31)); + assertTrue(containsSymbol(allSymbols, "@+ 'namedParameterJdbcTemplate' NamedParameterJdbcTemplate", docUri, 13, 14, 13, 45)); } private boolean containsSymbol(List symbols, String name, String uri, int startLine, int startCHaracter, int endLine, int endCharacter) {