/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.internal.typeconversion;
import org.gradle.api.Describable;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class NotationParserBuilder<T> {
private TypeInfo<T> resultingType;
private String invalidNotationMessage;
private Object typeDisplayName;
private boolean implicitConverters = true;
private boolean allowNullInput;
private final Collection<NotationConverter<Object, ? extends T>> notationParsers = new LinkedList<NotationConverter<Object, ? extends T>>();
public static <T> NotationParserBuilder<T> toType(Class<T> resultingType) {
return new NotationParserBuilder<T>(new TypeInfo<T>(resultingType));
}
public static <T> NotationParserBuilder<T> toType(TypeInfo<T> resultingType) {
return new NotationParserBuilder<T>(resultingType);
}
private NotationParserBuilder(TypeInfo<T> resultingType) {
this.resultingType = resultingType;
}
/**
* Specifies the display name for the target type, to use in error messages. By default the target type's simple name is used.
*/
public NotationParserBuilder<T> typeDisplayName(final String name) {
this.typeDisplayName = name;
return this;
}
/**
* Use only those converters that are explicitly registered, and disable any implicit conversion that may normally be done.
*/
public NotationParserBuilder<T> noImplicitConverters() {
implicitConverters = false;
return this;
}
/**
* Allow null as a valid input. The default is to disallow null.
*
* <p>When this is enabled, all converters must be null safe.
*
* TODO - attach the null safety to each converter and infer whether null is a valid input or not.
*/
public NotationParserBuilder<T> allowNullInput() {
allowNullInput = true;
return this;
}
/**
* Adds a converter to use to parse notations. Converters are used in the order added.
*/
public NotationParserBuilder<T> converter(NotationConverter<Object, ? extends T> converter) {
this.notationParsers.add(converter);
return this;
}
/**
* Adds a converter that accepts only notations of the given type.
*/
public <S> NotationParserBuilder<T> fromType(Class<S> notationType, NotationConverter<? super S, ? extends T> converter) {
this.notationParsers.add(new TypeFilteringNotationConverter<Object, S, T>(notationType, converter));
return this;
}
/**
* Adds a converter that accepts any CharSequence notation.
*/
public NotationParserBuilder<T> fromCharSequence(NotationConverter<? super String, ? extends T> converter) {
this.notationParsers.add(new CharSequenceNotationConverter<Object, T>(converter));
return this;
}
/**
* Adds a converter that accepts any CharSequence notation. Can only be used when the target type is String.
*/
public NotationParserBuilder<T> fromCharSequence() {
if (!resultingType.getTargetType().equals(String.class)) {
throw new UnsupportedOperationException("Can only convert from CharSequence when the target type is String.");
}
NotationConverter notationParser = new CharSequenceNotationParser();
fromCharSequence(notationParser);
return this;
}
public NotationParserBuilder<T> invalidNotationMessage(String invalidNotationMessage) {
this.invalidNotationMessage = invalidNotationMessage;
return this;
}
public NotationParser<Object, Set<T>> toFlatteningComposite() {
return wrapInErrorHandling(new FlatteningNotationParser<T>(create()));
}
public NotationParser<Object, T> toComposite() {
return wrapInErrorHandling(create());
}
private <S> NotationParser<Object, S> wrapInErrorHandling(NotationParser<Object, S> parser) {
if (typeDisplayName == null) {
typeDisplayName = new LazyDisplayName<T>(resultingType);
}
return new ErrorHandlingNotationParser<Object, S>(typeDisplayName, invalidNotationMessage, allowNullInput, parser);
}
private NotationParser<Object, T> create() {
List<NotationConverter<Object, ? extends T>> composites = new LinkedList<NotationConverter<Object, ? extends T>>();
if (!resultingType.getTargetType().equals(Object.class) && implicitConverters) {
composites.add(new JustReturningConverter<Object, T>(resultingType.getTargetType()));
}
composites.addAll(this.notationParsers);
return new NotationConverterToNotationParserAdapter<Object, T>(composites.size() == 1 ? composites.get(0) : new CompositeNotationConverter<Object, T>(composites));
}
private static class LazyDisplayName<T> implements Describable {
private final TypeInfo<T> resultingType;
private String displayName;
public LazyDisplayName(TypeInfo<T> resultingType) {
this.resultingType = resultingType;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
if (displayName == null) {
displayName = resultingType.getTargetType().equals(String.class) ? "a String" : ("an object of type " + resultingType.getTargetType().getSimpleName());
}
return displayName;
}
}
}