/** * * 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; import com.speedment.common.codegen.Generator; import com.speedment.common.codegen.Meta; import com.speedment.common.codegen.model.ClassOrInterface; import com.speedment.common.codegen.model.File; import com.speedment.common.codegen.model.Import; import com.speedment.runtime.config.*; import com.speedment.runtime.config.trait.HasAlias; import com.speedment.runtime.config.trait.HasEnabled; import com.speedment.runtime.config.trait.HasMainInterface; import com.speedment.runtime.core.exception.SpeedmentException; import java.util.Map; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Stream; import static java.util.Objects.requireNonNull; /** * Something that can translate a {@link Document} into something else. This * interface is implemented to generate more files from the same database * structure. * * @author Per Minborg * @param <DOC> the Document type to use * @param <T> the codegen type to make (Class, Interface or Enum) * @see Document * @since 2.3.0 */ public interface Translator<DOC extends Document & HasMainInterface, T> extends Supplier<File> { /** * Return this node or any ancestral node that is a {@link Project}. If no * such node exists, an {@code IllegalStateException} is thrown. * * @return the project node */ default Optional<Project> project() { return getDocument(Project.class); } /** * Return this node or any ancestral node that is a {@link Dbms}. If no such * node exists, an {@code IllegalStateException} is thrown. * * @return the dbms node */ default Optional<Dbms> dbms() { return getDocument(Dbms.class); } /** * Return this node or any ancestral node that is a {@link Schema}. If no * such node exists, an {@code IllegalStateException} is thrown. * * @return the schema node */ default Optional<Schema> schema() { return getDocument(Schema.class); } /** * Return this node or any ancestral node that is a {@link Table}. If no * such node exists, an {@code IllegalStateException} is thrown. * * @return the table node */ default Optional<Table> table() { return getDocument(Table.class); } /** * Return this node or any ancestral node that is a {@link Column}. If no * such node exists, an {@code IllegalStateException} is thrown. * * @return the column node */ default Optional<Column> column() { return getDocument(Column.class); } /** * Returns a stream over all enabled columns in the node tree. Disabled * nodes will be ignored. * * @return the enabled columns * @see Column * @see HasEnabled#isEnabled() */ default Stream<? extends Column> columns() { return table() .map(Table::columns) .orElse(Stream.empty()) .filter(HasEnabled::test); } /** * Returns a stream over all enabled indexes in the node tree. Disabled * nodes will be ignored. * * @return the enabled indexes * @see Index * @see HasEnabled#isEnabled() */ default Stream<? extends Index> indexes() { return table() .map(Table::indexes) .orElse(Stream.empty()).filter(HasEnabled::test); } /** * Returns a stream over all enabled foreign keys in the node tree. Disabled * nodes will be ignored. * * @return the enabled foreign keys * @see ForeignKey * @see HasEnabled#isEnabled() */ default Stream<? extends ForeignKey> foreignKeys() { return table() .map(Table::foreignKeys) .orElse(Stream.empty()) .filter(HasEnabled::test); } /** * Returns a stream over all enabled primary key columns in the node tree. * Disabled nodes will be ignored. * * @return the enabled primary key columns * @see PrimaryKeyColumn * @see HasEnabled#isEnabled() */ default Stream<? extends PrimaryKeyColumn> primaryKeyColumns() { return table() .map(Table::primaryKeyColumns) .orElse(Stream.empty()) .filter(HasEnabled::test); } /** * The document being translated. * * @return the document */ DOC getDocument(); /** * Returns this node or one of the ancestor nodes if it matches the * specified {@code Class}. If no such node exists, an * {@code IllegalStateException} is thrown. * * @param <E> the type of the class to match * @param clazz the class to match * @return the node found */ default <E extends Document> Optional<E> getDocument(Class<E> clazz) { requireNonNull(clazz); if (clazz.isAssignableFrom(Translator.this.getDocument().mainInterface())) { @SuppressWarnings("unchecked") final E result = (E) Translator.this.getDocument(); return Optional.of(result); } return Translator.this.getDocument() .ancestors() .filter(clazz::isInstance) .map(clazz::cast) .findAny(); } /** * The document being translated wrapped in a {@link HasAlias}. * * @return the document */ default HasAlias getAliasDocument() { return HasAlias.of(Translator.this.getDocument()); } /** * Generates code for the {@link Document} contained in this * {@code Translator}. * * @return the generated meta info */ default Meta<File, String> generate() { return getCodeGenerator().metaOn( get()).findFirst().orElseThrow( () -> new SpeedmentException("Unable to generate Java code") ); } /** * Generates and returns the code translated from the contained * {@link Document} by this {@code Translator}. * * @return the generated code */ default String toCode() { return generate().getResult(); } /** * Returns {@code true} if the file generated by this {@code Translator} * should be located in a {@code .generated.} package. Files located in * such a package will be overwritten each time the generator runs. * * @return {@code true} if located in {@code .generated.}, else {@code false} */ boolean isInGeneratedPackage(); /** * Returns the CodeGen {@link Generator} used by this {@code Translator}. * This can be used to add dependencies to the runtime or to install new * views. * * @return the {@link Generator} used */ Generator getCodeGenerator(); /** * Append an additional action that should be executed as part of the make * process of this translator. This method is useful for adding additional * methods, fields or internal classes based on the configuration model. * <p> * If the action requires additional {@link Import} statements to * be added to the generated file, the {@link #onMake(BiConsumer)} method * should be used instead. * * @param action the action to perform * @see #onMake(BiConsumer) */ default void onMake(Consumer<Builder<T>> action) { onMake((file, builder) -> action.accept(builder)); } /** * Append an additional action that should be executed as part of the make * process of this translator. This method is useful for adding additional * methods, fields or internal classes based on the configuration model. * <p> * This method allows the action to affect both the {@link Builder} and the * file where the class or interface will be located. This should be used * if the specified action require additional {@link Import} statements to * be added to the file. * * @param action the action to perform * @see #onMake(BiConsumer) */ void onMake(BiConsumer<File, Builder<T>> action); /** * Returns a {@code Stream} of the listeners currently part of the make * process of this {@code Translator}. * * @return {@code Stream} of all listeners */ Stream<BiConsumer<File, Builder<T>>> listeners(); /** * The make process is divided into three phases; {@link #PRE_MAKE}, * {@link #MAKE} and {@link #POST_MAKE}, as specified by this enum. * * @author Per Minborg * @since 2.3 */ enum Phase { PRE_MAKE, MAKE, POST_MAKE } /** * A general interface for all builder implementations used by this * {@link Translator}. A builder is finalized using the {@link #build()} * method. * * @param <T> {@link com.speedment.common.codegen.model.Class Class}, * {@link com.speedment.common.codegen.model.Interface Interface} or * {@link com.speedment.common.codegen.model.Enum Enum} to build. * * @author Emil Forslund * @since 2.3 */ interface Builder<T> { /** * Executes the specified code for every document of the specified type * that is found during this build. This method could for an example be * called to add additional methods or fields to the * {@link ClassOrInterface} being built. * <p> * A key and constructor is specified so that the document of the * unknown type can be constructed when the tree is traversed. * * @param <P> the type of the parent document * @param <DOC> the type of the document * @param key the key to react on * @param constructor constructor for the document to react on * @param consumer the code to apply * @return a reference to this {@code Builder} */ default <P extends Document, DOC extends Document> Builder<T> forEvery(String key, BiFunction<P, Map<String, Object>, DOC> constructor, BiConsumer<T, DOC> consumer) { return forEvery(Phase.MAKE, key, constructor, consumer); } /** * Executes the specified code for every document of the specified type * that is found during this build. This method could for an example be * called to add additional methods or fields to the * {@link ClassOrInterface} being built. * <p> * A key and constructor is specified so that the document of the * unknown type can be constructed when the tree is traversed. * * @param <P> the type of the parent document * @param <DOC> the type of the document * @param phase the {@link Phase} to execute in * @param key the key to react on * @param constructor constructor for the document to react on * @param consumer the code to apply * @return a reference to this {@code Builder} */ <P extends Document, DOC extends Document> Builder<T> forEvery(Phase phase, String key, BiFunction<P, Map<String, Object>, DOC> constructor, BiConsumer<T, DOC> consumer); /** * Executes the specified code for the {@link Project} document of this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param consumer the code to apply * @return a reference to this {@code Builder} */ default Builder<T> forEveryProject(BiConsumer<T, Project> consumer) {return forEveryProject(Phase.MAKE, consumer);} /** * Executes the specified code for the {@link Project} document of this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param phase the {@link Phase} to execute in * @param consumer the code to apply * @return a reference to this {@code Builder} */ Builder<T> forEveryProject(Phase phase, BiConsumer<T, Project> consumer); /** * Executes the specified code for every {@link Dbms} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param consumer the code to apply * @return a reference to this {@code Builder} */ default Builder<T> forEveryDbms(BiConsumer<T, Dbms> consumer) { return forEveryDbms(Phase.MAKE, consumer); } /** * Executes the specified code for every {@link Dbms} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param phase the {@link Phase} to execute in * @param consumer the code to apply * @return a reference to this {@code Builder} */ Builder<T> forEveryDbms(Phase phase, BiConsumer<T, Dbms> consumer); /** * Executes the specified code for every {@link Schema} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param consumer the code to apply * @return a reference to this {@code Builder} */ default Builder<T> forEverySchema(BiConsumer<T, Schema> consumer) { return forEverySchema(Phase.MAKE, consumer); } /** * Executes the specified code for every {@link Schema} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param phase the {@link Phase} to execute in * @param consumer the code to apply * @return a reference to this {@code Builder} */ Builder<T> forEverySchema(Phase phase, BiConsumer<T, Schema> consumer); /** * Executes the specified code for every {@link Table} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param consumer the code to apply * @return a reference to this {@code Builder} */ default Builder<T> forEveryTable(BiConsumer<T, Table> consumer) { return forEveryTable(Phase.MAKE, consumer); } /** * Executes the specified code for every {@link Table} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param phase the {@link Phase} to execute in * @param consumer the code to apply * @return a reference to this {@code Builder} */ Builder<T> forEveryTable(Phase phase, BiConsumer<T, Table> consumer); /** * Executes the specified code for every {@link Column} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param consumer the code to apply * @return a reference to this {@code Builder} */ default Builder<T> forEveryColumn(BiConsumer<T, Column> consumer) { return forEveryColumn(Phase.MAKE, consumer); } /** * Executes the specified code for every {@link Column} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param phase the {@link Phase} to execute in * @param consumer the code to apply * @return a reference to this {@code Builder} */ Builder<T> forEveryColumn(Phase phase, BiConsumer<T, Column> consumer); /** * Executes the specified code for every {@link Index} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param consumer the code to apply * @return a reference to this {@code Builder} */ default Builder<T> forEveryIndex(BiConsumer<T, Index> consumer) { return forEveryIndex(Phase.MAKE, consumer); } /** * Executes the specified code for every {@link Index} document in this * build. This method could for an example be called to add additional * methods or fields to the {@link ClassOrInterface} being built. * * @param phase the {@link Phase} to execute in * @param consumer the code to apply * @return a reference to this {@code Builder} */ Builder<T> forEveryIndex(Phase phase, BiConsumer<T, Index> consumer); /** * Executes the specified code for every {@link ForeignKey} document in * this build. This method could for an example be called to add * additional methods or fields to the {@link ClassOrInterface} being * built. * * @param consumer the code to apply * @return a reference to this {@code Builder} */ default Builder<T> forEveryForeignKey(BiConsumer<T, ForeignKey> consumer) { return forEveryForeignKey(Phase.MAKE, consumer); } /** * Executes the specified code for every {@link ForeignKey} document in * this build. This method could for an example be called to add * additional methods or fields to the {@link ClassOrInterface} being * built. * * @param phase the {@link Phase} to execute in * @param consumer the code to apply * @return a reference to this {@code Builder} */ Builder<T> forEveryForeignKey(Phase phase, BiConsumer<T, ForeignKey> consumer); /** * Executes the specified code for every external {@link ForeignKey} * document that is referencing this document during this build. This * method could for an example be called to add additional methods or * fields to the {@link ClassOrInterface} being built. * * @param consumer the code to apply * @return a reference to this {@code Builder} */ default Builder<T> forEveryForeignKeyReferencingThis(BiConsumer<T, ForeignKey> consumer) { return forEveryForeignKeyReferencingThis(Phase.MAKE, consumer); } /** * Executes the specified code for every external {@link ForeignKey} * document that is referencing this document during this build. This * method could for an example be called to add additional methods or * fields to the {@link ClassOrInterface} being built. * * @param phase the {@link Phase} to execute in * @param consumer the code to apply * @return a reference to this {@code Builder} */ Builder<T> forEveryForeignKeyReferencingThis(Phase phase, BiConsumer<T, ForeignKey> consumer); /** * Builds the {@link com.speedment.common.codegen.model.Class Class}, * {@link com.speedment.common.codegen.model.Interface Interface} or * {@link com.speedment.common.codegen.model.Enum Enum} that has been prepared * by this {@code Builder}. * * @return the built instance */ T build(); } }