RewriteActionCatalog.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.sandbox.jdt.triggerpattern.api;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/** Immutable catalog of schema-validated structured rewrite actions. */
public final class RewriteActionCatalog {
private static final Set<String> TARGET= Set.of("target"); //$NON-NLS-1$
private static final RewriteActionCatalog STANDARD= builder()
.register(schema("addAnnotation", Set.of("annotation"), Set.of("target", "value"), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"Add a marker, single-value or array annotation to the primary match or an explicit target")) //$NON-NLS-1$
.register(schema("removeAnnotation", Set.of("annotation"), TARGET, //$NON-NLS-1$ //$NON-NLS-2$
"Remove one validated annotation from the primary match or an explicit target")) //$NON-NLS-1$
.register(schema("addModifier", Set.of("modifier"), TARGET, //$NON-NLS-1$ //$NON-NLS-2$
"Add one Java declaration modifier")) //$NON-NLS-1$
.register(schema("removeModifier", Set.of("modifier"), TARGET, //$NON-NLS-1$ //$NON-NLS-2$
"Remove one Java declaration modifier")) //$NON-NLS-1$
.register(schema("removeSupertype", Set.of("type"), TARGET, //$NON-NLS-1$ //$NON-NLS-2$
"Remove one binding-validated superclass or interface")) //$NON-NLS-1$
.register(schema("replaceSupertype", Set.of("type", "replacement"), TARGET, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
"Replace one binding-validated superclass or interface")) //$NON-NLS-1$
.register(schema("removeDeclaration", Set.of(), TARGET, //$NON-NLS-1$
"Remove the primary matched declaration or an explicit target")) //$NON-NLS-1$
.register(schema("qualifyInvocation", Set.of("owner"), TARGET, //$NON-NLS-1$ //$NON-NLS-2$
"Qualify one exact static method invocation")) //$NON-NLS-1$
.register(schema("renameDeclaration", Set.of("name"), TARGET, //$NON-NLS-1$ //$NON-NLS-2$
"Rename one exact planned method or field declaration")) //$NON-NLS-1$
.register(schema("replaceFieldType", Set.of("type"), TARGET, //$NON-NLS-1$ //$NON-NLS-2$
"Replace the type of one single-fragment planned field")) //$NON-NLS-1$
.register(schema("addParameter", Set.of("type", "name"), Set.of("target", "index"), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"Add one parameter to an exact planned method or constructor")) //$NON-NLS-1$
.register(schema("removeParameter", Set.of(), Set.of("target", "name", "index"), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"Remove one parameter selected by stable name or, as an escape hatch, index")) //$NON-NLS-1$
.register(schema("replaceParameterType", Set.of("type"), Set.of("target", "name", "index"), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
"Replace one parameter type selected by stable name or index")) //$NON-NLS-1$
.build();
private final Map<String, RewriteActionSchema> schemas;
private RewriteActionCatalog(Map<String, RewriteActionSchema> schemas) {
this.schemas= Map.copyOf(schemas);
}
/** Returns the canonical built-in action catalog. */
public static RewriteActionCatalog standard() {
return STANDARD;
}
/** Creates an empty catalog builder for tests and extension composition. */
public static Builder builder() {
return new Builder();
}
/** Creates a builder initialized from this immutable catalog. */
public Builder toBuilder() {
return new Builder(schemas);
}
public Optional<RewriteActionSchema> schema(String name) {
return Optional.ofNullable(name == null ? null : schemas.get(name));
}
public Collection<RewriteActionSchema> schemas() {
return schemas.values();
}
public Set<String> names() {
return schemas.keySet();
}
/** Validates one parsed action or rejects an unknown action name. */
public void validate(StructuredRewriteAction action) {
RewriteActionSchema schema= schemas.get(action.name());
if (schema == null) {
throw new IllegalArgumentException("Unknown structured rewrite action " + action.name()); //$NON-NLS-1$
}
schema.validate(action);
}
private static RewriteActionSchema schema(String name, Set<String> required,
Set<String> optional, String description) {
return new RewriteActionSchema(name, required, optional, description);
}
/** Mutable builder that rejects action-name shadowing. */
public static final class Builder {
private final Map<String, RewriteActionSchema> schemas= new LinkedHashMap<>();
private Builder() {
}
private Builder(Map<String, RewriteActionSchema> initial) {
schemas.putAll(initial);
}
public Builder register(RewriteActionSchema schema) {
RewriteActionSchema previous= schemas.putIfAbsent(schema.name(), schema);
if (previous != null && !previous.equals(schema)) {
throw new IllegalArgumentException("Conflicting schema for action " + schema.name()); //$NON-NLS-1$
}
return this;
}
public RewriteActionCatalog build() {
return new RewriteActionCatalog(schemas);
}
}
}