Add support for generating methods
Add `GeneratedMethods` and `GeneratedMethod` support classes which can be used during code generation to generation additional methods that will ultimately be included in a `JavaFile`. See gh-28414
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.aot.generate;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.MethodSpec.Builder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A generated method.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @see GeneratedMethods
|
||||
* @see MethodGenerator
|
||||
*/
|
||||
public final class GeneratedMethod {
|
||||
|
||||
private final String name;
|
||||
|
||||
private MethodSpec spec;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link GeneratedMethod} instance with the given name. This
|
||||
* constructor is package-private since names should only be generated via a
|
||||
* {@link GeneratedMethods}.
|
||||
* @param name the generated name
|
||||
*/
|
||||
GeneratedMethod(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the generated name of the method.
|
||||
* @return the name of the generated method
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link MethodSpec} for this generated method.
|
||||
* @return the method spec
|
||||
* @throws IllegalStateException if one of the {@code generateBy(...)}
|
||||
* methods has not been called
|
||||
*/
|
||||
public MethodSpec getSpec() {
|
||||
Assert.state(this.spec != null,
|
||||
() -> String.format("Method '%s' has no method spec defined", this.name));
|
||||
return this.spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the method using the given consumer.
|
||||
* @param builder a consumer that will accept a method spec builder and
|
||||
* configure it as necessary
|
||||
* @return this instance
|
||||
*/
|
||||
public GeneratedMethod using(Consumer<MethodSpec.Builder> builder) {
|
||||
Builder builderToUse = MethodSpec.methodBuilder(this.name);
|
||||
builder.accept(builderToUse);
|
||||
MethodSpec spec = builderToUse.build();
|
||||
assertNameHasNotBeenChanged(spec);
|
||||
this.spec = spec;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void assertNameHasNotBeenChanged(MethodSpec spec) {
|
||||
Assert.isTrue(this.name.toString().equals(spec.name), () -> String
|
||||
.format("'spec' must use the generated name \"%s\"", this.name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (this.spec != null) ? this.spec.toString() : this.name.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.aot.generate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A managed collection of generated methods.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @see GeneratedMethod
|
||||
*/
|
||||
public class GeneratedMethods implements Iterable<GeneratedMethod>, MethodGenerator {
|
||||
|
||||
private final MethodNameGenerator methodNameGenerator;
|
||||
|
||||
private final List<GeneratedMethod> generatedMethods = new ArrayList<>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link GeneratedMethods} instance backed by a new
|
||||
* {@link MethodNameGenerator}.
|
||||
*/
|
||||
public GeneratedMethods() {
|
||||
this(new MethodNameGenerator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link GeneratedMethods} instance backed by the given
|
||||
* {@link MethodNameGenerator}.
|
||||
* @param methodNameGenerator the method name generator
|
||||
*/
|
||||
public GeneratedMethods(MethodNameGenerator methodNameGenerator) {
|
||||
Assert.notNull(methodNameGenerator, "'methodNameGenerator' must not be null");
|
||||
this.methodNameGenerator = methodNameGenerator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public GeneratedMethod generateMethod(Object... methodNameParts) {
|
||||
return add(methodNameParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new {@link GeneratedMethod}. The returned instance must define the
|
||||
* method spec by calling {@code using(builder -> ...)}.
|
||||
* @param methodNameParts the method name parts that should be used to
|
||||
* generate a unique method name
|
||||
* @return the newly added {@link GeneratedMethod}
|
||||
*/
|
||||
public GeneratedMethod add(Object... methodNameParts) {
|
||||
GeneratedMethod method = new GeneratedMethod(
|
||||
this.methodNameGenerator.generateMethodName(methodNameParts));
|
||||
this.generatedMethods.add(method);
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the given action with each of the {@link MethodSpec MethodSpecs}
|
||||
* that have been added to this collection.
|
||||
* @param action the action to perform
|
||||
*/
|
||||
public void doWithMethodSpecs(Consumer<MethodSpec> action) {
|
||||
stream().map(GeneratedMethod::getSpec).forEach(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<GeneratedMethod> iterator() {
|
||||
return this.generatedMethods.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Stream} of all the methods in this collection.
|
||||
* @return a stream of {@link GeneratedMethod} instances
|
||||
*/
|
||||
public Stream<GeneratedMethod> stream() {
|
||||
return this.generatedMethods.stream();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.aot.generate;
|
||||
|
||||
/**
|
||||
* Generates new {@link GeneratedMethod} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @see GeneratedMethods
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MethodGenerator {
|
||||
|
||||
/**
|
||||
* Generate a new {@link GeneratedMethod}. The returned instance must define
|
||||
* the method spec by calling {@code using(builder -> ...)}.
|
||||
* @param methodNameParts the method name parts that should be used to
|
||||
* generate a unique method name
|
||||
* @return the newly added {@link GeneratedMethod}
|
||||
*/
|
||||
GeneratedMethod generateMethod(Object... methodNameParts);
|
||||
|
||||
/**
|
||||
* Return a new {@link MethodGenerator} instance that generates method with
|
||||
* additional implicit method name parts. The final generated name will be
|
||||
* of the following form:
|
||||
* <p>
|
||||
* <table border="1">
|
||||
* <tr>
|
||||
* <th>Original</th>
|
||||
* <th>Updated</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>run</td>
|
||||
* <td><name>Run</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>getValue</td>
|
||||
* <td>get<Name>Value</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>setValue</td>
|
||||
* <td>set<Name>Value</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>isEnabled</td>
|
||||
* <td>is<Name>Enabled</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* @param nameParts the implicit name parts
|
||||
* @return a new {@link MethodGenerator} instance
|
||||
*/
|
||||
default MethodGenerator withName(Object... nameParts) {
|
||||
return new MethodGeneratorWithName(this, nameParts);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.aot.generate;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Internal class used to support {@link MethodGenerator#withName(Object...)}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class MethodGeneratorWithName implements MethodGenerator {
|
||||
|
||||
private static final String[] PREFIXES = { "get", "set", "is" };
|
||||
|
||||
private final MethodGenerator methodGenerator;
|
||||
|
||||
private final Object[] nameParts;
|
||||
|
||||
|
||||
MethodGeneratorWithName(MethodGenerator methodGenerator, Object[] nameParts) {
|
||||
this.methodGenerator = methodGenerator;
|
||||
this.nameParts = nameParts;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public GeneratedMethod generateMethod(Object... methodNameParts) {
|
||||
return this.methodGenerator.generateMethod(generateName(methodNameParts));
|
||||
}
|
||||
|
||||
private Object[] generateName(Object... methodNameParts) {
|
||||
String joined = MethodNameGenerator.join(methodNameParts);
|
||||
String prefix = getPrefix(joined);
|
||||
String suffix = joined.substring(prefix.length());
|
||||
Object[] result = this.nameParts;
|
||||
if (StringUtils.hasLength(prefix)) {
|
||||
result = ObjectUtils.addObjectToArray(result, prefix, 0);
|
||||
}
|
||||
if (StringUtils.hasLength(suffix)) {
|
||||
result = ObjectUtils.addObjectToArray(result, suffix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getPrefix(String name) {
|
||||
for (String candidate : PREFIXES) {
|
||||
if (name.startsWith(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user