<commit_before>/*******************************************************************************
 * 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
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Pivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springframework.ide.vscode.commons.yaml.reconcile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.util.HierarchicalDocumentSymbolHandler;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.YamlPathSegmentType;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;

public class TypeBasedYamlHierarchicalSymbolHandler implements HierarchicalDocumentSymbolHandler, ITypeCollector {

	private static final Logger log = LoggerFactory.getLogger(TypeBasedYamlHierarchicalSymbolHandler.class);

	public static class HierarchicalDefType {
		/**
		 * A yaml node of this type constitutes a definion. This should identify the 'whole' definition not just the
		 * part of the node that contains the name of the defined entity.
		 */
		public final YType defType;

		/**
		 * A yaml optional path that points to the part of the node where the defined entity's name can be found.
		 */
		public final YamlPath namePath;

		public final SymbolKind kind;

		public final String detail;

		public HierarchicalDefType(YType defType, YamlPath namePath, SymbolKind kind, String detail) {
			super();
			this.defType = defType;
			this.namePath = namePath;
			this.kind = kind;
			this.detail = detail;
		}
		@Override
		public String toString() {
			return "HierarchicalDefType [defType=" + defType + ", namePath=" + namePath + "]";
		}
		public DocumentSymbol createSymbol(YamlFileAST currentAst, Node node, YType type, YamlPath path) {
			try {
				IDocument doc = currentAst.getDocument();
				if (namePath!=null) {
					Node nameNode = namePath.traverseNode(node);
					if (nameNode!=null) {
						DocumentRegion nodeRegion = NodeUtil.region(doc, node);
						DocumentRegion nameRegion = NodeUtil.region(doc, nameNode);
						if (!nodeRegion.contains(nameRegion)) {
							//This violates the expectation that most clients have. 
							//vscode, for example drops the entire symbols hierarchy as 'invalid' if this happens
							nodeRegion = nameRegion;
						}
						return new DocumentSymbol(NodeUtil.asScalar(nameNode), kind,
								nodeRegion.asRange(),
								nameRegion.asRange(),
								detail
						);
					}
				} else {
					//If there's no 'namePath' then we will assume the node we found is the value of a map entry...
					//and use the map's key as the symbol's name
					MappingNode map = NodeUtil.asMapping(path.dropLast().traverseToNode(currentAst));
					if (map!=null) {
						YamlPathSegment segment = path.getLastSegment();
						if (segment.getType()==YamlPathSegmentType.VAL_AT_KEY) {
							String key = segment.toPropString();
							for (NodeTuple entry : map.getValue()) {
								if (key.equals(NodeUtil.asScalar(entry.getKeyNode()))) {
									return new DocumentSymbol(key, kind,
											NodeUtil.region(doc, entry).asRange(),
											NodeUtil.region(doc, entry.getKeyNode()).asRange(),
											detail
									);
								}
							}
						}
					}
				}
			} catch (Exception e) {
				log.error("", e);
			}
			return null;
		}
	}

	private static class Item {
		YamlPath path;
		DocumentSymbol symbol;
		Item(YamlPath path, DocumentSymbol symbol) {
			super();
			this.path = path;
			this.symbol = symbol;
		}
		public void addChild(DocumentSymbol sym) {
			if (symbol.getChildren()==null) {
				symbol.setChildren(new ArrayList<DocumentSymbol>());
			}
			symbol.getChildren().add(sym);
		}
	}

	private TypeBasedYamlSymbolHandler baseHandler;
	Map<YType, HierarchicalDefType> hierarchicalDefinitionTypes;

	Map<String, List<DocumentSymbol>> outlineByUri = new HashMap<>();
	private YamlFileAST currentAst;

	private Stack<Item> stack = new Stack<>();
	private ImmutableList.Builder<DocumentSymbol> rootSymbols;

	public TypeBasedYamlHierarchicalSymbolHandler(TypeBasedYamlSymbolHandler baseHandler,
			List<HierarchicalDefType> hierarchicalDefinitionTypes) {
				this.baseHandler = baseHandler;
				Builder<YType, HierarchicalDefType> builder = ImmutableMap.builder();
				for (HierarchicalDefType hdt : hierarchicalDefinitionTypes) {
					builder.put(hdt.defType, hdt);
				}
				this.hierarchicalDefinitionTypes = builder.build();
	}

	@Override
	public List<? extends SymbolInformation> handle(DocumentSymbolParams params) {
		return baseHandler.handle(params);
	}

	@Override
	public List<? extends DocumentSymbol> handleHierarchic(DocumentSymbolParams params) {
		List<DocumentSymbol> symbols = outlineByUri.get(params.getTextDocument().getUri());
		log.info("hierarchical symbols: {}", symbols);
		return symbols;
	}

	@Override
	public void beginCollecting(YamlFileAST ast) {
		Assert.isNull("Session already active", currentAst);
		this.currentAst = ast;
		this.rootSymbols = ImmutableList.builder();
		this.stack = new Stack<>();
	}

	@Override
	public void accept(Node node, YType type, YamlPath path) {
		HierarchicalDefType def = hierarchicalDefinitionTypes.get(type);<commit_msg>Cleanup debug logging<commit_after>/*******************************************************************************
 * 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
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Pivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springframework.ide.vscode.commons.yaml.reconcile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.util.HierarchicalDocumentSymbolHandler;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.YamlPathSegmentType;
import org.springframework.ide.vscode.commons.yaml.schema.YType;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;

public class TypeBasedYamlHierarchicalSymbolHandler implements HierarchicalDocumentSymbolHandler, ITypeCollector {

	private static final Logger log = LoggerFactory.getLogger(TypeBasedYamlHierarchicalSymbolHandler.class);

	public static class HierarchicalDefType {
		/**
		 * A yaml node of this type constitutes a definion. This should identify the 'whole' definition not just the
		 * part of the node that contains the name of the defined entity.
		 */
		public final YType defType;

		/**
		 * A yaml optional path that points to the part of the node where the defined entity's name can be found.
		 */
		public final YamlPath namePath;

		public final SymbolKind kind;

		public final String detail;

		public HierarchicalDefType(YType defType, YamlPath namePath, SymbolKind kind, String detail) {
			super();
			this.defType = defType;
			this.namePath = namePath;
			this.kind = kind;
			this.detail = detail;
		}
		@Override
		public String toString() {
			return "HierarchicalDefType [defType=" + defType + ", namePath=" + namePath + "]";
		}
		public DocumentSymbol createSymbol(YamlFileAST currentAst, Node node, YType type, YamlPath path) {
			try {
				IDocument doc = currentAst.getDocument();
				if (namePath!=null) {
					Node nameNode = namePath.traverseNode(node);
					if (nameNode!=null) {
						DocumentRegion nodeRegion = NodeUtil.region(doc, node);
						DocumentRegion nameRegion = NodeUtil.region(doc, nameNode);
						if (!nodeRegion.contains(nameRegion)) {
							//This violates the expectation that most clients have. 
							//vscode, for example drops the entire symbols hierarchy as 'invalid' if this happens
							nodeRegion = nameRegion;
						}
						return new DocumentSymbol(NodeUtil.asScalar(nameNode), kind,
								nodeRegion.asRange(),
								nameRegion.asRange(),
								detail
						);
					}
				} else {
					//If there's no 'namePath' then we will assume the node we found is the value of a map entry...
					//and use the map's key as the symbol's name
					MappingNode map = NodeUtil.asMapping(path.dropLast().traverseToNode(currentAst));
					if (map!=null) {
						YamlPathSegment segment = path.getLastSegment();
						if (segment.getType()==YamlPathSegmentType.VAL_AT_KEY) {
							String key = segment.toPropString();
							for (NodeTuple entry : map.getValue()) {
								if (key.equals(NodeUtil.asScalar(entry.getKeyNode()))) {
									return new DocumentSymbol(key, kind,
											NodeUtil.region(doc, entry).asRange(),
											NodeUtil.region(doc, entry.getKeyNode()).asRange(),
											detail
									);
								}
							}
						}
					}
				}
			} catch (Exception e) {
				log.error("", e);
			}
			return null;
		}
	}

	private static class Item {
		YamlPath path;
		DocumentSymbol symbol;
		Item(YamlPath path, DocumentSymbol symbol) {
			super();
			this.path = path;
			this.symbol = symbol;
		}
		public void addChild(DocumentSymbol sym) {
			if (symbol.getChildren()==null) {
				symbol.setChildren(new ArrayList<DocumentSymbol>());
			}
			symbol.getChildren().add(sym);
		}
	}

	private TypeBasedYamlSymbolHandler baseHandler;
	Map<YType, HierarchicalDefType> hierarchicalDefinitionTypes;

	Map<String, List<DocumentSymbol>> outlineByUri = new HashMap<>();
	private YamlFileAST currentAst;

	private Stack<Item> stack = new Stack<>();
	private ImmutableList.Builder<DocumentSymbol> rootSymbols;

	public TypeBasedYamlHierarchicalSymbolHandler(TypeBasedYamlSymbolHandler baseHandler,
			List<HierarchicalDefType> hierarchicalDefinitionTypes) {
				this.baseHandler = baseHandler;
				Builder<YType, HierarchicalDefType> builder = ImmutableMap.builder();
				for (HierarchicalDefType hdt : hierarchicalDefinitionTypes) {
					builder.put(hdt.defType, hdt);
				}
				this.hierarchicalDefinitionTypes = builder.build();
	}

	@Override
	public List<? extends SymbolInformation> handle(DocumentSymbolParams params) {
		return baseHandler.handle(params);
	}

	@Override
	public List<? extends DocumentSymbol> handleHierarchic(DocumentSymbolParams params) {
		return outlineByUri.get(params.getTextDocument().getUri());
	}

	@Override
	public void beginCollecting(YamlFileAST ast) {
		Assert.isNull("Session already active", currentAst);
		this.currentAst = ast;
		this.rootSymbols = ImmutableList.builder();
		this.stack = new Stack<>();
	}

	@Override
	public void accept(Node node, YType type, YamlPath path) {
		HierarchicalDefType def = hierarchicalDefinitionTypes.get(type);
{'fraction_non_alphanumeric': 0.12248050042474322, 'fraction_numerical': 0.0021623291373851264, 'mean_word_length': 4.311730926989336, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 30, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '147094', 'n_tokens_mistral': 4408, 'n_tokens_neox': 3714, 'n_words': 901}