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

com.speedment.generator.translator.AbstractJavaClassTranslator Maven / Gradle / Ivy

/**
 *
 * Copyright (c) 2006-2016, 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 static com.speedment.common.codegen.constant.DefaultAnnotationUsage.GENERATED;
import static com.speedment.common.codegen.constant.DefaultJavadocTag.AUTHOR;
import com.speedment.common.codegen.controller.AutoImports;
import static com.speedment.common.codegen.internal.util.NullUtil.requireNonNulls;
import com.speedment.common.codegen.model.AnnotationUsage;
import com.speedment.common.codegen.model.Class;
import com.speedment.common.codegen.model.ClassOrInterface;
import com.speedment.common.codegen.model.Constructor;
import com.speedment.common.codegen.model.Enum;
import com.speedment.common.codegen.model.Field;
import com.speedment.common.codegen.model.File;
import com.speedment.common.codegen.model.Interface;
import com.speedment.common.codegen.model.Javadoc;
import com.speedment.common.codegen.model.Value;
import com.speedment.common.injector.Injector;
import com.speedment.common.injector.annotation.Inject;
import com.speedment.common.mapstream.MapStream;
import com.speedment.generator.translator.component.TypeMapperComponent;
import com.speedment.runtime.config.*;
import com.speedment.runtime.config.internal.*;
import com.speedment.runtime.config.trait.HasEnabled;
import com.speedment.runtime.config.trait.HasMainInterface;
import com.speedment.runtime.config.trait.HasName;
import com.speedment.runtime.core.component.InfoComponent;
import java.lang.reflect.Type;
import java.util.*;
import static java.util.Objects.requireNonNull;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 *
 * @param   document type.
 * @param     Java type (Interface, Class or Enum) to generate
 * 
 * @author  Per Minborg
 */
