ContainerRuleRegistry.java

/*******************************************************************************
 * Copyright (c) 2026 Carsten Hammer.
 *
 * 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.container.analysis;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.sandbox.jdt.container.api.ContainerRuleDescriptor;
import org.sandbox.jdt.container.api.ContainerRuleDescriptor.RuleOwnership;
import org.sandbox.jdt.container.api.ContainerShape;

/**
 * Central inventory used to prevent semantic container analysis from duplicating
 * existing local cleanups and refactorings.
 */
public final class ContainerRuleRegistry {

	public static final String ARRAY_APPEND_SEQUENCE= "semantic.array.append.sequence"; //$NON-NLS-1$
	public static final String UNIQUE_SEQUENCE_SET= "semantic.sequence.unique.set"; //$NON-NLS-1$
	public static final String UNORDERED_UNIQUE_SEQUENCE_SET= "semantic.sequence.unique.unordered-set"; //$NON-NLS-1$
	public static final String FIFO_SEQUENCE_DEQUE= "semantic.sequence.fifo.deque"; //$NON-NLS-1$
	public static final String LIFO_SEQUENCE_DEQUE= "semantic.sequence.lifo.deque"; //$NON-NLS-1$
	public static final String ENUM_INDEXED_MEMBERSHIP_SET=
			"semantic.array.enum-indexed-membership.set"; //$NON-NLS-1$
	public static final String COLLECTION_BULK_ADD= "existing.collection.bulk-add"; //$NON-NLS-1$
	public static final String COLLECTION_COPY_CONSTRUCTOR= "existing.collection.copy-constructor"; //$NON-NLS-1$
	public static final String ARRAY_FILL= "existing.array.fill"; //$NON-NLS-1$
	public static final String TYPE_GENERALIZATION= "existing.type.generalization"; //$NON-NLS-1$

	private static final Map<String, ContainerRuleDescriptor> RULES= createRules();

	private ContainerRuleRegistry() {
		// Static registry.
	}

	/** Returns one descriptor by stable identifier. */
	public static Optional<ContainerRuleDescriptor> find(String ruleId) {
		return Optional.ofNullable(RULES.get(ruleId));
	}

	/** Returns descriptors in deterministic registration order. */
	public static List<ContainerRuleDescriptor> all() {
		return List.copyOf(RULES.values());
	}

	/** Returns the descriptor for append-array to sequence migration. */
	public static ContainerRuleDescriptor arrayAppendSequence() {
		return RULES.get(ARRAY_APPEND_SEQUENCE);
	}

	/** Returns the descriptor for manually unique sequence to ordered-set migration. */
	public static ContainerRuleDescriptor uniqueSequenceSet() {
		return RULES.get(UNIQUE_SEQUENCE_SET);
	}

	/** Returns the descriptor for manually unique sequence with no observed order. */
	public static ContainerRuleDescriptor unorderedUniqueSequenceSet() {
		return RULES.get(UNORDERED_UNIQUE_SEQUENCE_SET);
	}

	/** Returns the descriptor for FIFO list-to-deque analysis. */
	public static ContainerRuleDescriptor fifoSequenceDeque() {
		return RULES.get(FIFO_SEQUENCE_DEQUE);
	}

	/** Returns the descriptor for LIFO list-to-deque analysis. */
	public static ContainerRuleDescriptor lifoSequenceDeque() {
		return RULES.get(LIFO_SEQUENCE_DEQUE);
	}

	/** Returns the descriptor for enum-ordinal boolean membership to set analysis. */
	public static ContainerRuleDescriptor enumIndexedMembershipSet() {
		return RULES.get(ENUM_INDEXED_MEMBERSHIP_SET);
	}

	private static Map<String, ContainerRuleDescriptor> createRules() {
		Map<String, ContainerRuleDescriptor> rules= new LinkedHashMap<>();
		register(rules, new ContainerRuleDescriptor(
				COLLECTION_BULK_ADD,
				ContainerShape.LIST,
				ContainerShape.LIST,
				RuleOwnership.DUPLICATE,
				"Eclipse collection bulk-add cleanup", //$NON-NLS-1$
				"Local loop-to-bulk-add syntax is already owned by an existing cleanup.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				COLLECTION_COPY_CONSTRUCTOR,
				ContainerShape.LIST,
				ContainerShape.LIST,
				RuleOwnership.DUPLICATE,
				"Eclipse collection copy-constructor cleanup", //$NON-NLS-1$
				"Local construction followed by copying is already normalized elsewhere.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				ARRAY_FILL,
				ContainerShape.ARRAY,
				ContainerShape.ARRAY,
				RuleOwnership.DUPLICATE,
				"Eclipse array-fill cleanup", //$NON-NLS-1$
				"Replacing a uniform assignment loop with array fill is a local syntax cleanup.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				TYPE_GENERALIZATION,
				ContainerShape.LIST,
				ContainerShape.LIST,
				RuleOwnership.DUPLICATE,
				"Eclipse Change Type and Sandbox Use General Type", //$NON-NLS-1$
				"Changing only the declared supertype is already covered by type-generalization tools.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				UNIQUE_SEQUENCE_SET,
				ContainerShape.LIST,
				ContainerShape.SET,
				RuleOwnership.NOVEL,
				"", //$NON-NLS-1$
				"The rule changes duplicate semantics in the declared contract while preserving observed encounter order.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				UNORDERED_UNIQUE_SEQUENCE_SET,
				ContainerShape.LIST,
				ContainerShape.SET,
				RuleOwnership.NOVEL,
				"", //$NON-NLS-1$
				"The complete local flow observes uniqueness but no element order, allowing an unordered set contract without an executable implementation choice.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				FIFO_SEQUENCE_DEQUE,
				ContainerShape.LIST,
				ContainerShape.DEQUE,
				RuleOwnership.NOVEL,
				"", //$NON-NLS-1$
				"The rule replaces numeric head removal with an explicit FIFO container contract.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				LIFO_SEQUENCE_DEQUE,
				ContainerShape.LIST,
				ContainerShape.DEQUE,
				RuleOwnership.NOVEL,
				"", //$NON-NLS-1$
				"The rule replaces size-based tail removal with an explicit LIFO container contract.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				ENUM_INDEXED_MEMBERSHIP_SET,
				ContainerShape.ARRAY,
				ContainerShape.SET,
				RuleOwnership.NOVEL,
				"", //$NON-NLS-1$
				"The rule replaces an internal enum-ordinal boolean table with an explicit enum membership contract; automatic primitive-array rewriting remains excluded.")); //$NON-NLS-1$
		register(rules, new ContainerRuleDescriptor(
				ARRAY_APPEND_SEQUENCE,
				ContainerShape.ARRAY,
				ContainerShape.LIST,
				RuleOwnership.NOVEL,
				"", //$NON-NLS-1$
				"The rule changes representation and may propagate through fields, signatures and callers.")); //$NON-NLS-1$
		return Collections.unmodifiableMap(new LinkedHashMap<>(rules));
	}

	private static void register(
			Map<String, ContainerRuleDescriptor> rules,
			ContainerRuleDescriptor descriptor) {
		if (rules.putIfAbsent(descriptor.ruleId(), descriptor) != null) {
			throw new IllegalStateException("Duplicate container rule id: " + descriptor.ruleId()); //$NON-NLS-1$
		}
	}
}