All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.speedment.generator.translator.internal.component.CodeGenerationComponentImpl Maven / Gradle / Ivy

Go to download

A Speedment bundle that shades all dependencies into one jar. This is useful when deploying an application on a server.

The newest version!
/*
 *
 * Copyright (c) 2006-2019, 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.codegen.model.Enum;
import com.speedment.common.codegen.model.Interface;
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.TranslatorKey;
import com.speedment.generator.translator.component.CodeGenerationComponent;
import com.speedment.generator.translator.component.DecoratorBuilder;
import com.speedment.generator.translator.component.TranslatorAppender;
import com.speedment.generator.translator.component.function.GenerateClass;
import com.speedment.generator.translator.component.function.GenerateEnum;
import com.speedment.generator.translator.component.function.GenerateInterface;
import com.speedment.generator.translator.exception.SpeedmentTranslatorException;
import com.speedment.generator.translator.internal.SimpleTranslator;
import com.speedment.runtime.config.Dbms;
import com.speedment.runtime.config.Project;
import com.speedment.runtime.config.Schema;
import com.speedment.runtime.config.Table;
import com.speedment.runtime.config.trait.HasMainInterface;
import com.speedment.runtime.config.trait.HasName;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

import static java.util.Objects.requireNonNull;

public final class CodeGenerationComponentImpl implements CodeGenerationComponent {

    private @Inject Injector injector;

    private final Map, Map>> map;

    public CodeGenerationComponentImpl() {
        map = new ConcurrentHashMap<>();
    }

    @Override
    public CodeGenerationComponent newClass(GenerateClass creator) {
        injector.inject(creator);
        put(Project.class,
            com.speedment.common.codegen.model.Class.class,
            creator.getClass().getName(),
            project -> new SimpleTranslator<>(injector, project, creator, false));
        return this;
    }

    @Override
    public CodeGenerationComponent newEnum(GenerateEnum creator) {
        injector.inject(creator);
        put(Project.class,
            Enum.class,
            creator.getClass().getName(),
            project -> new SimpleTranslator<>(injector, project, creator, false));
        return this;
    }

    @Override
    public CodeGenerationComponent newInterface(GenerateInterface creator) {
        injector.inject(creator);
        put(Project.class,
            Interface.class,
            creator.getClass().getName(),
            project -> new SimpleTranslator<>(injector, project, creator, false));
        return this;
    }

    @Override
    public TranslatorAppender forEveryTable() {
        return new TranslatorAppenderImpl<>(Table.class);
    }

    @Override
    public TranslatorAppender forEverySchema() {
        return new TranslatorAppenderImpl<>(Schema.class);
    }

    @Override
    public TranslatorAppender forEveryDbms() {
        return new TranslatorAppenderImpl<>(Dbms.class);
    }

    @Override
    public >
    DecoratorBuilder decorate(TranslatorKey key) {
        return new DecoratorBuilderImpl<>(key);
    }

    @SuppressWarnings("unchecked")
    @Override
    public >
        void put(Class docType, Class modelType, String key, TranslatorConstructor constructor) {
        aquireTranslatorSettings(docType, modelType, key).setConstructor(constructor);
    }

    @Override
    public >
        void add(Class docType, Class modelType, String key, TranslatorDecorator decorator) {
        aquireTranslatorSettings(docType, modelType, key).decorators().add(decorator);
    }

    @Override
    public >
        void remove(Class docType, String key) {
        aquireTranslatorSettings(docType, null, key).setConstructor(null);
    }

    private >
        TranslatorSettings aquireTranslatorSettings(Class docType, Class modelType, String key) {
        @SuppressWarnings("unchecked")
        final TranslatorSettings result = (TranslatorSettings) map
            .computeIfAbsent(docType,
                s -> new ConcurrentHashMap<>()
            ).computeIfAbsent(key, k -> new TranslatorSettings<>(k));

        return result;
    }

    @Override
    public 
        Stream> translators(DOC document) {
        return translators(document, s -> true);
    }

    @Override
    public Stream translatorKeys() {
        return map.values().stream()
            .flatMap(m -> MapStream.of(m).filterValue(s -> s.constructor != null).keys())
            .distinct();
    }

    private MapStream, Map>> translatorKeyClasses() {
        return MapStream.of(map);
    }

    @Override
    @SuppressWarnings("unchecked")
    public >
        Translator findTranslator(DOC document, Class modelType, String key) {
        return translators(document, key::equals)
            .findAny()
            .map(translator -> (Translator) translator)
            .orElseThrow(noTranslatorFound(document, key));
    }

    @SuppressWarnings("unchecked")
    private 
        Stream> translators(DOC document, Predicate nameFilter) {

        return MapStream.of(map)
            .filterKey(c -> c.isInstance(document))
            .values()
            .flatMap(m -> MapStream.of(m).filterKey(nameFilter).values())
            .map(s -> (TranslatorSettings) s)
            .filter(s -> s != null)
            .filter(s -> s.getConstructor() != null)
            .map(settings -> settings.createDecorated(injector, document));
    }

    private static Supplier noTranslatorFound(HasMainInterface doc, String key) {
        return () -> new SpeedmentTranslatorException(
            "Found no translator with key '"
            + key + "' for document '"
            + doc.mainInterface().getSimpleName() + "'."
        );
    }

    private final static class TranslatorSettings> {

        private final String key;
        private final List> decorators;
        private TranslatorConstructor constructor;

        public TranslatorSettings(String key) {
            this.key = requireNonNull(key);
            this.decorators = new CopyOnWriteArrayList<>();
        }

        public String key() {
            return key;
        }

        public TranslatorConstructor getConstructor() {
            return constructor;
        }

        public void setConstructor(TranslatorConstructor constructor) {
            this.constructor = constructor;
        }

        public List> decorators() {
            return decorators;
        }

        public JavaClassTranslator createDecorated(Injector injector, DOC document) {
            @SuppressWarnings("unchecked")
            final JavaClassTranslator translator
                = (JavaClassTranslator) getConstructor().apply(document);

            injector.inject(translator);
            
            decorators.stream()
                .map(injector::inject)
                .forEachOrdered(dec -> dec.apply(translator));

            return translator;
        }
    }

    private final class DecoratorBuilderImpl
        >
    implements DecoratorBuilder {

        private final TranslatorKey key;

        DecoratorBuilderImpl(TranslatorKey key) {
            this.key = requireNonNull(key);
        }

        @Override
        public CodeGenerationComponent preGenerate(Consumer action) {
            final Class documentType = figureOutDocumentType();
            if (Project.class.equals(documentType)) {
                add(figureOutDocumentType(), key, translator ->
                    translator.onMake((file, builder) -> {
                        builder.forEveryProject(Translator.Phase.PRE_MAKE,
                            (model, table) -> action.accept(model));
                    })
                );
            } else if (Table.class.equals(documentType)) {
                add(figureOutDocumentType(), key, translator ->
                    translator.onMake((file, builder) -> {
                        builder.forEveryTable(Translator.Phase.PRE_MAKE,
                            (model, table) -> action.accept(model));
                    })
                );
            } else {
                throw new IllegalArgumentException(
                    "Can not decorate translator with document type " +
                        documentType);
            }

            return CodeGenerationComponentImpl.this;
        }

        @Override
        public CodeGenerationComponent postGenerate(Consumer action) {
            final Class documentType = figureOutDocumentType();
            if (Project.class.equals(documentType)) {
                add(figureOutDocumentType(), key, translator ->
                    translator.onMake((file, builder) -> {
                        builder.forEveryProject(Translator.Phase.POST_MAKE,
                            (model, table) -> action.accept(model));
                    })
                );
            } else if (Table.class.equals(documentType)) {
                add(figureOutDocumentType(), key, translator ->
                    translator.onMake((file, builder) -> {
                        builder.forEveryTable(com.speedment.generator.translator.Translator.Phase.POST_MAKE,
                            (model, table) -> action.accept(model));
                    })
                );
            } else {
                throw new IllegalArgumentException(
                    "Can not decorate translator with document type " +
                        documentType);
            }

            return CodeGenerationComponentImpl.this;
        }

        @SuppressWarnings("unchecked")
        private Class figureOutDocumentType() {
            return (Class) translatorKeyClasses()
                .filterValue(settings -> null != settings.get(key.getKey()))
                .keys().findFirst().orElseThrow(() -> new IllegalArgumentException(
                    "Could not find any translators with key " + key
                ));
        }
    }

    private final class TranslatorAppenderImpl
    implements TranslatorAppender {

        private final Class docClass;

        public TranslatorAppenderImpl(Class docClass) {
            this.docClass = requireNonNull(docClass);
        }

        @Override
        public CodeGenerationComponent newClass(GenerateClass creator) {
            injector.inject(creator);
            put(docClass,
                com.speedment.common.codegen.model.Class.class,
                creator.getClass().getName(),
                project -> new SimpleTranslator<>(injector, project, creator, false));
            return CodeGenerationComponentImpl.this;
        }

        @Override
        public CodeGenerationComponent newEnum(GenerateEnum creator) {
            injector.inject(creator);
            put(docClass,
                Enum.class,
                creator.getClass().getName(),
                project -> new SimpleTranslator<>(injector, project, creator, false));
            return CodeGenerationComponentImpl.this;
        }

        @Override
        public CodeGenerationComponent newInterface(GenerateInterface creator) {
            injector.inject(creator);
            put(docClass,
                Interface.class,
                creator.getClass().getName(),
                project -> new SimpleTranslator<>(injector, project, creator, false));
            return CodeGenerationComponentImpl.this;
        }
    }
}