/*******************************************************************************
* 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.eclipse.boot.properties.editor.metadata;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.IJavaProject;
import org.springframework.boot.configurationmetadata.ValueHint;
import org.springframework.ide.eclipse.boot.properties.editor.metadata.ValueProviderRegistry.ValueProviderStrategy;
import org.springframework.ide.eclipse.boot.properties.editor.util.Type;
import org.springframework.ide.eclipse.boot.properties.editor.util.TypedProperty;
import org.springframework.ide.eclipse.editor.support.util.CollectionUtil;
import org.springframework.ide.eclipse.editor.support.yaml.path.YamlPathSegment;
import com.google.common.collect.ImmutableList;
/**
* Methods for creating hints providers that provide hint in specific kind of context.
*
* @author Kris De Volder
*/
public class HintProviders {
/**
* HintProvider that never returns any hints. This should be used
* instead of null pointer.
*/
public static final HintProvider NULL = new HintProvider() {
@Override
public HintProvider traverse(YamlPathSegment s) throws Exception {
return NULL;
}
@Override
public List<StsValueHint> getValueHints(String query) {
return ImmutableList.of();
}
@Override
public List<TypedProperty> getPropertyHints(String query) {
return ImmutableList.of();
}
};
/**
* Creates a non-context-aware hint provider. Typically a hint provider is created by composing the result of
* this with one or more of the other methods in this class to wrap the basic provider so it becomes context-aware.
*/
public static HintProvider basic(IJavaProject jp, final ImmutableList<ValueHint> valueHints, final ValueProviderStrategy valueProvider) {
if (!CollectionUtil.hasElements(valueHints) && valueProvider==null) {
return NULL;
}
return new BasicHintProvider(jp, valueHints, valueProvider);
}
/**
* Create a hint provider that will return the given hints in the context following
* a traversal that goes down into a 'domain of' context a given number of times.
*/
public static HintProvider forDomainAt(final HintProvider valueHints, final int dim) {
if (isNull(valueHints)) {
return NULL;
}
if (dim==0) {
return forHere(valueHints);
}
return new HintProvider() {
public HintProvider traverse(YamlPathSegment s) throws Exception {
switch (s.getType()) {
case VAL_AT_INDEX:
case VAL_AT_KEY:
return forDomainAt(valueHints, dim-1);
default:
return NULL;
}
}
public List<StsValueHint> getValueHints(String query) {
return ImmutableList.of();
}
@Override
public List<TypedProperty> getPropertyHints(String query) {
return ImmutableList.of();
}
};
}
/**
* Only returns the given hints in this context but not one of its 'sub contexts'.
*/
public static HintProvider forHere(final HintProvider valueHints) {
if (isNull(valueHints)) {
return NULL;
}
return new HintProvider() {
@Override
public HintProvider traverse(YamlPathSegment s) throws Exception {
return NULL;
}
@Override
public List<StsValueHint> getValueHints(String query) {
return valueHints.getValueHints(query);
}
@Override
public List<TypedProperty> getPropertyHints(String query) {
return ImmutableList.of();
}
};
}
/**
* REturns the given hints in this context and any of its subcontexts that expect values.
*/
public static HintProvider forAllValueContexts(final HintProvider valueProvider) {
if (isNull(valueProvider)) {
return NULL;
}
return new HintProvider() {
@Override
public HintProvider traverse(YamlPathSegment s) throws Exception {
switch (s.getType()) {
case VAL_AT_INDEX:
case VAL_AT_KEY:
return this;
default:
return NULL;
}
}
@Override
public List<StsValueHint> getValueHints(String query) {
return valueProvider.getValueHints(query);
}
@Override
public List<TypedProperty> getPropertyHints(String query) {
return ImmutableList.of();
}
};
}
public static boolean isNull(HintProvider p) {
//If everyone is nice and doesn't ever use null pointers then the p==null check is
// not needed. But just in case.
return p == NULL || p==null;
}
public static HintProvider forMap(HintProvider _keyProvider, HintProvider _valueProvider, final Type valueType, final boolean dimensionAware) {
final HintProvider keyProvider = notNull(_keyProvider);
final HintProvider valueProvider = notNull(_valueProvider);
if (isNull(keyProvider) && isNull(valueProvider)) {
return NULL;
}
return new HintProvider() {
@Override
public HintProvider traverse(YamlPathSegment s) throws Exception {
switch (s.getType()) {
case VAL_AT_INDEX:
case VAL_AT_KEY:
if (dimensionAware) {
return forHere(valueProvider);
} else {
return forAllValueContexts(valueProvider);
}
default:
return NULL;
}
}
@Override
public List<StsValueHint> getValueHints(String query) {
if (dimensionAware) {
//pickier, completions only suggested in the domain of map, but not for map itself.
return ImmutableList.of();
} else {
return valueProvider.getValueHints(query);
}
}
@Override
public List<TypedProperty> getPropertyHints(String query) {
List<StsValueHint> keyHints = keyProvider.getValueHints(query);
if (CollectionUtil.hasElements(keyHints)) {
List<TypedProperty> props = new ArrayList<>(keyHints.size());
for (StsValueHint keyHint : keyHints) {
Object key = keyHint.getValue();
if (key instanceof String) {
props.add(new TypedProperty((String)key, valueType, null));
}
}
return props;
}
return ImmutableList.of();
}
};
}
/**
* Protection against bad code passing us null pointers.
*/
private static HintProvider notNull(HintProvider p) {
if (p==null) {
return NULL;
}
return p;
}
}