blob: dd3ecc6a20f2181a5c2716abe5752ad50fa8f9a0 [file] [log] [blame]
/*
* Copyright (c) 2010-2020 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
package org.eclipse.scout.sdk.core.generator.annotation;
import static java.util.Collections.unmodifiableMap;
import static java.util.stream.Collectors.toMap;
import static org.eclipse.scout.sdk.core.generator.transformer.IWorkingCopyTransformer.transformAnnotationElement;
import static org.eclipse.scout.sdk.core.util.Ensure.failOnDuplicates;
import static org.eclipse.scout.sdk.core.util.Ensure.newFail;
import java.util.AbstractMap.SimpleEntry;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
import org.eclipse.scout.sdk.core.builder.ISourceBuilder;
import org.eclipse.scout.sdk.core.builder.java.IJavaSourceBuilder;
import org.eclipse.scout.sdk.core.builder.java.expression.ExpressionBuilder;
import org.eclipse.scout.sdk.core.builder.java.expression.IExpressionBuilder;
import org.eclipse.scout.sdk.core.generator.AbstractJavaElementGenerator;
import org.eclipse.scout.sdk.core.generator.ISourceGenerator;
import org.eclipse.scout.sdk.core.generator.transformer.DefaultWorkingCopyTransformer;
import org.eclipse.scout.sdk.core.generator.transformer.IWorkingCopyTransformer;
import org.eclipse.scout.sdk.core.generator.transformer.SimpleWorkingCopyTransformerBuilder;
import org.eclipse.scout.sdk.core.model.annotation.GeneratedAnnotation;
import org.eclipse.scout.sdk.core.model.api.IAnnotation;
import org.eclipse.scout.sdk.core.util.Ensure;
import org.eclipse.scout.sdk.core.util.JavaTypes;
import org.eclipse.scout.sdk.core.util.Strings;
/**
* <h3>{@link AnnotationGenerator}</h3>
*
* @since 6.1.0
*/
public class AnnotationGenerator<TYPE extends IAnnotationGenerator<TYPE>> extends AbstractJavaElementGenerator<TYPE> implements IAnnotationGenerator<TYPE> {
private final Map<String, ISourceGenerator<IExpressionBuilder<?>>> m_values;
protected AnnotationGenerator(IAnnotation annotation, IWorkingCopyTransformer transformer) {
super(annotation);
withElementName(annotation.type().name());
m_values = annotation.elements().values().stream()
.filter(ae -> !ae.isDefault())
.map(ae -> new SimpleEntry<>(ae.elementName(), transformAnnotationElement(ae, transformer)))
.filter(ae -> ae.getValue().isPresent())
.collect(toMap(SimpleEntry::getKey, se -> se.getValue().get(), failOnDuplicates(), LinkedHashMap::new));
}
protected AnnotationGenerator() {
m_values = new LinkedHashMap<>();
}
/**
* Creates a new {@link IAnnotationGenerator} based on the given {@link IAnnotation}.
* <p>
* <b>Example:</b> See {@link IWorkingCopyTransformer}.
*
* @param annotation
* The {@link IAnnotation} that should be converted to an {@link IAnnotationGenerator}. Must not be
* {@code null}.
* @param transformer
* An optional {@link IWorkingCopyTransformer} callback that is responsible for transforming the annotation
* to a working copy. May be {@code null} if no custom transformation is required and the annotation should
* be converted into a working copy without any modification.
* @return A new {@link IAnnotationGenerator} initialized to generate source that is structurally similar to the one
* from the given {@link IAnnotation}.
* @see DefaultWorkingCopyTransformer
* @see SimpleWorkingCopyTransformerBuilder
*/
public static IAnnotationGenerator<?> create(IAnnotation annotation, IWorkingCopyTransformer transformer) {
return new AnnotationGenerator<>(annotation, transformer);
}
/**
* @return A new empty {@link IAnnotationGenerator}.
*/
public static IAnnotationGenerator<?> create() {
return new AnnotationGenerator<>();
}
/**
* @return A new {@link Override} {@link IAnnotationGenerator}.
*/
public static IAnnotationGenerator<?> createOverride() {
return new AnnotationGenerator<>()
.withElementName(Override.class.getName());
}
/**
* @param typeThatGeneratedTheCode
* The name of the class the generated (derived) element is based on. Must not be blank.
* @return A new {@code Generated} {@link IAnnotationGenerator} with the specified value and a default comment.
*/
public static IAnnotationGenerator<?> createGenerated(CharSequence typeThatGeneratedTheCode) {
return createGenerated(typeThatGeneratedTheCode, "This class is auto generated. No manual modifications recommended.");
}
/**
* @param typeThatGeneratedTheCode
* The name of the class the generated (derived) element is based on. Must not be blank.
* @param comments
* The comment value of the {@code Generated} annotation. May be {@code null}.
* @return A new {@code Generated} {@link IAnnotationGenerator} with the specified value and comment.
*/
public static IAnnotationGenerator<?> createGenerated(CharSequence typeThatGeneratedTheCode, CharSequence comments) {
IAnnotationGenerator<?> result = new AnnotationGenerator<>()
.withElementName(GeneratedAnnotation.TYPE_NAME)
.withElement(GeneratedAnnotation.VALUE_ELEMENT_NAME, b -> b.stringLiteral(Ensure.notBlank(typeThatGeneratedTheCode)));
Strings.notBlank(comments).ifPresent(c -> result.withElement(GeneratedAnnotation.COMMENTS_ELEMENT_NAME, b -> b.stringLiteral(c)));
return result;
}
/**
* @return A new {@link Deprecated} {@link IAnnotationGenerator}.
*/
public static IAnnotationGenerator<?> createDeprecated() {
return new AnnotationGenerator<>()
.withElementName(Deprecated.class.getName());
}
/**
* @param values
* The tokens to suppress. May not be {@code null}.
* @return A new {@link SuppressWarnings} {@link IAnnotationGenerator} with the specified suppression values.
*/
public static IAnnotationGenerator<?> createSuppressWarnings(CharSequence... values) {
return new AnnotationGenerator<>()
.withElementName(SuppressWarnings.class.getName())
.withElement("value", b -> b.stringLiteralArray(values, false, true));
}
@Override
public TYPE withElement(String name, String valueSrc) {
return withElement(name, ISourceGenerator.raw(valueSrc));
}
@Override
public TYPE withElement(String name, ISourceGenerator<IExpressionBuilder<?>> value) {
if (value != null) {
m_values.put(Ensure.notBlank(name), value);
}
return currentInstance();
}
@Override
public Optional<ISourceGenerator<IExpressionBuilder<?>>> element(String name) {
return Optional.ofNullable(m_values.get(name));
}
@Override
public Map<String, ISourceGenerator<IExpressionBuilder<?>>> elements() {
return unmodifiableMap(m_values);
}
@Override
public TYPE withoutElement(String name) {
m_values.remove(name);
return currentInstance();
}
@Override
protected void build(IJavaSourceBuilder<?> builder) {
super.build(builder);
builder.at().ref(elementName().orElseThrow(() -> newFail("Annotation name missing for generator {}", this)));
if (m_values.isEmpty()) {
return;
}
builder.parenthesisOpen();
buildElements(ExpressionBuilder.create(builder));
builder.parenthesisClose();
}
protected void buildElements(IExpressionBuilder<?> builder) {
if (m_values.size() == 1 && m_values.containsKey("value")) {
buildSingleValueAnnotation(builder);
return;
}
buildMultiValueAnnotation(builder);
}
protected void buildMultiValueAnnotation(IExpressionBuilder<?> builder) {
StringBuilder elementDelimiter = new StringBuilder(3);
elementDelimiter.append(JavaTypes.C_COMMA);
if (m_values.size() > 4) {
elementDelimiter.append(builder.context().lineDelimiter());
}
else {
elementDelimiter.append(JavaTypes.C_SPACE);
}
Stream<ISourceGenerator<ISourceBuilder<?>>> elementGenerators = m_values.entrySet().stream()
.<ISourceGenerator<IJavaSourceBuilder<?>>> map(e -> b -> b.append(e.getKey()).equalSign().append(e.getValue().generalize(builder)))
.map(jb -> jb.generalize(builder));
builder.append(elementGenerators, null, elementDelimiter, null);
}
protected void buildSingleValueAnnotation(IExpressionBuilder<?> builder) {
m_values.values().iterator().next().generate(builder);
}
}