I have faced in Spray the situation that a segment of a package name of a qualified Java type conflicts with a keyword of the Spray language. Specifically you can refer to custom features with the custom keyword, and the referred type was in a package containing also the name custom.
When inserting the proposed qualified name this results in an error due to the keyword collision.
The qualified name is usually combined through a sequence of ID rules, which allows escaping an identifier by prefixing it with the ^ character in the case of conflict with a keyword.
For content assist it would now be useful if the inserted string would be automatically escaped. So here is a way to solve this.
My first approach was to override the completeJvmParameterizedTypeReference_Type() method from the proposal provider and modify the proposals through a custom ICompletionProposalAcceptor. Sebastian Zarnekow commented that the proper way would be to register a IValueConverter for QualifiedName. I tried to do so, but was not successful. Actually there is already an QualifiedNameValueConverter registered.
My next solution is now to subclass JdtTypesProposalProvider and pass a custom IValueConverter. The overridden methods just pass null as converter, thus do no conversion. I am reusing the QualifiedNameValueConverter here, since it does the necessary keyword escaping.
public class SprayJdtTypesProposalProvider extends JdtTypesProposalProvider {
@Inject
private QualifiedNameValueConverter qnValueConverter;
/**
* Overridden to pass a default value converter
*/
@Override
public void createSubTypeProposals(JvmType superType, ICompletionProposalFactory proposalFactory, ContentAssistContext context, EReference typeReference, Filter filter, ICompletionProposalAcceptor acceptor) {
createSubTypeProposals(superType, proposalFactory, context, typeReference, filter, getConverter(), acceptor);
}
/**
* Overridden to pass a default value converter
*/
@Override
public void createTypeProposals(ICompletionProposalFactory proposalFactory, ContentAssistContext context, EReference typeReference, Filter filter, ICompletionProposalAcceptor acceptor) {
createTypeProposals(proposalFactory, context, typeReference, filter, getConverter(), acceptor);
}
private AbstractValueConverter<String> getConverter() {
return new AbstractValueConverter<String>() {
/**
* Remove ^ character from escaped segments
*/
@Override
public String toValue(String string, INode node) throws ValueConverterException {
return string.replace("^", "");
}
/**
* Escape segments colliding with keywords with ^ character
*/
@Override
public String toString(String value) throws ValueConverterException {
// this converter will escape keywords with ^
return qnValueConverter.toString(value);
}
};
}
}
This custom implementation must be bound to the UI module:
public class SprayUiModule extends AbstractSprayUiModule {
@Override
public Class<? extends ITypesProposalProvider> bindITypesProposalProvider() {
return SprayJdtTypesProposalProvider.class;
}
}
Now the inserted qualified type name will be automatically replaced.