/**
*
* Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved.
*
* 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 com.speedment.generator.translator.internal.component;
import com.speedment.common.codegen.model.ClassOrInterface;
import com.speedment.common.injector.Injector;
import com.speedment.common.injector.annotation.Inject;
import com.speedment.common.mapstream.MapStream;
import com.speedment.generator.translator.JavaClassTranslator;
import com.speedment.generator.translator.Translator;
import com.speedment.generator.translator.TranslatorConstructor;
import com.speedment.generator.translator.TranslatorDecorator;
import com.speedment.generator.translator.component.CodeGenerationComponent;
import com.speedment.generator.translator.exception.SpeedmentTranslatorException;
import com.speedment.runtime.config.trait.HasMainInterface;
import com.speedment.runtime.config.trait.HasName;
import java.util.List;
import java.util.Map;
import static java.util.Objects.requireNonNull;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
public final class CodeGenerationComponentImpl implements CodeGenerationComponent {
private @Inject Injector injector;
private final Map<Class<? extends HasMainInterface>, Map<String, TranslatorSettings<?, ?>>> map;
public CodeGenerationComponentImpl() {
map = new ConcurrentHashMap<>();
}
@SuppressWarnings("unchecked")
@Override
public <DOC extends HasName & HasMainInterface, T extends ClassOrInterface<T>>
void put(Class<DOC> docType, Class<T> modelType, String key, TranslatorConstructor<DOC, T> constructor) {
aquireTranslatorSettings(docType, modelType, key).setConstructor(constructor);
}
@Override
public <DOC extends HasName & HasMainInterface, T extends ClassOrInterface<T>>
void add(Class<DOC> docType, Class<T> modelType, String key, TranslatorDecorator<DOC, T> decorator) {
aquireTranslatorSettings(docType, modelType, key).decorators().add(decorator);
}
@Override
public <DOC extends HasName & HasMainInterface, T extends ClassOrInterface<T>>
void remove(Class<DOC> docType, String key) {
aquireTranslatorSettings(docType, null, key).setConstructor(null);
}
private <DOC extends HasName & HasMainInterface, T extends ClassOrInterface<T>>
TranslatorSettings<DOC, T> aquireTranslatorSettings(Class<DOC> docType, Class<T> modelType, String key) {
@SuppressWarnings("unchecked")
final TranslatorSettings<DOC, T> result = (TranslatorSettings<DOC, T>) map
.computeIfAbsent(docType,
s -> new ConcurrentHashMap<>()
).computeIfAbsent(key, k -> new TranslatorSettings<>(k));
return result;
}
@Override
public <DOC extends HasName & HasMainInterface>
Stream<? extends Translator<DOC, ?>> translators(DOC document) {
return translators(document, s -> true);
}
@Override
public Stream<String> translatorKeys() {
return map.values().stream()
.flatMap(m -> MapStream.of(m).filterValue(s -> s.constructor != null).keys())
.distinct();
}
@Override
@SuppressWarnings("unchecked")
public <DOC extends HasName & HasMainInterface, T extends ClassOrInterface<T>>
Translator<DOC, T> findTranslator(DOC document, Class<T> modelType, String key) {
return translators(document, key::equals)
.findAny()
.map(translator -> (Translator<DOC, T>) translator)
.orElseThrow(noTranslatorFound(document, key));
}
@SuppressWarnings("unchecked")
private <DOC extends HasName & HasMainInterface>
Stream<? extends Translator<DOC, ?>> translators(DOC document, Predicate<String> nameFilter) {
return MapStream.of(map)
.filterKey(c -> c.isInstance(document))
.values()
.flatMap(m -> MapStream.of(m).filterKey(nameFilter).values())
.map(s -> (TranslatorSettings<DOC, ?>) s)
.filter(s -> s != null)
.filter(s -> s.getConstructor() != null)
.map(settings -> settings.createDecorated(injector, document));
}
private static Supplier<SpeedmentTranslatorException> noTranslatorFound(HasMainInterface doc, String key) {
return () -> new SpeedmentTranslatorException(
"Found no translator with key '"
+ key + "' for document '"
+ doc.mainInterface().getSimpleName() + "'."
);
}
private final static class TranslatorSettings<DOC extends HasName & HasMainInterface, T extends ClassOrInterface<T>> {
private final String key;
private final List<TranslatorDecorator<DOC, T>> decorators;
private TranslatorConstructor<DOC, T> constructor;
public TranslatorSettings(String key) {
this.key = requireNonNull(key);
this.decorators = new CopyOnWriteArrayList<>();
}
public String key() {
return key;
}
public TranslatorConstructor<DOC, T> getConstructor() {
return constructor;
}
public void setConstructor(TranslatorConstructor<DOC, T> constructor) {
this.constructor = constructor;
}
public List<TranslatorDecorator<DOC, T>> decorators() {
return decorators;
}
public JavaClassTranslator<DOC, T> createDecorated(Injector injector, DOC document) {
@SuppressWarnings("unchecked")
final JavaClassTranslator<DOC, T> translator
= (JavaClassTranslator<DOC, T>) getConstructor().apply(document);
injector.inject(translator);
decorators.stream()
.map(injector::inject)
.forEachOrdered(dec -> dec.apply(translator));
return translator;
}
}
}