Debugging notation parsers
satya - 8/21/2018, 10:35:09 AM
Consider these dependencies
dependencies {
//for dependencies found in artifact repositories you can use
//the group:name:version notation
compile 'commons-lang:commons-lang:2.6'
testCompile 'org.mockito:mockito:1.9.0-rc1'
//map-style notation:
compile group: 'com.google.code.guice', name: 'guice', version: '1.0'
//declaring arbitrary files as dependencies
compile files('hibernate.jar', 'libs/spring.jar')
//putting all jars from 'libs' onto compile classpath
compile fileTree('libs')
}
satya - 8/21/2018, 10:35:24 AM
How does files() become a Dependency??
How does files() become a Dependency??
satya - 8/21/2018, 10:37:07 AM
Here is some earlier research
TypeFilteringNotationConverter
NotationConverter
DependencyFilesNotationConverter
NotationParserBuilder
DependencyNotationParser
DependencyHandler
SelfResolvingDependency
DefaultSelfResolvingDependency
FileCollectionDependency
satya - 8/21/2018, 10:39:01 AM
DependencyHandler is documented here
satya - 8/21/2018, 10:40:27 AM
Start with DependencyNotationParser source code
satya - 8/22/2018, 10:31:10 AM
TypeInfo code
/**
* Type literal, useful for nested Generics.
*/
public class TypeInfo<T> {
private final Class<T> targetType;
public TypeInfo(Class targetType) {
assert targetType != null;
this.targetType = targetType;
}
public Class<T> getTargetType() {
return targetType;
}
}
Just stores a class reference of a specified type.
satya - 8/22/2018, 10:36:55 AM
Here is how a notationpareserbuilder is used
public class DependencyNotationParser {
public static NotationParser<Object, Dependency> parser(Instantiator instantiator,
DefaultProjectDependencyFactory dependencyFactory,
ClassPathRegistry classPathRegistry, FileLookup fileLookup,
RuntimeShadedJarFactory runtimeShadedJarFactory,
CurrentGradleInstallation currentGradleInstallation,
Interner<String> stringInterner) {
return NotationParserBuilder
.toType(Dependency.class)
.fromCharSequence(
new DependencyStringNotationConverter<DefaultExternalModuleDependency>
(instantiator,
DefaultExternalModuleDependency.class,
stringInterner))
.converter(
new DependencyMapNotationConverter<DefaultExternalModuleDependency>
(instantiator,
DefaultExternalModuleDependency.class))
.fromType(FileCollection.class,
new DependencyFilesNotationConverter(instantiator))
.fromType(Project.class,
new DependencyProjectNotationConverter(dependencyFactory))
.fromType(DependencyFactory.ClassPathNotation.class,
new DependencyClassPathNotationConverter(
instantiator,
classPathRegistry,
fileLookup.getFileResolver(),
runtimeShadedJarFactory,
currentGradleInstallation))
.invalidNotationMessage(
"Comprehensive documentation on dependency
notations is available in DSL reference for
DependencyHandler type.")
.toComposite();
}
}
satya - 8/22/2018, 10:57:51 AM
NotationConvertResult
package org.gradle.internal.typeconversion;
public interface NotationConvertResult<T> {
boolean hasResult();
/**
* Invoked when a {@link NotationConverter} is able to convert a notation to a result.
*/
void converted(T result);
}
satya - 8/22/2018, 11:00:30 AM
NotationParser
public interface NotationParser<N, T> {
/**
* @throws UnsupportedNotationException When the supplied notation is not handled by this parser.
* @throws TypeConversionException When the supplied notation cannot be converted to the target type.
*/
T parseNotation(N notation) throws TypeConversionException;
/**
* Describes the formats and values that the parser accepts.
*/
void describe(DiagnosticsVisitor visitor);
}
satya - 8/22/2018, 11:10:51 AM
NotationConverterToNotationParserAdapter
public class NotationConverterToNotationParserAdapter<N, T>
implements NotationParser<N, T> {
private final NotationConverter<? super N, ? extends T> converter;
public NotationConverterToNotationParserAdapter
(NotationConverter<? super N, ? extends T> converter) {
this.converter = converter;
}
public T parseNotation(N notation) throws TypeConversionException {
ResultImpl<T> result = new ResultImpl<T>();
converter.convert(notation, result);
if (!result.hasResult) {
throw new UnsupportedNotationException(notation);
}
return result.result;
}
@Override
public void describe(DiagnosticsVisitor visitor) {
converter.describe(visitor);
}
private static class ResultImpl<T>
implements NotationConvertResult<T> {
private boolean hasResult;
private T result;
public boolean hasResult() {
return hasResult;
}
public void converted(T result) {
hasResult = true;
this.result = result;
}
}
}
satya - 8/22/2018, 1:06:36 PM
TypeFilteringNotationConverter
class TypeFilteringNotationConverter<N, S, T>
implements NotationConverter<N, T> {
private final Class<S> type;
private final NotationConverter<? super S, ? extends T> delegate;
public TypeFilteringNotationConverter(
Class<S> type,
NotationConverter<? super S, ? extends T> delegate) {
this.type = type;
this.delegate = delegate;
}
public void convert(N notation, NotationConvertResult<? super T> result)
throws TypeConversionException {
if (type.isInstance(notation)) {
delegate.convert(type.cast(notation), result);
}
}
@Override
public void describe(DiagnosticsVisitor visitor) {
delegate.describe(visitor);
}
}
satya - 8/22/2018, 1:09:39 PM
NotationConverter
public interface NotationConverter<N, T> {
//Attempt to convert the given notation.
void convert(N notation,
NotationConvertResult<? super T> result)
throws TypeConversionException;
}
satya - 8/22/2018, 1:16:07 PM
DependencyFilesNotationConverter
public class DependencyFilesNotationConverter
implements NotationConverter<FileCollection,
SelfResolvingDependency> {
private final Instantiator instantiator;
public DependencyFilesNotationConverter(Instantiator instantiator) {
this.instantiator = instantiator;
}
@Override
public void describe(DiagnosticsVisitor visitor) {
visitor.candidate("FileCollections").example("files('some.jar', 'someOther.jar')");
}
public void convert(FileCollection notation,
NotationConvertResult<? super SelfResolvingDependency> result)
throws TypeConversionException
{
result.converted(
instantiator
.newInstance(DefaultSelfResolvingDependency.class,
notation));
}
}
satya - 8/22/2018, 1:18:51 PM
Notice the nature of convert()
In the base class it is generic
In the derived class it becomes specific
For each derived class it carries a different type signature!!
satya - 8/22/2018, 1:26:06 PM
DefaultSelfResolvingDependency
public class DefaultSelfResolvingDependency
extends AbstractDependency
implements SelfResolvingDependencyInternal, FileCollectionDependency
{
@Override
public Set<File> resolve() {
return source.getFiles();
}
}
satya - 8/22/2018, 1:26:41 PM
Notice
FileCollectionDependency is an interface
satya - 8/22/2018, 1:27:12 PM
Here is the constructor of DefaultSelfResolvingDependency
public DefaultSelfResolvingDependency(FileCollectionInternal source) {
this.targetComponentId = null;
this.source = source;
}
satya - 8/22/2018, 1:27:31 PM
See how the file collection is passed in
See how the file collection is passed in
satya - 8/22/2018, 1:39:06 PM
High level logic
A notation is an object
it can be a string
it can be a file collection
etc.
A series of converters are registered with the NotationParserBuilder.
Each converter will assess to see if an input notation (object) is allowed by any of the converters
DependencyFileNotationConverter is responsible for converting a file collection to a dependency subclass.
This converter simply instantiates a DefaultSelfResolvingDependency with the notation (FileCollection) as an input to that constructor
satya - 8/22/2018, 1:44:55 PM
Here is the compositeconverter
public class CompositeNotationConverter<N, T>
implements NotationConverter<N, T> {
private final List<NotationConverter<? super N, ? extends T>> converters;
public CompositeNotationConverter(
List<NotationConverter<? super N, ? extends T>> converters) {
this.converters = converters;
}
public void convert(N notation, NotationConvertResult<? super T> result)
throws TypeConversionException {
for (int i = 0; !result.hasResult() && i < converters.size(); i++) {
NotationConverter<? super N, ? extends T> converter = converters.get(i);
converter.convert(notation, result);
}
}
@Override
public void describe(DiagnosticsVisitor visitor) {
for (NotationConverter<? super N, ? extends T> converter : converters) {
converter.describe(visitor);
}
}
}
satya - 8/22/2018, 1:45:28 PM
Notice the decision to convert or not is done by the Converter
Notice the decision to convert or not is done by the Converter
satya - 8/22/2018, 1:45:57 PM
Code returns as soon as the result is secured
Code returns as soon as the result is secured
satya - 8/22/2018, 1:47:18 PM
Notice how the TypeFilteringNotationConverter rejects/selects converter to help the composition
public void convert(N notation, NotationConvertResult<? super T> result)
throws TypeConversionException {
if (type.isInstance(notation)) {
delegate.convert(type.cast(notation), result);
}
}
satya - 8/22/2018, 1:52:13 PM
This appears to be a very round about way to confirm to an expected interface: Dependency
Question has been how to convert an agreeable type to make it look like a dependency
So how to wrap or construct a suitable dependency object
why register converters this way?
isn't there a better way to register on the fly?
Question becomes how to register for type conversion or instantiation at run time. Perhaps an equivalence of replacing property file registrations with code based registrations!!
satya - 8/22/2018, 1:53:46 PM
So DependencyNotationParser is a localized factory registry!!!
So DependencyNotationParser is a localized factory registry!!!
satya - 8/22/2018, 2:06:50 PM
So
//Say given
f(T)
//but you want to allow
f(T1)
f(T2)
....
f(T3)
etc.
//So you do a composite registry
R(
a(T1) -> T
b(T2) -> T
c(T3) -> T )
//ok
f(R(T1))
f(R(T2))
f(R(T3))