public abstract class AbstractJavaClassTranslator>
    implements JavaClassTranslator {

    public static final String 
        GETTER_METHOD_PREFIX = "get",
        SETTER_METHOD_PREFIX = "set",
        FINDER_METHOD_PREFIX = "find",
        JAVADOC_MESSAGE
        = "\n

\nThis file is safe to edit. It will not be overwritten by the " + "code generator."; private @Inject Generator generator; private @Inject InfoComponent infoComponent; private @Inject TypeMapperComponent typeMappers; private @Inject Injector injector; private final DOC document; private final Function mainModelConstructor; private final List>> listeners; protected AbstractJavaClassTranslator(DOC document, Function mainModelConstructor) { this.document = requireNonNull(document); this.mainModelConstructor = requireNonNull(mainModelConstructor); this.listeners = new CopyOnWriteArrayList<>(); } @Override public final TranslatorSupport getSupport() { return new TranslatorSupport<>(injector, document); } @Override public final DOC getDocument() { return document; } protected AnnotationUsage generated() { final String owner = infoComponent.getTitle(); return GENERATED.set(Value.ofText(owner)); } /** * Returns the name of the {@link Class}/{@link Interface}/{@link Enum} that * this translator will create. The name is only the short name. It does not * include package information. *

* Example: {@code HareImpl} * * @return the short filename of the file to generate */ protected abstract String getClassOrInterfaceName(); /** * Creates and configures a {@link Class}/{@link Interface}/{@link Enum} for * the specified {@code File}. This method uses a builder created using the * {@link #newBuilder(File, String)} method to make sure all the listeners * are notified when the model is built. *

* Observe that this method doesn't add the created model to the file. * * @param file the file to make a model for * @return the model made */ protected abstract T makeCodeGenModel(File file); /** * This method is executed after the file has been created but before it is * returned to the code generator. * * @param file the file to operate on */ protected void finializeFile(File file) { // Do nothing } @Override public File get() { final File file = File.of(getSupport().baseDirectoryName() + "/" + (isInGeneratedPackage() ? "generated/" : "") + getClassOrInterfaceName() + ".java" ); final T item = makeCodeGenModel(file); item.set(getJavaDoc()); file.add(item); finializeFile(file); file.call(new AutoImports(getCodeGenerator().getDependencyMgr())); return file; } protected abstract String getJavadocRepresentText(); protected Javadoc getJavaDoc() { final String owner, message; if (isInGeneratedPackage()) { owner = infoComponent.getTitle(); message = getGeneratedJavadocMessage(); } else { owner = project().get().getCompanyName(); message = JAVADOC_MESSAGE; } return Javadoc.of(getJavadocRepresentText() + message) .add(AUTHOR.setValue(owner)); } @Override public Generator getCodeGenerator() { return generator; } @Override public boolean isInGeneratedPackage() { return false; } @Override public void onMake(BiConsumer> action) { listeners.add(action); } @Override public Stream>> listeners() { return listeners.stream(); } protected final class BuilderImpl implements Translator.Builder { private final static String PROJECTS = "projects"; private final String name; private final Map>>> map; // Special for this case private final Map>> foreignKeyReferencesThisTableConsumers; public BuilderImpl(String name) { this.name = requireNonNull(name); // Phase stuff this.map = new EnumMap<>(Phase.class); this.foreignKeyReferencesThisTableConsumers = new EnumMap<>(Phase.class); for (final Phase phase : Phase.values()) { map.put(phase, new HashMap<>()); foreignKeyReferencesThisTableConsumers.put(phase, new ArrayList<>()); } } @Override public

Builder forEvery(Phase phase, String key, BiFunction, D> constructor, BiConsumer consumer) { aquireListAndAdd(phase, key, wrap(consumer, constructor)); return this; } @Override public BuilderImpl forEveryProject(Phase phase, BiConsumer consumer) { aquireListAndAdd(phase, PROJECTS, wrap(consumer, ProjectImpl::new)); return this; } @Override public BuilderImpl forEveryDbms(Phase phase, BiConsumer consumer) { aquireListAndAdd(phase, Project.DBMSES, wrap(consumer, DbmsImpl::new)); return this; } @Override public BuilderImpl forEverySchema(Phase phase, BiConsumer consumer) { aquireListAndAdd(phase, Dbms.SCHEMAS, wrap(consumer, SchemaImpl::new)); return this; } @Override public BuilderImpl forEveryTable(Phase phase, BiConsumer consumer) { aquireListAndAdd(phase, Schema.TABLES, wrap(consumer, TableImpl::new)); return this; } @Override public BuilderImpl forEveryColumn(Phase phase, BiConsumer consumer) { aquireListAndAdd(phase, Table.COLUMNS, wrap(consumer, ColumnImpl::new)); return this; } @Override public BuilderImpl forEveryIndex(Phase phase, BiConsumer consumer) { aquireListAndAdd(phase, Table.INDEXES, wrap(consumer, IndexImpl::new)); return this; } @Override public BuilderImpl forEveryForeignKey(Phase phase, BiConsumer consumer) { aquireListAndAdd(phase, Table.FOREIGN_KEYS, wrap(consumer, ForeignKeyImpl::new)); return this; } private

BiConsumer wrap(BiConsumer consumer, BiFunction, D> constructor) { return (t, doc) -> { @SuppressWarnings("unchecked") final P parent = (P) doc.getParent().orElse(null); consumer.accept(t, constructor.apply(parent, doc.getData())); }; } @Override public BuilderImpl forEveryForeignKeyReferencingThis(Phase phase, BiConsumer consumer) { foreignKeyReferencesThisTableConsumers.get(phase).add(requireNonNull(consumer)); return this; } @SuppressWarnings("unchecked") protected void aquireListAndAdd(Phase phase, String key, BiConsumer consumer) { aquireList(phase, key).add(requireNonNull(consumer)); } @SuppressWarnings("unchecked") protected List> aquireList(Phase phase, String key) { return (List>) (List) map.get(phase).computeIfAbsent(key, $ -> new CopyOnWriteArrayList<>()); } public void act(Phase phase, String key, T item, D document) { aquireList(phase, key).forEach(c -> c.accept(requireNonNull(item), requireNonNull(document)) ); } @Override public T build() { final T model = mainModelConstructor.apply(name); if (isInGeneratedPackage()) { model.add(generated()); } for (Phase phase : Phase.values()) { project().ifPresent(p -> act(phase, PROJECTS, model, p)); dbms().ifPresent(d -> act(phase, Project.DBMSES, model, d)); schema().ifPresent(s -> act(phase, Dbms.SCHEMAS, model, s)); table().ifPresent(t -> act(phase, Schema.TABLES, model, t)); MapStream.of(map.get(phase)) .flatMapValue(List::stream) .forEachOrdered((key, actor) -> table() .ifPresent(table -> MapStream.of(table.getData()) .filterKey(key::equals) // Filter out elements that map to a list and flat // map the stream so that every value in the list // becomes an element of the stream. .filterValue(List.class::isInstance) .mapValue(v -> { @SuppressWarnings("unchecked") final List> val = (List>) v; return val; }) .flatMapValue(List::stream) // The foreignKeys-property is special in that only // keys that reference enabled and existing table // and columns are to be included. .filter((k, v) -> { if (Table.FOREIGN_KEYS.equals(k)) { return new ForeignKeyImpl(table, v) .foreignKeyColumns() .map(ForeignKeyColumn::findColumn) .allMatch(c -> c.filter(Column::isEnabled) .map(Column::getParentOrThrow) .filter(Table::isEnabled) .isPresent() ); // Indexes, PrimaryKeyColumns and Columns should // be handled as usual. } else return true; }) // We are now done with the keys. Use the values of // the stream to produce an ordinary stream of base // documents. .values() .map(data -> new BaseDocument(table, data)) // All the documents that are enabled should be // passed to the actor. .filter(HasEnabled::test) .forEachOrdered(c -> actor.accept(model, c)) ) ); if (Table.class.equals(getDocument().mainInterface())) { schema().ifPresent(schema -> schema.tables() .filter(HasEnabled::test) .flatMap(t -> t.foreignKeys()) .filter(HasEnabled::test) .filter(fk -> fk.foreignKeyColumns() .filter(fkc -> fkc.getForeignTableName().equals(getDocument().getName())) .filter(HasEnabled::test) .filter(fkc -> fkc.findForeignColumn().map(HasEnabled::test).orElse(false)) .findFirst() .isPresent() ).forEachOrdered(fk -> foreignKeyReferencesThisTableConsumers.get(phase).forEach( c -> c.accept(model, fk) ) ) ); } } return model; } } protected final Builder newBuilder(File file, String className) { requireNonNulls(file, className); final Builder builder = new BuilderImpl(className); listeners().forEachOrdered(action -> action.accept(file, builder)); return builder; } public Field fieldFor(Column c) { return Field.of( getSupport().variableName(c), typeMappers.get(c).getJavaType(c) ); } public Constructor emptyConstructor() { return Constructor.of().public_(); } public enum CopyConstructorMode { SETTER, FIELD } public Constructor copyConstructor(Type type, CopyConstructorMode mode) { final TranslatorSupport support = getSupport(); final Constructor constructor = Constructor.of().protected_() .add(Field.of(support.variableName(), type)); columns().forEachOrdered(c -> { switch (mode) { case FIELD: { constructor.add( "this." + support.variableName(c) + " = " + support.variableName() + "." + GETTER_METHOD_PREFIX + support.typeName(c) + "();"); break; } case SETTER: { if (c.isNullable()) { constructor.add( support.variableName() + "." + GETTER_METHOD_PREFIX + support.typeName(c) + "().ifPresent(this::" + SETTER_METHOD_PREFIX + support.typeName(c) + ");" ); } else { constructor.add( SETTER_METHOD_PREFIX + support.typeName(c) + "(" + support.variableName() + ".get" + support.typeName(c) + "());" ); } break; } default: throw new UnsupportedOperationException( "Unknown mode '" + mode + "'." ); } }); return constructor; } protected String getGeneratedJavadocMessage() { return "\n

\nThis file has been automatically generated by " + infoComponent.getTitle() + ". Any changes made to it will be overwritten."; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy