simplified spring property index handling by removing a lot of value-specific stuff
This commit is contained in:
@@ -19,7 +19,7 @@ import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
@@ -31,9 +31,9 @@ import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
*/
|
||||
public class ValueCompletionProcessor {
|
||||
|
||||
private FuzzyMap<PropertyInfo> index;
|
||||
private FuzzyMap<ConfigurationMetadataProperty> index;
|
||||
|
||||
public ValueCompletionProcessor(FuzzyMap<PropertyInfo> index) {
|
||||
public ValueCompletionProcessor(FuzzyMap<ConfigurationMetadataProperty> index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ public class ValueCompletionProcessor {
|
||||
try {
|
||||
// case: @Value(<*>)
|
||||
if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) {
|
||||
List<Match<PropertyInfo>> matches = findMatches("");
|
||||
List<Match<ConfigurationMetadataProperty>> matches = findMatches("");
|
||||
|
||||
for (Match<PropertyInfo> match : matches) {
|
||||
for (Match<ConfigurationMetadataProperty> match : matches) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(offset, offset, "\"${" + match.data.getId() + "}\"");
|
||||
@@ -64,9 +64,9 @@ public class ValueCompletionProcessor {
|
||||
String proposalPrefix = "\"";
|
||||
String proposalPostfix = "\"";
|
||||
|
||||
List<Match<PropertyInfo>> matches = findMatches(prefix);
|
||||
List<Match<ConfigurationMetadataProperty>> matches = findMatches(prefix);
|
||||
|
||||
for (Match<PropertyInfo> match : matches) {
|
||||
for (Match<ConfigurationMetadataProperty> match : matches) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(startOffset, endOffset, proposalPrefix + "${" + match.data.getId() + "}" + proposalPostfix);
|
||||
@@ -101,9 +101,9 @@ public class ValueCompletionProcessor {
|
||||
String fullNodeContent = doc.get(node.getStartPosition(), node.getLength());
|
||||
String postCompletion = isClosingBracketMissing(fullNodeContent + preCompletion) ? "}" : "";
|
||||
|
||||
List<Match<PropertyInfo>> matches = findMatches(prefix);
|
||||
List<Match<ConfigurationMetadataProperty>> matches = findMatches(prefix);
|
||||
|
||||
for (Match<PropertyInfo> match : matches) {
|
||||
for (Match<ConfigurationMetadataProperty> match : matches) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(startOffset, endOffset, preCompletion + match.data.getId() + postCompletion);
|
||||
@@ -150,8 +150,8 @@ public class ValueCompletionProcessor {
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Match<PropertyInfo>> findMatches(String prefix) {
|
||||
List<Match<PropertyInfo>> matches = index.find(camelCaseToHyphens(prefix));
|
||||
private List<Match<ConfigurationMetadataProperty>> findMatches(String prefix) {
|
||||
List<Match<ConfigurationMetadataProperty>> matches = index.find(camelCaseToHyphens(prefix));
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 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.metadata;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy;
|
||||
import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* A abstract {@link ValueProviderStrategy} that is mean to help speedup successive invocations of
|
||||
* content assist with a similar 'query' string.
|
||||
* <p>
|
||||
* This implementation is meant to be used for providers that use potentially lenghty/expensive searches
|
||||
* to determine hints. Since content assist hints are requested by Eclipse CA framework directly on
|
||||
* the UI thread, they can not simply perform a lengthy search and block UI thread until it finished.
|
||||
* <p>
|
||||
* This implementation therefore does the following:
|
||||
* <ul>
|
||||
* <li>Limit the duration of time spent on the UI thread.
|
||||
* <li>Cache results of searches for a limited time.
|
||||
* <li>Speedup queries for successive queries by using the already cached result of a similar (prefix) query.
|
||||
* <li>When the time spent on UI thread waiting for a current search exceeds the allowed time limit,
|
||||
* return immediately with whatever results have been found so far.
|
||||
* </ul>
|
||||
*
|
||||
* TODO: rather than an abstract class this should really be 'Wrapper' class that delegates to another
|
||||
* {@link ValueProviderStrategy} and adds a cache in front of it.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public abstract class CachingValueProvider implements ValueProviderStrategy {
|
||||
|
||||
private static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1000);
|
||||
|
||||
/**
|
||||
* Content assist is called inside UI thread and so doing something lenghty things
|
||||
* like a JavaSearch will block the UI thread completely freezing the UI. So, we
|
||||
* only return as many results as can be obtained within this hard TIMEOUT limit.
|
||||
*/
|
||||
public static Duration TIMEOUT = DEFAULT_TIMEOUT;
|
||||
|
||||
/**
|
||||
* The maximum number of results returned for a single request. Used to limit the
|
||||
* values that are cached per entry.
|
||||
*/
|
||||
private int MAX_RESULTS = 500;
|
||||
|
||||
private Cache<Tuple2<String,String>, CacheEntry> cache = createCache();
|
||||
|
||||
private class CacheEntry {
|
||||
boolean isComplete = false;
|
||||
int count = 0;
|
||||
Flux<StsValueHint> values;
|
||||
|
||||
public CacheEntry(String query, Flux<StsValueHint> producer) {
|
||||
values = producer
|
||||
.take(MAX_RESULTS)
|
||||
.cache(MAX_RESULTS);
|
||||
values.subscribe(); // create infinite demand so that we actually force cache entries to be fetched upto the max.
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CacheEntry [isComplete=" + isComplete + ", count=" + count + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Flux<StsValueHint> getValues(IJavaProject javaProject, String query) {
|
||||
Tuple2<String, String> key = key(javaProject, query);
|
||||
CacheEntry cached = null;
|
||||
try {
|
||||
cached = cache.get(key, () -> new CacheEntry(query, getValuesIncremental(javaProject, query)));
|
||||
} catch (ExecutionException e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return cached.values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to use an already cached, complete result for a query that is a prefix of the current query to speed things up.
|
||||
* <p>
|
||||
* Falls back on doing a full-blown search if there's no usable 'prefix-query' in the cache.
|
||||
*/
|
||||
private Flux<StsValueHint> getValuesIncremental(IJavaProject javaProject, String query) {
|
||||
// debug("trying to solve "+query+" incrementally");
|
||||
String subquery = query;
|
||||
while (subquery.length()>=1) {
|
||||
subquery = subquery.substring(0, subquery.length()-1);
|
||||
CacheEntry cached = null;
|
||||
try {
|
||||
cached = cache.get(key(javaProject, subquery), () -> null);
|
||||
} catch (ExecutionException | InvalidCacheLoadException e) {
|
||||
// Log.log(e);
|
||||
}
|
||||
if (cached!=null) {
|
||||
System.out.println("cached "+subquery+": "+cached);
|
||||
if (cached.isComplete) {
|
||||
return cached.values
|
||||
// .doOnNext((hint) -> debug("filter["+query+"]: "+hint.getValue()))
|
||||
.filter((hint) -> 0!=FuzzyMatcher.matchScore(query, hint.getValue().toString()));
|
||||
} else {
|
||||
// debug("subquery "+subquery+" cached but is incomplete");
|
||||
}
|
||||
}
|
||||
}
|
||||
// debug("full search for: "+query);
|
||||
return getValuesAsync(javaProject, query);
|
||||
}
|
||||
|
||||
protected abstract Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query);
|
||||
|
||||
private Tuple2<String,String> key(IJavaProject javaProject, String query) {
|
||||
return Tuples.of(javaProject==null?null:javaProject.getElementName(), query);
|
||||
}
|
||||
|
||||
protected <K,V> Cache<K,V> createCache() {
|
||||
return CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).expireAfterAccess(1, TimeUnit.MINUTES).build();
|
||||
}
|
||||
|
||||
public static void restoreDefaults() {
|
||||
TIMEOUT = DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 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.metadata;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy;
|
||||
import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.commons.java.Flags;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* Provides the algorithm for 'class-reference' valueProvider.
|
||||
* <p>
|
||||
* See: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc
|
||||
*
|
||||
* @author Kris De Volder
|
||||
* @author Alex Boyko
|
||||
*/
|
||||
public class ClassReferenceProvider extends CachingValueProvider {
|
||||
|
||||
/**
|
||||
* Default value for the 'concrete' parameter.
|
||||
*/
|
||||
private static final boolean DEFAULT_CONCRETE = true;
|
||||
|
||||
private static final ClassReferenceProvider UNTARGETTED_INSTANCE = new ClassReferenceProvider(null, DEFAULT_CONCRETE);
|
||||
|
||||
public static final Function<Map<String, Object>, ValueProviderStrategy> FACTORY = applyOn(
|
||||
1, TimeUnit.MINUTES,
|
||||
(params) -> {
|
||||
String target = getTarget(params);
|
||||
Boolean concrete = getConcrete(params);
|
||||
if (target!=null || concrete!=null) {
|
||||
if (concrete==null) {
|
||||
concrete = DEFAULT_CONCRETE;
|
||||
}
|
||||
return new ClassReferenceProvider(target, concrete);
|
||||
}
|
||||
return UNTARGETTED_INSTANCE;
|
||||
}
|
||||
);
|
||||
|
||||
private static <K,V> Function<K,V> applyOn(long duration, TimeUnit unit, Function<K,V> func) {
|
||||
Cache<K,V> cache = CacheBuilder.newBuilder().expireAfterAccess(duration, unit).expireAfterWrite(duration, unit).build();
|
||||
return (k) -> {
|
||||
try {
|
||||
return cache.get(k, () -> func.apply(k));
|
||||
} catch (ExecutionException e) {
|
||||
Log.log(e);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static String getTarget(Map<String, Object> params) {
|
||||
if (params!=null) {
|
||||
Object obj = params.get("target");
|
||||
if (obj instanceof String) {
|
||||
String target = (String) obj;
|
||||
if (StringUtil.hasText(target)) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isAbstract(IType type) {
|
||||
try {
|
||||
return type.isInterface() || Flags.isAbstract(type.getFlags());
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Boolean getConcrete(Map<String, Object> params) {
|
||||
try {
|
||||
if (params!=null) {
|
||||
Object obj = params.get("concrete");
|
||||
if (obj instanceof String) {
|
||||
String concrete = (String) obj;
|
||||
return Boolean.valueOf(concrete);
|
||||
} else if (obj instanceof Boolean) {
|
||||
return (Boolean) obj;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional, fully qualified name of the 'target' type. Suggested hints should be a subtype of this type.
|
||||
*/
|
||||
private String target;
|
||||
|
||||
/**
|
||||
* Optional parameter, whether only concrete types should be suggested. Default value is true.
|
||||
*/
|
||||
private boolean concrete;
|
||||
|
||||
private ClassReferenceProvider(String target, boolean concrete) {
|
||||
this.target = target;
|
||||
this.concrete = concrete;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query) {
|
||||
IType targetType = target == null || target.isEmpty() ? javaProject.getClasspath().findType("java.lang.Object") : javaProject.getClasspath().findType(target);
|
||||
if (targetType == null) {
|
||||
return Flux.empty();
|
||||
}
|
||||
Set<IType> allSubclasses = javaProject.getClasspath()
|
||||
.allSubtypesOf(targetType)
|
||||
.filter(t -> Flags.isPublic(t.getFlags()) && !concrete || !isAbstract(t))
|
||||
.collect(Collectors.toSet())
|
||||
.block();
|
||||
if (allSubclasses.isEmpty()) {
|
||||
return Flux.empty();
|
||||
} else {
|
||||
return javaProject.getClasspath()
|
||||
.fuzzySearchTypes(query, type -> allSubclasses.contains(type))
|
||||
.collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2()))
|
||||
.flatMap(l -> Flux.fromIterable(l))
|
||||
.map(t -> StsValueHint.create(t.getT1()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
package org.springframework.ide.vscode.boot.metadata;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
@@ -19,10 +20,10 @@ import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexProvider {
|
||||
|
||||
private static final FuzzyMap<PropertyInfo> EMPTY_INDEX = new SpringPropertyIndex(null, null);
|
||||
private static final FuzzyMap<ConfigurationMetadataProperty> EMPTY_INDEX = new SpringPropertyIndex(null);
|
||||
|
||||
private JavaProjectFinder javaProjectFinder;
|
||||
private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault());
|
||||
private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager();
|
||||
|
||||
private ProgressService progressService = (id, msg) -> { /*ignore*/ };
|
||||
|
||||
@@ -31,7 +32,7 @@ public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexPr
|
||||
}
|
||||
|
||||
@Override
|
||||
public FuzzyMap<PropertyInfo> getIndex(IDocument doc) {
|
||||
public FuzzyMap<ConfigurationMetadataProperty> getIndex(IDocument doc) {
|
||||
IJavaProject jp = javaProjectFinder.find(doc);
|
||||
if (jp!=null) {
|
||||
return indexManager.get(jp, progressService);
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 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.metadata;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.util.StringUtil.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
/**
|
||||
* An index navigator allows selecting subset of a property index as if
|
||||
* navigating the index by selecting on a property
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class IndexNavigator {
|
||||
|
||||
//Possible opitmization: we could cache prefix match candidate and extended match candidate
|
||||
// since it is assumed that the index is immutable for the lifetime of
|
||||
// the index navigator.
|
||||
|
||||
private static final char NAV_CHAR = '.';
|
||||
|
||||
/**
|
||||
* Property access in this navigator are interpreted relative
|
||||
* to this prefix
|
||||
*/
|
||||
private String prefix = null;
|
||||
private FuzzyMap<PropertyInfo> index;
|
||||
|
||||
private IndexNavigator(FuzzyMap<PropertyInfo> index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
private IndexNavigator(FuzzyMap<PropertyInfo> index, String prefix) {
|
||||
this.index = index;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public static IndexNavigator with(FuzzyMap<PropertyInfo> index) {
|
||||
return new IndexNavigator(index);
|
||||
}
|
||||
|
||||
public IndexNavigator selectSubProperty(String name) {
|
||||
return new IndexNavigator(index, join(prefix, name));
|
||||
}
|
||||
|
||||
protected String join(String prefix, String postfix) {
|
||||
if (!hasText(prefix)) {
|
||||
return postfix;
|
||||
} else {
|
||||
return prefix + NAV_CHAR + postfix;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return property info that is an exact match with the current prefix or
|
||||
* null if there's no exact match
|
||||
*/
|
||||
public PropertyInfo getExactMatch() {
|
||||
if (prefix!=null) {
|
||||
PropertyInfo candidate = index.findLongestCommonPrefixEntry(prefix);
|
||||
if (candidate.getId().equals(prefix)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property that has the current prefix as a 'true' prefix. A true prefix
|
||||
* is a String that has the current prefix as a prefix and continues onward with
|
||||
* a navigation operation.
|
||||
*/
|
||||
public PropertyInfo getExtensionCandidate() {
|
||||
//If current prefix is null then all entries in the index are candidates since
|
||||
// the index is at the 'root' of the tree and we don't need a '.' to navigate
|
||||
String extendedPrefix = prefix==null?"":prefix + NAV_CHAR;
|
||||
PropertyInfo candidate = index.findLongestCommonPrefixEntry(extendedPrefix);
|
||||
if (candidate.getId().startsWith(extendedPrefix)) {
|
||||
return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public List<Match<PropertyInfo>> findMatching(String query) {
|
||||
if (!StringUtil.hasText(prefix)) {
|
||||
return index.find(query);
|
||||
} else {
|
||||
String dottedPrefix = prefix +".";
|
||||
List<Match<PropertyInfo>> candidates = index.find(dottedPrefix + query);
|
||||
if (!candidates.isEmpty()) {
|
||||
//TODO: we can do better than this using treemap to narrow based on
|
||||
// prefix
|
||||
List<Match<PropertyInfo>> matches = new ArrayList<Match<PropertyInfo>>(candidates.size());
|
||||
for (Match<PropertyInfo> match : candidates) {
|
||||
if (match.data.getId().startsWith(dottedPrefix)){
|
||||
matches.add(match);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IndexNavigator("+prefix+")";
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return getExactMatch()==null && getExtensionCandidate()==null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 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.metadata;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy;
|
||||
import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* Provides the algorithm for 'logger-name' valueProvider.
|
||||
* <p>
|
||||
* See: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc
|
||||
*
|
||||
* @author Kris De Volder
|
||||
* @author Alex Boyko
|
||||
*/
|
||||
public class LoggerNameProvider extends CachingValueProvider {
|
||||
|
||||
private static final ValueProviderStrategy INSTANCE = new LoggerNameProvider();
|
||||
public static final Function<Map<String, Object>, ValueProviderStrategy> FACTORY = (params) -> INSTANCE;
|
||||
|
||||
@Override
|
||||
protected Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query) {
|
||||
return Flux.concat(
|
||||
javaProject.getClasspath()
|
||||
.fuzzySearchPackages(query)
|
||||
.map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())),
|
||||
javaProject.getClasspath()
|
||||
.fuzzySearchTypes(query, null)
|
||||
.map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2()))
|
||||
)
|
||||
.collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2()))
|
||||
.flatMap(l -> Flux.fromIterable(l))
|
||||
.map(t -> t.getT1());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 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.metadata;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Helper class to manipulate data in a file presumed to contain
|
||||
* spring-boot configuration data.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
* @author Alex Boyko
|
||||
*/
|
||||
public class MetadataManipulator {
|
||||
|
||||
private abstract class Content {
|
||||
public abstract String toString();
|
||||
public abstract void addProperty(JSONObject jsonObject) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content was parse as JSONObject.
|
||||
*/
|
||||
private class ParsedContent extends Content {
|
||||
|
||||
private JSONObject object;
|
||||
|
||||
public ParsedContent(JSONObject o) {
|
||||
this.object = o;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
try {
|
||||
return object.toString(indentFactor);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProperty(JSONObject propertyData) throws Exception {
|
||||
JSONArray properties = object.getJSONArray("properties");
|
||||
properties.put(properties.length(), propertyData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Content that is 'unparsed' and just a bunch of text.
|
||||
* Used only as a fallback when data in file can't
|
||||
* be parsed.
|
||||
* <p>
|
||||
* This content is manipulated by string manipulation.
|
||||
* It is less reliable, but can be done even if the
|
||||
* file data is not parseable.
|
||||
*/
|
||||
private class RawContent extends Content {
|
||||
|
||||
private StringBuilder doc;
|
||||
|
||||
public RawContent(String content) {
|
||||
this.doc = new StringBuilder(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return doc.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProperty(JSONObject propertyData) throws Exception {
|
||||
int insertAt = findLast(']');
|
||||
if (insertAt<0) {
|
||||
//although we're not looking for much, we didn't find it!
|
||||
//Funky file contents. Let's just insert something at end of file in a 'best effort' spirit.
|
||||
insertAt = doc.length();
|
||||
}
|
||||
insert(insertAt, "\n");
|
||||
|
||||
insert(insertAt, propertyData.toString(indentFactor));
|
||||
|
||||
int insertComma = findInsertCommaPos(insertAt);
|
||||
if (insertComma>=0) {
|
||||
insert(insertComma, ",");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe we need to add a comma in front of the new entry. This
|
||||
* method finds if/where to stick this comma.
|
||||
* @throws Exception
|
||||
*/
|
||||
private int findInsertCommaPos(int pos) throws Exception {
|
||||
pos--;
|
||||
while (pos>=0 && Character.isWhitespace(doc.charAt(pos))) {
|
||||
pos--;
|
||||
}
|
||||
if (pos>=0) {
|
||||
char c = doc.charAt(pos);
|
||||
if (c == '}') {
|
||||
//Add a comma after a '}'
|
||||
return pos+1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int insert(int insertAt, String str) throws Exception {
|
||||
if (insertAt < doc.length()) {
|
||||
doc.replace(insertAt, insertAt, str);
|
||||
} else {
|
||||
doc.append(str);
|
||||
}
|
||||
return insertAt + str.length();
|
||||
}
|
||||
|
||||
private int findLast(char toFind) throws Exception {
|
||||
int pos = doc.length()-1;
|
||||
while (pos>=0 && doc.charAt(pos)!=toFind) {
|
||||
pos--;
|
||||
}
|
||||
//We got here either because
|
||||
// - we found char at pos or..
|
||||
// - we reached position *before* start of file (i.e. -1)
|
||||
return pos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface ContentStore {
|
||||
String getContents() throws Exception;
|
||||
void setContents(String content) throws Exception;
|
||||
}
|
||||
|
||||
private static final String INITIAL_CONTENT =
|
||||
"{\"properties\": [\n" +
|
||||
"]}";
|
||||
|
||||
private static final String ENCODING = "UTF8";
|
||||
private ContentStore contentStore;
|
||||
private Content fContent;
|
||||
private int indentFactor = 2;
|
||||
|
||||
public MetadataManipulator(ContentStore contentStore) {
|
||||
this.contentStore = contentStore;
|
||||
}
|
||||
|
||||
public MetadataManipulator(final File file) {
|
||||
this(new ContentStore() {
|
||||
|
||||
@Override
|
||||
public String getContents() throws Exception {
|
||||
return new String(Files.readAllBytes(Paths.get(file.toURI())), ENCODING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(String content) throws Exception {
|
||||
Files.write(Paths.get(file.toURI()), content.getBytes(ENCODING));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private Content getContent() throws Exception {
|
||||
if (fContent==null) {
|
||||
fContent = readContent();
|
||||
}
|
||||
return fContent;
|
||||
}
|
||||
|
||||
private Content readContent() throws Exception {
|
||||
String content = contentStore.getContents();
|
||||
if (content.trim().isEmpty()) {
|
||||
JSONObject o = initialContent();
|
||||
return new ParsedContent(o);
|
||||
} else {
|
||||
try {
|
||||
return new ParsedContent(new JSONObject(content));
|
||||
} catch (Exception e) {
|
||||
//couldn't parse?
|
||||
return new RawContent(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addDefaultInfo(String propertyName) throws Exception {
|
||||
getContent().addProperty(createDefaultData(propertyName));
|
||||
}
|
||||
|
||||
private JSONObject createDefaultData(String propertyName) throws Exception {
|
||||
JSONObject obj = new JSONObject(new LinkedHashMap<String, Object>());
|
||||
obj.put("name", propertyName);
|
||||
obj.put("type", String.class.getName());
|
||||
obj.put("description", "A description for '"+propertyName+"'");
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the initial content (must be generated rather than being a constant to respect newline conventions
|
||||
* on user's system.
|
||||
*/
|
||||
private JSONObject initialContent() throws Exception {
|
||||
return new JSONObject(INITIAL_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* After manipulating the data, use this to persist changes back to the file.
|
||||
*/
|
||||
public void save() throws Exception {
|
||||
contentStore.setContents(getContent().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the 'reliable' manipulations can be used (which is the case
|
||||
* only if the data in the file is valid json).
|
||||
*/
|
||||
public boolean isReliable() throws Exception {
|
||||
return getContent() instanceof ParsedContent;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 Pivotal, Inc.
|
||||
* Copyright (c) 2016, 2017 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
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014-2016 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.metadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource;
|
||||
import org.springframework.boot.configurationmetadata.Deprecation;
|
||||
import org.springframework.boot.configurationmetadata.ValueHint;
|
||||
import org.springframework.boot.configurationmetadata.ValueProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
/**
|
||||
* Information about a spring property, basically, this is the same as
|
||||
*
|
||||
* {@link ConfigurationMetadataProperty} but augmented with information
|
||||
* about {@link ConfigurationMetadataSource}s that declare the property.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class PropertyInfo {
|
||||
|
||||
/**
|
||||
* Identifies a 'Source'. This is essentially the sames as {@link ConfigurationMetadataSource}.
|
||||
* We could use {@link ConfigurationMetadataSource} directly, but this only contains
|
||||
* the info that we actually use so takes less memory.
|
||||
*/
|
||||
public static class PropertySource {
|
||||
private final String sourceType;
|
||||
private final String sourceMethod;
|
||||
public PropertySource(ConfigurationMetadataSource source) {
|
||||
String st = source.getSourceType();
|
||||
this.sourceType = st!=null?st:source.getType();
|
||||
this.sourceMethod = source.getSourceMethod();
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return sourceType+"::"+sourceMethod;
|
||||
}
|
||||
public String getSourceType() {
|
||||
return sourceType;
|
||||
}
|
||||
public String getSourceMethod() {
|
||||
return sourceMethod;
|
||||
}
|
||||
}
|
||||
|
||||
final private String id;
|
||||
private String type;
|
||||
final private String name;
|
||||
final private Object defaultValue;
|
||||
final private String description;
|
||||
private List<PropertySource> sources;
|
||||
private Deprecation deprecation;
|
||||
private ImmutableList<ValueHint> valueHints;
|
||||
private ImmutableList<ValueHint> keyHints;
|
||||
private ValueProviderStrategy valueProvider;
|
||||
private ValueProviderStrategy keyProvider;
|
||||
|
||||
public PropertyInfo(String id, String type, String name,
|
||||
Object defaultValue, String description,
|
||||
Deprecation deprecation,
|
||||
List<ValueHint> valueHints,
|
||||
List<ValueHint> keyHints,
|
||||
ValueProviderStrategy valueProvider,
|
||||
ValueProviderStrategy keyProvider,
|
||||
List<PropertySource> sources) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.defaultValue = defaultValue;
|
||||
this.description = description;
|
||||
this.deprecation = deprecation;
|
||||
this.valueHints = valueHints==null?null:ImmutableList.copyOf(valueHints);
|
||||
this.keyHints = keyHints==null?null:ImmutableList.copyOf(keyHints);
|
||||
this.valueProvider = valueProvider;
|
||||
this.keyProvider = keyProvider;
|
||||
this.sources = sources;
|
||||
}
|
||||
public PropertyInfo(ValueProviderRegistry valueProviders, ConfigurationMetadataProperty prop) {
|
||||
this(
|
||||
prop.getId(),
|
||||
prop.getType(),
|
||||
prop.getName(),
|
||||
prop.getDefaultValue(),
|
||||
prop.getDescription(),
|
||||
prop.getDeprecation(),
|
||||
prop.getHints().getValueHints(),
|
||||
prop.getHints().getKeyHints(),
|
||||
valueProviders.resolve(prop.getHints().getValueProviders()),
|
||||
valueProviders.resolve(prop.getHints().getKeyProviders()),
|
||||
null
|
||||
);
|
||||
for (ValueProvider h : prop.getHints().getValueProviders()) {
|
||||
if (h.getName().equals("handle-as")) {
|
||||
handleAs(h.getParameters().get("target"));
|
||||
}
|
||||
}
|
||||
}
|
||||
private void handleAs(Object targetObject) {
|
||||
// debug("handle-as "+this.getId()+" -> "+targetObject);
|
||||
if (targetObject instanceof String) {
|
||||
this.type = (String)targetObject;
|
||||
}
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public List<PropertySource> getSources() {
|
||||
if (sources!=null) {
|
||||
return sources;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PropertyInfo("+getId()+")";
|
||||
}
|
||||
public void addSource(ConfigurationMetadataSource source) {
|
||||
if (sources==null) {
|
||||
sources = new ArrayList<PropertySource>();
|
||||
}
|
||||
sources.add(new PropertySource(source));
|
||||
}
|
||||
|
||||
public PropertyInfo withId(String alias) {
|
||||
if (alias.equals(id)) {
|
||||
return this;
|
||||
}
|
||||
return new PropertyInfo(alias, type, name, defaultValue, description, deprecation, valueHints, keyHints, valueProvider, keyProvider, sources);
|
||||
}
|
||||
|
||||
public void setDeprecation(Deprecation d) {
|
||||
this.deprecation = d;
|
||||
}
|
||||
|
||||
public boolean isDeprecated() {
|
||||
return deprecation!=null;
|
||||
}
|
||||
|
||||
public String getDeprecationReason() {
|
||||
return deprecation == null ? null : deprecation.getReason();
|
||||
}
|
||||
|
||||
public String getDeprecationReplacement() {
|
||||
return deprecation == null ? null : deprecation.getReplacement();
|
||||
}
|
||||
|
||||
public void addValueHints(List<ValueHint> hints) {
|
||||
Builder<ValueHint> builder = ImmutableList.builder();
|
||||
builder.addAll(valueHints);
|
||||
builder.addAll(hints);
|
||||
valueHints = builder.build();
|
||||
}
|
||||
public void addKeyHints(List<ValueHint> hints) {
|
||||
Builder<ValueHint> builder = ImmutableList.builder();
|
||||
builder.addAll(keyHints);
|
||||
builder.addAll(hints);
|
||||
keyHints = builder.build();
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 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.metadata;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy;
|
||||
import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ResourceHintProvider implements ValueProviderStrategy {
|
||||
|
||||
private static String[] CLASSPATH_PREFIXES = {
|
||||
"classpath:",
|
||||
"classpath*:"
|
||||
};
|
||||
|
||||
private static final String[] URL_PREFIXES = new String[] {
|
||||
"classpath:",
|
||||
"classpath*:",
|
||||
"file:",
|
||||
"http://",
|
||||
"https://"
|
||||
};
|
||||
|
||||
@Override
|
||||
public Flux<StsValueHint> getValues(IJavaProject javaProject, String query) {
|
||||
for (String prefix : CLASSPATH_PREFIXES) {
|
||||
if (query.startsWith(prefix)) {
|
||||
return classpathHints
|
||||
.getValues(javaProject, query.substring(prefix.length()))
|
||||
.map((hint) -> hint.prefixWith(prefix));
|
||||
}
|
||||
}
|
||||
return Flux.fromIterable(urlPrefixHints);
|
||||
}
|
||||
|
||||
final private ImmutableList<StsValueHint> urlPrefixHints = ImmutableList.copyOf(
|
||||
Arrays.stream(URL_PREFIXES)
|
||||
.map(StsValueHint::create)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
private ClasspathHints classpathHints = new ClasspathHints();
|
||||
|
||||
private static class ClasspathHints extends CachingValueProvider {
|
||||
@Override
|
||||
protected Flux<StsValueHint> getValuesAsync(IJavaProject javaProject, String query) {
|
||||
return Flux.fromStream(javaProject.getClasspath().getClasspathResources().distinct().map(StsValueHint::create));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -13,9 +13,8 @@ package org.springframework.ide.vscode.boot.metadata;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.Listener;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.ListenerManager;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
|
||||
@@ -27,20 +26,19 @@ import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class SpringPropertiesIndexManager extends ListenerManager<Listener<SpringPropertiesIndexManager>> {
|
||||
public class SpringPropertiesIndexManager {
|
||||
|
||||
private Map<IJavaProject, SpringPropertyIndex> indexes = null;
|
||||
private final ValueProviderRegistry valueProviders;
|
||||
private static int progressIdCt = 0;
|
||||
|
||||
public SpringPropertiesIndexManager(ValueProviderRegistry valueProviders) {
|
||||
this.valueProviders = valueProviders;
|
||||
public SpringPropertiesIndexManager() {
|
||||
}
|
||||
|
||||
public synchronized FuzzyMap<PropertyInfo> get(IJavaProject project, ProgressService progressService) {
|
||||
public synchronized FuzzyMap<ConfigurationMetadataProperty> get(IJavaProject project, ProgressService progressService) {
|
||||
if (indexes==null) {
|
||||
indexes = new HashMap<>();
|
||||
}
|
||||
|
||||
SpringPropertyIndex index = indexes.get(project);
|
||||
if (index==null) {
|
||||
String progressId = getProgressId();
|
||||
@@ -48,7 +46,7 @@ public class SpringPropertiesIndexManager extends ListenerManager<Listener<Sprin
|
||||
progressService.progressEvent(progressId, "Indexing Spring Boot Properties...");
|
||||
}
|
||||
|
||||
index = new SpringPropertyIndex(valueProviders, project.getClasspath());
|
||||
index = new SpringPropertyIndex(project.getClasspath());
|
||||
indexes.put(project, index);
|
||||
|
||||
if (progressService != null) {
|
||||
@@ -57,15 +55,6 @@ public class SpringPropertiesIndexManager extends ListenerManager<Listener<Sprin
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
if (indexes!=null) {
|
||||
indexes.clear();
|
||||
for (Listener<SpringPropertiesIndexManager> l : getListeners()) {
|
||||
l.changed(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static synchronized String getProgressId() {
|
||||
return DefaultSpringPropertyIndexProvider.class.getName()+ (progressIdCt++);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Pivotal, Inc.
|
||||
* Copyright (c) 2015, 2017 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
|
||||
@@ -11,71 +11,41 @@
|
||||
package org.springframework.ide.vscode.boot.metadata;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
public class SpringPropertyIndex extends FuzzyMap<PropertyInfo> {
|
||||
public class SpringPropertyIndex extends FuzzyMap<ConfigurationMetadataProperty> {
|
||||
|
||||
private ValueProviderRegistry valueProviders;
|
||||
|
||||
public SpringPropertyIndex(ValueProviderRegistry valueProviders, IClasspath projectPath) {
|
||||
this.valueProviders = valueProviders;
|
||||
public SpringPropertyIndex(IClasspath projectPath) {
|
||||
if (projectPath!=null) {
|
||||
// try {
|
||||
PropertiesLoader loader = new PropertiesLoader();
|
||||
ConfigurationMetadataRepository metadata = loader.load(projectPath);
|
||||
//^^^ Should be done in bg? It seems fast enough for now.
|
||||
|
||||
Collection<ConfigurationMetadataProperty> allEntries = metadata.getAllProperties().values();
|
||||
for (ConfigurationMetadataProperty item : allEntries) {
|
||||
add(new PropertyInfo(valueProviders, item));
|
||||
}
|
||||
|
||||
for (ConfigurationMetadataGroup group : metadata.getAllGroups().values()) {
|
||||
for (ConfigurationMetadataSource source : group.getSources().values()) {
|
||||
for (ConfigurationMetadataProperty prop : source.getProperties().values()) {
|
||||
PropertyInfo info = get(prop.getId());
|
||||
info.addSource(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// System.out.println(">>> spring properties metadata loaded "+this.size()+" items===");
|
||||
// dumpAsTestData();
|
||||
// System.out.println(">>> spring properties metadata loaded "+this.size()+" items===");
|
||||
// } catch (Exception e) {
|
||||
// LOG.log
|
||||
// }
|
||||
PropertiesLoader loader = new PropertiesLoader();
|
||||
ConfigurationMetadataRepository metadata = loader.load(projectPath);
|
||||
Collection<ConfigurationMetadataProperty> allEntries = metadata.getAllProperties().values();
|
||||
for (ConfigurationMetadataProperty item : allEntries) {
|
||||
add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void add(ConfigurationMetadataProperty propertyInfo) {
|
||||
add(new PropertyInfo(valueProviders, propertyInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps out 'test data' based on the current contents of the index. This is not meant to be
|
||||
* used in 'production' code. The idea is to call this method during development to dump a
|
||||
* 'snapshot' of the index onto System.out. The data is printed in a forma so that it can be easily
|
||||
* pasted/used into JUNit testing code.
|
||||
*/
|
||||
public void dumpAsTestData() {
|
||||
List<Match<PropertyInfo>> allData = this.find("");
|
||||
for (Match<PropertyInfo> match : allData) {
|
||||
PropertyInfo d = match.data;
|
||||
System.out.println("data("
|
||||
+dumpString(d.getId())+", "
|
||||
+dumpString(d.getType())+", "
|
||||
+dumpString(d.getDefaultValue())+", "
|
||||
+dumpString(d.getDescription()) +");"
|
||||
);
|
||||
// public void dumpAsTestData() {
|
||||
// List<Match<PropertyInfo>> allData = this.find("");
|
||||
// for (Match<PropertyInfo> match : allData) {
|
||||
// PropertyInfo d = match.data;
|
||||
// System.out.println("data("
|
||||
// +dumpString(d.getId())+", "
|
||||
// +dumpString(d.getType())+", "
|
||||
// +dumpString(d.getDefaultValue())+", "
|
||||
// +dumpString(d.getDescription()) +");"
|
||||
// );
|
||||
// for (PropertySource source : d.getSources()) {
|
||||
// String st = source.getSourceType();
|
||||
// String sm = source.getSourceMethod();
|
||||
@@ -83,15 +53,15 @@ public class SpringPropertyIndex extends FuzzyMap<PropertyInfo> {
|
||||
// System.out.println(d.getId() +" from: "+st+"::"+sm);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
// }
|
||||
// }
|
||||
|
||||
private String dumpString(Object v) {
|
||||
if (v==null) {
|
||||
return "null";
|
||||
}
|
||||
return dumpString(""+v);
|
||||
}
|
||||
// private String dumpString(Object v) {
|
||||
// if (v==null) {
|
||||
// return "null";
|
||||
// }
|
||||
// return dumpString(""+v);
|
||||
// }
|
||||
|
||||
private String dumpString(String s) {
|
||||
if (s==null) {
|
||||
@@ -123,7 +93,7 @@ public class SpringPropertyIndex extends FuzzyMap<PropertyInfo> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKey(PropertyInfo entry) {
|
||||
protected String getKey(ConfigurationMetadataProperty entry) {
|
||||
return entry.getId();
|
||||
}
|
||||
|
||||
@@ -132,25 +102,25 @@ public class SpringPropertyIndex extends FuzzyMap<PropertyInfo> {
|
||||
* 'string prefix' but a prefix in the sense of treating '.' as a kind of separators. So
|
||||
* 'prefix' is not allowed to end in the middle of a 'segment'.
|
||||
*/
|
||||
public static PropertyInfo findLongestValidProperty(FuzzyMap<PropertyInfo> index, String name) {
|
||||
int bracketPos = name.indexOf('[');
|
||||
int endPos = bracketPos>=0?bracketPos:name.length();
|
||||
PropertyInfo prop = null;
|
||||
String prefix = null;
|
||||
while (endPos>0 && prop==null) {
|
||||
prefix = name.substring(0, endPos);
|
||||
String canonicalPrefix = StringUtil.camelCaseToHyphens(prefix);
|
||||
prop = index.get(canonicalPrefix);
|
||||
if (prop==null) {
|
||||
endPos = name.lastIndexOf('.', endPos-1);
|
||||
}
|
||||
}
|
||||
if (prop!=null) {
|
||||
//We should meet caller's expectation that matched properties returned by this method
|
||||
// match the names exactly even if we found them using relaxed name matching.
|
||||
return prop.withId(prefix);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// public static PropertyInfo findLongestValidProperty(FuzzyMap<PropertyInfo> index, String name) {
|
||||
// int bracketPos = name.indexOf('[');
|
||||
// int endPos = bracketPos>=0?bracketPos:name.length();
|
||||
// PropertyInfo prop = null;
|
||||
// String prefix = null;
|
||||
// while (endPos>0 && prop==null) {
|
||||
// prefix = name.substring(0, endPos);
|
||||
// String canonicalPrefix = StringUtil.camelCaseToHyphens(prefix);
|
||||
// prop = index.get(canonicalPrefix);
|
||||
// if (prop==null) {
|
||||
// endPos = name.lastIndexOf('.', endPos-1);
|
||||
// }
|
||||
// }
|
||||
// if (prop!=null) {
|
||||
// //We should meet caller's expectation that matched properties returned by this method
|
||||
// // match the names exactly even if we found them using relaxed name matching.
|
||||
// return prop.withId(prefix);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Pivotal, Inc.
|
||||
* Copyright (c) 2015, 2017 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
|
||||
@@ -10,11 +10,12 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.metadata;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SpringPropertyIndexProvider {
|
||||
FuzzyMap<PropertyInfo> getIndex(IDocument doc);
|
||||
FuzzyMap<ConfigurationMetadataProperty> getIndex(IDocument doc);
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 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.metadata;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ValueProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.CollectionUtil;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* An instance of this class serves as a 'registry' that associates known
|
||||
* {@link ValueProvider} ids to strategy objects used in the computation of completions
|
||||
* for properties to which the provider is attached.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ValueProviderRegistry {
|
||||
|
||||
private static ValueProviderRegistry DEFAULT;
|
||||
|
||||
/**
|
||||
* Creates a default {@link ValueProviderRegistry} which is initialized with all the known
|
||||
* providers. (This is the one production code should use, test code might make use
|
||||
* something else for mocking purposes).
|
||||
*/
|
||||
public synchronized static ValueProviderRegistry getDefault() {
|
||||
if (DEFAULT==null) {
|
||||
DEFAULT = new ValueProviderRegistry();
|
||||
DEFAULT.initializeDefaults(DEFAULT);
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
protected void initializeDefaults(ValueProviderRegistry r) {
|
||||
def("logger-name", LoggerNameProvider.FACTORY);
|
||||
def("class-reference", ClassReferenceProvider.FACTORY);
|
||||
}
|
||||
|
||||
private Map<String, Function<Map<String, Object>, ValueProviderStrategy>> registry = new HashMap<>();
|
||||
|
||||
public interface ValueProviderStrategy {
|
||||
Flux<StsValueHint> getValues(IJavaProject javaProject, String query);
|
||||
|
||||
default Collection<StsValueHint> getValuesNow(IJavaProject javaProject, String query) {
|
||||
return this.getValues(javaProject, query)
|
||||
.take(CachingValueProvider.TIMEOUT)
|
||||
.collectList()
|
||||
.block();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a value provider by binding its id to a strategy.
|
||||
*/
|
||||
public void def(String id, Function<Map<String, Object>, ValueProviderStrategy> algo) {
|
||||
registry.put(id, algo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of {@link ValueProvider}s to a {@link ValueProviderStrategy}.
|
||||
* <p>
|
||||
* Essentially this finds the first provider from the list which has a known name
|
||||
* and uses that to iinstantiate a ValueProviderStrategy. Spring boot assumes that
|
||||
* a list is provided to allow new providers to be defined that override older ones
|
||||
* and these are added at the top of the list. Thus an older IDE can continue to
|
||||
* function using the older provider further down the list whereas newer IDEs will
|
||||
* use a 'better' one from higher up the list.
|
||||
*/
|
||||
public ValueProviderStrategy resolve(List<ValueProvider> providerDescriptors) {
|
||||
if (CollectionUtil.hasElements(providerDescriptors)) {
|
||||
for (ValueProvider descriptor : providerDescriptors) {
|
||||
Function<Map<String, Object>, ValueProviderStrategy> factory = registry.get(descriptor.getName());
|
||||
if (factory!=null) {
|
||||
Map<String, Object> params = descriptor.getParameters();
|
||||
return factory.apply(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 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.metadata.hints;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.Deprecation;
|
||||
import org.springframework.boot.configurationmetadata.ValueHint;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.DeprecationUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaElement;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Sts version of {@link ValueHint} contains similar data, but accomoates
|
||||
* a html snippet to be computed lazyly for the description.
|
||||
* <p>
|
||||
* This is meant to support using data pulled from JavaDoc in enums as description.
|
||||
* This data is a html snippet, whereas the data derived from spring-boot metadata is
|
||||
* just plain text.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class StsValueHint {
|
||||
|
||||
|
||||
private final String value;
|
||||
private final Renderable description;
|
||||
private final Deprecation deprecation;
|
||||
|
||||
/**
|
||||
* Create a hint with a textual description.
|
||||
* <p>
|
||||
* This constructor is private. Use one of the provided
|
||||
* static 'create' methods instead.
|
||||
*/
|
||||
private StsValueHint(String value, Renderable description, Deprecation deprecation) {
|
||||
this.value = value==null?"null":value.toString();
|
||||
Assert.isLegal(!this.value.startsWith("StsValueHint"));
|
||||
this.description = description;
|
||||
this.deprecation = deprecation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hint out of an IJavaElement.
|
||||
*/
|
||||
public static StsValueHint create(String value, IJavaElement javaElement) {
|
||||
return new StsValueHint(value, javaDocSnippet(javaElement), DeprecationUtil.extract(javaElement)) {
|
||||
@Override
|
||||
public IJavaElement getJavaElement() {
|
||||
return javaElement;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static StsValueHint create(String value) {
|
||||
return new StsValueHint(value, Renderables.NO_DESCRIPTION, null);
|
||||
}
|
||||
|
||||
public static StsValueHint create(ValueHint hint) {
|
||||
return new StsValueHint(""+hint.getValue(), textSnippet(hint.getDescription()), null);
|
||||
}
|
||||
|
||||
public static StsValueHint create(IType klass) {
|
||||
return new StsValueHint(klass.getFullyQualifiedName(), javaDocSnippet(klass), DeprecationUtil.extract(klass)) {
|
||||
@Override
|
||||
public IJavaElement getJavaElement() {
|
||||
return klass;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a html snippet from a text snippet.
|
||||
*/
|
||||
private static Renderable textSnippet(String description) {
|
||||
if (StringUtil.hasText(description)) {
|
||||
return Renderables.text(description);
|
||||
}
|
||||
return Renderables.NO_DESCRIPTION;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Renderable getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
private static Renderable javaDocSnippet(IJavaElement je) {
|
||||
return Renderables.lazy(() -> {
|
||||
IJavadoc jdoc = je.getJavaDoc();
|
||||
if (jdoc != null) {
|
||||
return jdoc.getRenderable();
|
||||
} else {
|
||||
return Renderables.NO_DESCRIPTION;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StsValueHint("+value+")";
|
||||
}
|
||||
|
||||
public Deprecation getDeprecation() {
|
||||
return deprecation;
|
||||
}
|
||||
|
||||
public IJavaElement getJavaElement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public StsValueHint prefixWith(String prefix) {
|
||||
StsValueHint it = this;
|
||||
return new StsValueHint(prefix+getValue(), description, deprecation) {
|
||||
@Override
|
||||
public IJavaElement getJavaElement() {
|
||||
return it.getJavaElement();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 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.metadata.hints;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.util.Renderables.*;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
public class ValueHintHoverInfo {
|
||||
|
||||
public static Renderable create(StsValueHint hint) {
|
||||
Builder<Renderable> builder = ImmutableList.builder();
|
||||
builder.add(bold(""+hint.getValue()));
|
||||
builder.add(paragraph(hint.getDescription()));
|
||||
return concat(builder.build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 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.metadata.util;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.Deprecation;
|
||||
import org.springframework.ide.vscode.commons.java.IAnnotatable;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaElement;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
public class DeprecationUtil {
|
||||
|
||||
private static final ImmutableSet<String> DEPRECATED_ANOT_NAMES = ImmutableSet.of(
|
||||
"org.springframework.boot.context.properties.DeprecatedConfigurationProperty",
|
||||
"DeprecatedConfigurationProperty",
|
||||
"java.lang.Deprecated",
|
||||
"Deprecated"
|
||||
);
|
||||
|
||||
/**
|
||||
* Extract {@link Deprecation} info from annotations on a {@link IJavaElement}
|
||||
*/
|
||||
public static Deprecation extract(IJavaElement je) {
|
||||
Optional<Deprecation> deprecation = Optional.empty();
|
||||
if (je instanceof IAnnotatable) {
|
||||
deprecation = extract((IAnnotatable)je);
|
||||
}
|
||||
return deprecation.isPresent() ? deprecation.get() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract {@link Deprecation} info from annotations on a {@link IJavaElement}
|
||||
*/
|
||||
private static Optional<Deprecation> extract(IAnnotatable m) {
|
||||
return m.getAnnotations().filter(a -> DEPRECATED_ANOT_NAMES.contains(a.getElementName())).map(a -> {
|
||||
Deprecation d = new Deprecation();
|
||||
a.getMemberValuePairs().forEach(pair -> {
|
||||
String name = pair.getMemberName();
|
||||
if (name.equals("reason")) {
|
||||
d.setReason((String) pair.getValue());
|
||||
} else if (name.equals("replacement")) {
|
||||
d.setReplacement((String) pair.getValue());
|
||||
}
|
||||
});
|
||||
return d;
|
||||
}).findFirst();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 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.metadata.util;
|
||||
|
||||
/**
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public interface Listener<T> {
|
||||
|
||||
void changed(T info);
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014 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.metadata.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.ide.vscode.commons.util.ListenerList;
|
||||
|
||||
public class ListenerManager<T> {
|
||||
|
||||
private ListenerList<T> listeners = new ListenerList<>(ListenerList.IDENTITY);
|
||||
|
||||
public void addListener(T l) {
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
public void removeListener(T l) {
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Iterable<T> getListeners() {
|
||||
return (Iterable<T>) Arrays.asList(listeners.getListeners());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -18,10 +18,8 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataPrope
|
||||
import org.springframework.boot.configurationmetadata.Deprecation;
|
||||
import org.springframework.boot.configurationmetadata.ValueHint;
|
||||
import org.springframework.boot.configurationmetadata.ValueProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex;
|
||||
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry;
|
||||
import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
@@ -33,17 +31,16 @@ import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
public class PropertyIndexHarness {
|
||||
|
||||
private Map<String, ConfigurationMetadataProperty> datas = new LinkedHashMap<>();
|
||||
private ValueProviderRegistry valueProviders = ValueProviderRegistry.getDefault();
|
||||
private SpringPropertyIndex index = null;
|
||||
private IJavaProject testProject = null;
|
||||
|
||||
protected final SpringPropertyIndexProvider indexProvider = new SpringPropertyIndexProvider() {
|
||||
@Override
|
||||
public FuzzyMap<PropertyInfo> getIndex(IDocument doc) {
|
||||
public FuzzyMap<ConfigurationMetadataProperty> getIndex(IDocument doc) {
|
||||
synchronized (PropertyIndexHarness.this) {
|
||||
if (index==null) {
|
||||
IClasspath classpath = testProject == null ? null : testProject.getClasspath();
|
||||
index = new SpringPropertyIndex(valueProviders, classpath);
|
||||
index = new SpringPropertyIndex(classpath);
|
||||
for (ConfigurationMetadataProperty propertyInfo : datas.values()) {
|
||||
index.add(propertyInfo);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user