下面列出了怎么用org.eclipse.lsp4j.DocumentSymbol的API类实例代码及写法,或者点击链接到github查看源代码。
public static void testDocumentSymbolsFor(String xml, String fileURI, XMLSymbolSettings symbolSettings,
DocumentSymbol... expected) {
TextDocument document = new TextDocument(xml, fileURI != null ? fileURI : "test.xml");
XMLLanguageService xmlLanguageService = new XMLLanguageService();
ContentModelSettings settings = new ContentModelSettings();
settings.setUseCache(false);
xmlLanguageService.doSave(new SettingsSaveContext(settings));
DOMDocument xmlDocument = DOMParser.getInstance().parse(document,
xmlLanguageService.getResolverExtensionManager());
xmlLanguageService.setDocumentProvider((uri) -> xmlDocument);
List<DocumentSymbol> actual = xmlLanguageService.findDocumentSymbols(xmlDocument, symbolSettings);
assertDocumentSymbols(actual, expected);
}
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> getDocumentSymbols() {
return CompletableFuture.supplyAsync(() -> {
if (textDocumentItem.getUri().endsWith(".xml")) {
try {
List<Either<SymbolInformation, DocumentSymbol>> symbolInformations = new ArrayList<>();
NodeList routeNodes = parserFileHelper.getRouteNodes(textDocumentItem);
if (routeNodes != null) {
symbolInformations.addAll(convertToSymbolInformation(routeNodes));
}
NodeList camelContextNodes = parserFileHelper.getCamelContextNodes(textDocumentItem);
if (camelContextNodes != null) {
symbolInformations.addAll(convertToSymbolInformation(camelContextNodes));
}
return symbolInformations;
} catch (Exception e) {
LOGGER.error(CANNOT_DETERMINE_DOCUMENT_SYMBOLS, e);
}
}
return Collections.emptyList();
});
}
@Test
void testNoExceptionWithJavaFile() throws Exception {
final TestLogAppender appender = new TestLogAppender();
final Logger logger = Logger.getRootLogger();
logger.addAppender(appender);
File f = new File("src/test/resources/workspace/camel.java");
try (FileInputStream fis = new FileInputStream(f)) {
CamelLanguageServer camelLanguageServer = initializeLanguageServer(fis, ".java");
CompletableFuture<List<Either<SymbolInformation,DocumentSymbol>>> documentSymbolFor = getDocumentSymbolFor(camelLanguageServer);
List<Either<SymbolInformation, DocumentSymbol>> symbolsInformation = documentSymbolFor.get();
assertThat(symbolsInformation).isEmpty();
for (LoggingEvent loggingEvent : appender.getLog()) {
if (loggingEvent.getMessage() != null) {
assertThat((String)loggingEvent.getMessage()).doesNotContain(DocumentSymbolProcessor.CANNOT_DETERMINE_DOCUMENT_SYMBOLS);
}
}
}
}
@Override
public DocumentSymbol toDocumentSymbol(EObject object) {
SymbolKind symbolKind = kindCalculationHelper.getSymbolKind(object);
if (symbolKind == null) {
return null;
}
DocumentSymbol documentSymbol = super.toDocumentSymbol(object);
documentSymbol.setKind(symbolKind);
String symbolLabel = labelHelper.getSymbolLabel(object);
if (symbolLabel != null) {
documentSymbol.setName(symbolLabel);
}
return documentSymbol;
}
@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(DocumentSymbolParams params) {
JavaSource js = getSource(params.getTextDocument().getUri());
List<Either<SymbolInformation, DocumentSymbol>> result = new ArrayList<>();
try {
js.runUserActionTask(cc -> {
cc.toPhase(JavaSource.Phase.RESOLVED);
for (Element tel : cc.getTopLevelElements()) {
DocumentSymbol ds = element2DocumentSymbol(cc, tel);
if (ds != null)
result.add(Either.forRight(ds));
}
}, true);
} catch (IOException ex) {
//TODO: include stack trace:
client.logMessage(new MessageParams(MessageType.Error, ex.getMessage()));
}
return CompletableFuture.completedFuture(result);
}
private DocumentSymbol element2DocumentSymbol(CompilationInfo info, Element el) throws BadLocationException {
TreePath path = info.getTrees().getPath(el);
if (path == null)
return null;
long start = info.getTrees().getSourcePositions().getStartPosition(path.getCompilationUnit(), path.getLeaf());
long end = info.getTrees().getSourcePositions().getEndPosition(path.getCompilationUnit(), path.getLeaf());
if (end == (-1))
return null;
Range range = new Range(createPosition(info.getCompilationUnit(), (int) start),
createPosition(info.getCompilationUnit(), (int) end));
List<DocumentSymbol> children = new ArrayList<>();
for (Element c : el.getEnclosedElements()) {
DocumentSymbol ds = element2DocumentSymbol(info, c);
if (ds != null) {
children.add(ds);
}
}
return new DocumentSymbol(el.getSimpleName().toString(), elementKind2SymbolKind(el.getKind()), range, range, null, children);
}
@Test
public void testDocumentSymbol() throws Exception {
when(preferenceManager.getClientPreferences().isHierarchicalDocumentSymbolSupported()).thenReturn(Boolean.TRUE);
URI fileURI = openFile("maven/salut4", "src/main/java/java/Foo.java");
TextDocumentIdentifier identifier = new TextDocumentIdentifier(fileURI.toString());
DocumentSymbolParams params = new DocumentSymbolParams(identifier);
List<Either<SymbolInformation, DocumentSymbol>> result = server.documentSymbol(params).join();
assertNotNull(result);
assertEquals(2, result.size());
Either<SymbolInformation, DocumentSymbol> symbol = result.get(0);
assertTrue(symbol.isRight());
assertEquals("java", symbol.getRight().getName());
assertEquals(SymbolKind.Package, symbol.getRight().getKind());
symbol = result.get(1);
assertTrue(symbol.isRight());
assertEquals("Foo", symbol.getRight().getName());
assertEquals(SymbolKind.Class, symbol.getRight().getKind());
List<DocumentSymbol> children = symbol.getRight().getChildren();
assertNotNull(children);
assertEquals(1, children.size());
assertEquals("main(String[])", children.get(0).getName());
assertEquals(SymbolKind.Method, children.get(0).getKind());
}
/**
* {@code false} if the argument is {@code null} or any of the {@link NonNull} properties are {@code null}.
* Otherwise, {@code true}.
*/
public static boolean isValid(DocumentSymbol symbol) {
if (symbol != null) {
for (Field field : DocumentSymbol.class.getDeclaredFields()) {
for (Annotation annotation : field.getAnnotations()) {
if (NonNull.class == annotation.annotationType()) {
field.setAccessible(true);
try {
Object o = field.get(symbol);
if (o == null) {
return false;
}
} catch (Throwable e) {
throw Exceptions.sneakyThrow(e);
}
}
}
}
return true;
}
return false;
}
public List<Either<SymbolInformation, DocumentSymbol>> getSymbols(XtextResource resource,
CancelIndicator cancelIndicator) {
String uri = uriExtensions.toUriString(resource.getURI());
ArrayList<SymbolInformation> infos = new ArrayList<>();
List<DocumentSymbol> rootSymbols = Lists
.transform(hierarchicalDocumentSymbolService.getSymbols(resource, cancelIndicator), Either::getRight);
for (DocumentSymbol rootSymbol : rootSymbols) {
Iterable<DocumentSymbol> symbols = Traverser.forTree(DocumentSymbol::getChildren)
.depthFirstPreOrder(rootSymbol);
Function1<? super DocumentSymbol, ? extends String> containerNameProvider = (DocumentSymbol symbol) -> {
DocumentSymbol firstSymbol = IterableExtensions.findFirst(symbols, (DocumentSymbol it) -> {
return it != symbol && !IterableExtensions.isNullOrEmpty(it.getChildren())
&& it.getChildren().contains(symbol);
});
if (firstSymbol != null) {
return firstSymbol.getName();
}
return null;
};
for (DocumentSymbol s : symbols) {
infos.add(createSymbol(uri, s, containerNameProvider));
}
}
return Lists.transform(infos, Either::forLeft);
}
/**
* Converts the {@code EObject} argument into a {@link DocumentSymbol document symbol} without the
* {@link DocumentSymbol#children children} information filled in.
*/
public DocumentSymbol toDocumentSymbol(EObject object) {
DocumentSymbol documentSymbol = new DocumentSymbol();
String objectName = nameProvider.getName(object);
if (objectName != null) {
documentSymbol.setName(objectName);
}
SymbolKind objectKind = kindProvider.getSymbolKind(object);
if (objectKind != null) {
documentSymbol.setKind(objectKind);
}
Range objectRange = rangeProvider.getRange(object);
if (objectRange != null) {
documentSymbol.setRange(objectRange);
}
Range objectSelectionRange = rangeProvider.getSelectionRange(object);
if (objectSelectionRange != null) {
documentSymbol.setSelectionRange(objectSelectionRange);
}
documentSymbol.setDetail(detailsProvider.getDetails(object));
documentSymbol.setDeprecated(deprecationInfoProvider.isDeprecated(object));
documentSymbol.setChildren(new ArrayList<>());
return documentSymbol;
}
@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(
DocumentSymbolParams params) {
URI uri = URI.create(params.getTextDocument().getUri());
recompileIfContextChanged(uri);
DocumentSymbolProvider provider = new DocumentSymbolProvider(astVisitor);
return provider.provideDocumentSymbols(params.getTextDocument());
}
@Test
public void exceedSymbolLimit() {
String xml = "<?xml version = \"1.0\"?>\r\n" + //
"<!DOCTYPE Folks [\r\n" + //
" <!ELEMENT Folks (Person*)>\r\n" + //
" <!ELEMENT Person (Name,Email?)>\r\n" + //
" <!ATTLIST Person Pin ID #REQUIRED>\r\n" + //
" <!ATTLIST Person Friend IDREF #IMPLIED>\r\n" + //
" <!ATTLIST Person Likes IDREFS #IMPLIED>\r\n" + //
" <!ELEMENT Name (#PCDATA)>\r\n" + //
" <!ELEMENT Email (#PCDATA)>\r\n" + //
" ]>\r\n" + //
"<Folks>\r\n" + //
" \r\n" + //
"</Folks>";
DocumentSymbol symbol1 = ds("xml", SymbolKind.Property, r(0, 0, 0, 23), r(0, 0, 0, 23), null, //
Collections.emptyList());
DocumentSymbol symbol2 = ds("DOCTYPE:Folks", SymbolKind.Struct, r(1, 0, 9, 3), r(1, 0, 9, 3), null,
Arrays.asList(
ds("Folks", SymbolKind.Property, r(2, 1, 2, 27), r(2, 1, 2, 27), null, Collections.emptyList()), //
ds("Person", SymbolKind.Property, r(3, 1, 3, 32), r(3, 1, 3, 32), null, //
Arrays.asList( //
ds("Pin", SymbolKind.Key, r(4, 18, 4, 21), r(4, 18, 4, 21), null, Collections.emptyList()), //
ds("Friend", SymbolKind.Key, r(5, 18, 5, 24), r(5, 18, 5, 24), null, Collections.emptyList()), //
ds("Likes", SymbolKind.Key, r(6, 18, 6, 23), r(6, 18, 6, 23), null, Collections.emptyList()))), //
ds("Name", SymbolKind.Property, r(7, 1, 7, 26), r(7, 1, 7, 26), null, Collections.emptyList()), //
ds("Email", SymbolKind.Property, r(8, 1, 8, 27), r(8, 1, 8, 27), null, Collections.emptyList())));
DocumentSymbol symbol3 = ds("Folks", SymbolKind.Field, r(10, 0, 12, 8), r(10, 0, 12, 8), null, Collections.emptyList());
XMLSymbolSettings settings = new XMLSymbolSettings();
settings.setMaxItemsComputed(10);
XMLAssert.testDocumentSymbolsFor(xml, "test.xml", settings, symbol1, symbol2, symbol3);
settings.setMaxItemsComputed(15);
XMLAssert.testDocumentSymbolsFor(xml, "test.xml", settings, symbol1, symbol2, symbol3);
settings.setMaxItemsComputed(9);
XMLAssert.testDocumentSymbolsFor(xml, "test.xml", settings, symbol1, symbol2);
}
private List<Either<SymbolInformation, DocumentSymbol>> convertToSymbolInformation(NodeList routeNodes) {
List<Either<SymbolInformation, DocumentSymbol>> res = new ArrayList<>();
for (int i = 0; i < routeNodes.getLength(); i++) {
Node routeNode = routeNodes.item(i);
Location location = parserFileHelper.retrieveLocation(routeNode, textDocumentItem);
String displayNameOfSymbol = computeDisplayNameOfSymbol(routeNode);
res.add(Either.forLeft(new SymbolInformation(displayNameOfSymbol, SymbolKind.Field, location)));
}
return res;
}
@Test
void testRoutesProvidedAsDocumentSymbol() throws Exception {
String textTotest =
"<camelContext id=\"camel\" xmlns=\"http://camel.apache.org/schema/spring\">\r\n" +
"\r\n" +
" <route id=\"a route\">\r\n" +
" <from uri=\"direct:cafe\"/>\r\n" +
" <split>\r\n" +
" <method bean=\"orderSplitter\"/>\r\n" +
" <to uri=\"direct:drink\"/>\r\n" +
" </split>\r\n" +
" </route>\r\n" +
"\r\n" +
" <route id=\"another Route\">\r\n" +
" <from uri=\"direct:drink\"/>\r\n" +
" <recipientList>\r\n" +
" <method bean=\"drinkRouter\"/>\r\n" +
" </recipientList>\r\n" +
" </route>\n"
+ "</camelContext>\n";
List<Either<SymbolInformation, DocumentSymbol>> documentSymbols = testRetrieveDocumentSymbol(textTotest, 3);
SymbolInformation firstRoute = documentSymbols.get(0).getLeft();
assertThat(firstRoute.getName()).isEqualTo("a route");
Position expectedStart = new Position(2, 24/* expecting 4 but seems a bug in Camel*/);
Position expectedEnd = new Position(8, 12);
assertThat(firstRoute.getLocation()).usingRecursiveComparison().isEqualTo(new Location(DUMMY_URI+".xml", new Range(expectedStart, expectedEnd)));
}
private List<Either<SymbolInformation, DocumentSymbol>> testRetrieveDocumentSymbol(String textTotest, int expectedSize) throws URISyntaxException, InterruptedException, ExecutionException {
CamelLanguageServer camelLanguageServer = initializeLanguageServer(textTotest);
CompletableFuture<List<Either<SymbolInformation,DocumentSymbol>>> documentSymbolFor = getDocumentSymbolFor(camelLanguageServer);
List<Either<SymbolInformation, DocumentSymbol>> symbolsInformation = documentSymbolFor.get();
assertThat(symbolsInformation).hasSize(expectedSize);
return symbolsInformation;
}
@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(
DocumentSymbolParams params) {
URI uri = getURI(params.getTextDocument());
return openFilesManager.runInOpenFileContext(uri, "documentSymbol", (ofc, ci) -> {
return documentSymbol(ofc, params, ci);
});
}
/**
* Compute the symbol information. Executed in a read request.
*/
protected List<Either<SymbolInformation, DocumentSymbol>> documentSymbol(OpenFileContext ofc,
DocumentSymbolParams params, CancelIndicator cancelIndicator) {
URI uri = ofc.getURI();
IDocumentSymbolService documentSymbolService = getIDocumentSymbolService(getResourceServiceProvider(uri));
if ((documentSymbolService == null)) {
return Collections.emptyList();
}
XtextResource res = ofc.getResource();
XDocument doc = ofc.getDocument();
return documentSymbolService.getSymbols(doc, res, params, cancelIndicator);
}
@Override
public List<Either<SymbolInformation, DocumentSymbol>> getSymbols(XtextResource resource,
CancelIndicator cancelIndicator) {
List<Either<SymbolInformation, DocumentSymbol>> result = new ArrayList<>();
for (EObject content : resource.getContents()) {
if (content instanceof JSONDocument) {
JSONDocument document = (JSONDocument) content;
JSONValue rootValue = document.getContent();
getSymbols(rootValue, symbol -> result.add(Either.forRight(symbol)), cancelIndicator);
}
}
return result;
}
private String toString(DocumentSymbol sym) {
return sym.getKind().toString() + ":" +
sym.getName() + ":" +
sym.getRange() + ":" +
sym.getChildren()
.stream()
.map(this::toString)
.collect(Collectors.joining(", ", "(", ")"));
}
public NodeImpl(String currentFileUri, Either<SymbolInformation, DocumentSymbol> symbol) {
super(createChildren(currentFileUri, symbol));
if (symbol.isLeft()) {
setDisplayName(symbol.getLeft().getName());
setIconBaseWithExtension(Icons.getSymbolIconBase(symbol.getLeft().getKind()));
this.open = createOpenAction(symbol.getLeft().getLocation().getUri(), symbol.getLeft().getLocation().getRange());
} else {
setDisplayName(symbol.getRight().getName());
setIconBaseWithExtension(Icons.getSymbolIconBase(symbol.getRight().getKind()));
this.open = createOpenAction(currentFileUri, symbol.getRight().getRange());
}
}
public List<Either<SymbolInformation, DocumentSymbol>> documentSymbol(DocumentSymbolParams params, IProgressMonitor monitor) {
ITypeRoot unit = JDTUtils.resolveTypeRoot(params.getTextDocument().getUri());
if (unit == null) {
return Collections.emptyList();
}
if (hierarchicalDocumentSymbolSupported) {
List<DocumentSymbol> symbols = this.getHierarchicalOutline(unit, monitor);
return symbols.stream().map(Either::<SymbolInformation, DocumentSymbol>forRight).collect(toList());
} else {
SymbolInformation[] elements = this.getOutline(unit, monitor);
return Arrays.asList(elements).stream().map(Either::<SymbolInformation, DocumentSymbol>forLeft).collect(toList());
}
}
private DocumentSymbol toDocumentSymbol(IJavaElement unit, IProgressMonitor monitor) {
int type = unit.getElementType();
if (type != TYPE && type != FIELD && type != METHOD && type != PACKAGE_DECLARATION && type != COMPILATION_UNIT) {
return null;
}
if (monitor.isCanceled()) {
throw new OperationCanceledException("User abort");
}
DocumentSymbol symbol = new DocumentSymbol();
try {
String name = getName(unit);
symbol.setName(name);
symbol.setRange(getRange(unit));
symbol.setSelectionRange(getSelectionRange(unit));
symbol.setKind(mapKind(unit));
symbol.setDeprecated(isDeprecated(unit));
symbol.setDetail(getDetail(unit, name));
if (unit instanceof IParent) {
//@formatter:off
IJavaElement[] children = filter(((IParent) unit).getChildren());
symbol.setChildren(Stream.of(children)
.map(child -> toDocumentSymbol(child, monitor))
.filter(Objects::nonNull)
.collect(Collectors.toList()));
//@formatter:off
}
} catch (JavaModelException e) {
Exceptions.sneakyThrow(e);
}
return symbol;
}
@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(DocumentSymbolParams params) {
logInfo(">> document/documentSymbol");
boolean hierarchicalDocumentSymbolSupported = preferenceManager.getClientPreferences().isHierarchicalDocumentSymbolSupported();
DocumentSymbolHandler handler = new DocumentSymbolHandler(hierarchicalDocumentSymbolSupported);
return computeAsync((monitor) -> {
waitForLifecycleJobs(monitor);
return handler.documentSymbol(params, monitor);
});
}
@Override
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(DocumentSymbolParams params) {
logInfo(">> document/documentSymbol");
boolean hierarchicalDocumentSymbolSupported = preferenceManager.getClientPreferences().isHierarchicalDocumentSymbolSupported();
DocumentSymbolHandler handler = new DocumentSymbolHandler(hierarchicalDocumentSymbolSupported);
return computeAsync((monitor) -> {
waitForLifecycleJobs(monitor);
return handler.documentSymbol(params, monitor);
});
}
@Test
public void testSyntheticMember_hierarchical() throws Exception {
String className = "org.apache.commons.lang3.text.StrTokenizer";
List<? extends DocumentSymbol> symbols = asStream(getHierarchicalSymbols(className)).collect(Collectors.toList());
boolean overloadedMethod1Found = false;
boolean overloadedMethod2Found = false;
String overloadedMethod1 = "getCSVInstance(String) : StrTokenizer";
String overloadedMethod2 = "reset() : StrTokenizer";
for (DocumentSymbol symbol : symbols) {
Range fullRange = symbol.getRange();
Range selectionRange = symbol.getSelectionRange();
assertTrue("Class: " + className + ", Symbol:" + symbol.getName() + " - invalid location.",
fullRange != null && isValid(fullRange) && selectionRange != null && isValid(selectionRange));
assertFalse("Class: " + className + ", Symbol:" + symbol.getName() + " - invalid name",
symbol.getName().startsWith("access$"));
assertFalse("Class: " + className + ", Symbol:" + symbol.getName() + "- invalid name",
symbol.getName().equals("<clinit>"));
if (overloadedMethod1.equals(symbol.getName() + symbol.getDetail())) {
overloadedMethod1Found = true;
}
if (overloadedMethod2.equals(symbol.getName() + symbol.getDetail())) {
overloadedMethod2Found = true;
}
}
assertTrue("The " + overloadedMethod1 + " method hasn't been found", overloadedMethod1Found);
assertTrue("The " + overloadedMethod2 + " method hasn't been found", overloadedMethod2Found);
}
@Test
public void testTypes_hierarchical() throws Exception {
String className = "org.sample.Bar";
List<? extends DocumentSymbol> symbols = getHierarchicalSymbols(className);
assertHasHierarchicalSymbol("main(String[]) : void", "Bar", SymbolKind.Method, symbols);
assertHasHierarchicalSymbol("MyInterface", "Bar", SymbolKind.Interface, symbols);
assertHasHierarchicalSymbol("foo() : void", "MyInterface", SymbolKind.Method, symbols);
assertHasHierarchicalSymbol("MyClass", "Bar", SymbolKind.Class, symbols);
assertHasHierarchicalSymbol("bar() : void", "MyClass", SymbolKind.Method, symbols);
}
@Test
public void testSyntheticMember_hierarchical_noSourceAttached() throws Exception {
String className = "foo.bar";
List<? extends DocumentSymbol> symbols = asStream(internalGetHierarchicalSymbols(noSourceProject, monitor, className)).collect(Collectors.toList());
assertHasHierarchicalSymbol("bar()", "bar", SymbolKind.Constructor, symbols);
assertHasHierarchicalSymbol("add(int...) : int", "bar", SymbolKind.Method, symbols);
}
private static List<? extends DocumentSymbol> internalGetHierarchicalSymbols(IProject project, IProgressMonitor monitor, String className)
throws JavaModelException, UnsupportedEncodingException, InterruptedException, ExecutionException {
String uri = ClassFileUtil.getURI(project, className);
TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri);
DocumentSymbolParams params = new DocumentSymbolParams();
params.setTextDocument(identifier);
//@formatter:off
List<DocumentSymbol> symbols = new DocumentSymbolHandler(true)
.documentSymbol(params, monitor).stream()
.map(Either::getRight).collect(toList());
//@formatter:on
assertTrue(symbols.size() > 0);
return symbols;
}
private void assertHasHierarchicalSymbol(String expectedType, String expectedParent, SymbolKind expectedKind, Collection<? extends DocumentSymbol> symbols) {
Optional<? extends DocumentSymbol> parent = asStream(symbols).filter(s -> expectedParent.equals(s.getName() + s.getDetail())).findFirst();
assertTrue("Cannot find parent with name: " + expectedParent, parent.isPresent());
Optional<? extends DocumentSymbol> symbol = asStream(symbols)
.filter(s -> expectedType.equals(s.getName() + s.getDetail()) && parent.get().getChildren().contains(s))
.findFirst();
assertTrue(expectedType + " (" + expectedParent + ")" + " is missing from " + symbols, symbol.isPresent());
assertKind(expectedKind, symbol.get());
}
private Stream<DocumentSymbol> asStream(Collection<? extends DocumentSymbol> symbols) {
//@formatter:off
return symbols.stream()
.map(s -> TreeTraverser.<DocumentSymbol>using(ds -> ds.getChildren() == null
? Collections.<DocumentSymbol>emptyList()
: ds.getChildren()).breadthFirstTraversal(s).toList())
.flatMap(List::stream);
//@formatter:on
}