/*
* Copyright 2015 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.model.internal.manage.schema.extract;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.gradle.model.internal.manage.schema.ModelSchema;
import org.gradle.model.internal.manage.schema.cache.ModelSchemaCache;
import org.gradle.model.internal.type.ModelType;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
public class DefaultModelSchemaExtractor implements ModelSchemaExtractor {
private final List<? extends ModelSchemaExtractionStrategy> strategies;
public static DefaultModelSchemaExtractor withDefaultStrategies(List<? extends ModelSchemaExtractionStrategy> strategies, ModelSchemaAspectExtractor aspectExtractor) {
return new DefaultModelSchemaExtractor(ImmutableList.<ModelSchemaExtractionStrategy>builder()
.addAll(strategies)
.add(new PrimitiveStrategy())
.add(new EnumStrategy())
.add(new JdkValueTypeStrategy())
.add(new ModelSetStrategy())
.add(new SpecializedMapStrategy())
.add(new ModelMapStrategy())
.add(new JavaUtilCollectionStrategy())
.add(new ManagedImplStructStrategy(aspectExtractor))
.add(new RuleSourceSchemaExtractionStrategy(aspectExtractor))
.add(new UnmanagedImplStructStrategy(aspectExtractor))
.build());
}
public static DefaultModelSchemaExtractor withDefaultStrategies() {
return withDefaultStrategies(Collections.<ModelSchemaExtractionStrategy>emptyList(), new ModelSchemaAspectExtractor());
}
public DefaultModelSchemaExtractor(List<? extends ModelSchemaExtractionStrategy> strategies) {
this.strategies = strategies;
}
@Override
public <T> ModelSchema<T> extract(ModelType<T> type, ModelSchemaCache cache) {
DefaultModelSchemaExtractionContext<T> context = DefaultModelSchemaExtractionContext.root(type);
List<DefaultModelSchemaExtractionContext<?>> validations = Lists.newArrayList();
Queue<DefaultModelSchemaExtractionContext<?>> unsatisfiedDependencies = new ArrayDeque<DefaultModelSchemaExtractionContext<?>>();
DefaultModelSchemaExtractionContext<?> extractionContext = context;
validations.add(extractionContext);
while (extractionContext != null) {
extractSchema(extractionContext, cache);
Iterable<DefaultModelSchemaExtractionContext<?>> dependencies = extractionContext.getChildren();
Iterables.addAll(validations, dependencies);
pushUnsatisfiedDependencies(dependencies, unsatisfiedDependencies, cache);
extractionContext = unsatisfiedDependencies.poll();
}
for (DefaultModelSchemaExtractionContext<?> validationContext : Lists.reverse(validations)) {
// TODO - this will leave invalid types in the cache when it fails
validate(validationContext, cache);
}
return context.getResult();
}
private void pushUnsatisfiedDependencies(Iterable<? extends DefaultModelSchemaExtractionContext<?>> allDependencies, Queue<DefaultModelSchemaExtractionContext<?>> dependencyQueue, final ModelSchemaCache cache) {
Iterables.addAll(dependencyQueue, Iterables.filter(allDependencies, new Predicate<ModelSchemaExtractionContext<?>>() {
public boolean apply(ModelSchemaExtractionContext<?> dependency) {
return cache.get(dependency.getType()) == null;
}
}));
}
private <T> void validate(DefaultModelSchemaExtractionContext<T> extractionContext, ModelSchemaCache cache) {
extractionContext.validate(cache.get(extractionContext.getType()));
}
private <T> void extractSchema(DefaultModelSchemaExtractionContext<T> extractionContext, ModelSchemaCache cache) {
final ModelType<T> type = extractionContext.getType();
ModelSchema<T> cached = cache.get(type);
if (cached != null) {
extractionContext.found(cached);
return;
}
for (ModelSchemaExtractionStrategy strategy : strategies) {
strategy.extract(extractionContext);
if (extractionContext.hasProblems()) {
throw new InvalidManagedModelElementTypeException(extractionContext);
}
if (extractionContext.getResult() != null) {
cache.set(type, extractionContext.getResult());
return;
}
}
// Should never get here, the last strategy should be a catch all
throw new IllegalStateException("No extraction strategy found for type: " + type);
}
}