ContainerUsageProfile.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.api;

import java.util.List;
import java.util.Objects;

/**
 * Immutable semantic description of how one connected container value is used.
 *
 * <p>A profile is an analysis result, not yet a rewrite decision. In particular,
 * {@link AnalysisCompleteness#LOCAL_SEED} means that a useful local motif was found
 * but project-wide data flow, escape and compatibility checks are still outstanding.</p>
 *
 * @param identity stable identity and source anchor of the candidate
 * @param currentShape current structural representation
 * @param elementDomain known element-domain category
 * @param access observed access operations
 * @param orderRequirement ordering required by observed code
 * @param uniquenessRequirement uniqueness required by observed code
 * @param mutationLifecycle observed mutation lifecycle
 * @param nullContract observed null behaviour
 * @param aliasingContract observed aliasing behaviour
 * @param escapeLevel widest known escape boundary
 * @param concurrency observed concurrency and publication behaviour
 * @param completeness completeness of the semantic proof
 * @param evidence source-backed observations supporting the profile
 */
public record ContainerUsageProfile(
		ContainerIdentity identity,
		ContainerShape currentShape,
		ElementDomain elementDomain,
		AccessProfile access,
		OrderRequirement orderRequirement,
		UniquenessRequirement uniquenessRequirement,
		MutationLifecycle mutationLifecycle,
		NullContract nullContract,
		AliasingContract aliasingContract,
		EscapeLevel escapeLevel,
		ConcurrencyProfile concurrency,
		AnalysisCompleteness completeness,
		List<UsageEvidence> evidence) {

	public ContainerUsageProfile {
		Objects.requireNonNull(identity, "identity"); //$NON-NLS-1$
		Objects.requireNonNull(currentShape, "currentShape"); //$NON-NLS-1$
		Objects.requireNonNull(elementDomain, "elementDomain"); //$NON-NLS-1$
		Objects.requireNonNull(access, "access"); //$NON-NLS-1$
		Objects.requireNonNull(orderRequirement, "orderRequirement"); //$NON-NLS-1$
		Objects.requireNonNull(uniquenessRequirement, "uniquenessRequirement"); //$NON-NLS-1$
		Objects.requireNonNull(mutationLifecycle, "mutationLifecycle"); //$NON-NLS-1$
		Objects.requireNonNull(nullContract, "nullContract"); //$NON-NLS-1$
		Objects.requireNonNull(aliasingContract, "aliasingContract"); //$NON-NLS-1$
		Objects.requireNonNull(escapeLevel, "escapeLevel"); //$NON-NLS-1$
		Objects.requireNonNull(concurrency, "concurrency"); //$NON-NLS-1$
		Objects.requireNonNull(completeness, "completeness"); //$NON-NLS-1$
		evidence= List.copyOf(Objects.requireNonNull(evidence, "evidence")); //$NON-NLS-1$
	}

	/** Returns whether a later planner may treat this profile as a complete flow proof. */
	public boolean isFlowComplete() {
		return completeness == AnalysisCompleteness.FLOW_COMPLETE;
	}

	/** Stable identity of the represented value, independent of retained AST nodes. */
	public record ContainerIdentity(String bindingKey, String displayName, int sourceStart, int sourceLength) {

		public ContainerIdentity {
			bindingKey= bindingKey == null ? "" : bindingKey; //$NON-NLS-1$
			displayName= Objects.requireNonNull(displayName, "displayName").strip(); //$NON-NLS-1$
			if (displayName.isEmpty()) {
				throw new IllegalArgumentException("displayName must not be empty"); //$NON-NLS-1$
			}
			if (sourceStart < 0) {
				throw new IllegalArgumentException("sourceStart must not be negative"); //$NON-NLS-1$
			}
			if (sourceLength < 0) {
				throw new IllegalArgumentException("sourceLength must not be negative"); //$NON-NLS-1$
			}
		}

		/** Returns whether binding resolution supplied a stable key. */
		public boolean hasResolvedBinding() {
			return !bindingKey.isBlank();
		}

		/** Returns a deterministic identifier suitable for reports and maps. */
		public String stableId() {
			return hasResolvedBinding() ? bindingKey : displayName + '@' + sourceStart;
		}
	}

	/** Operations observed on the represented value. */
	public record AccessProfile(
			boolean indexedRead,
			boolean indexedWrite,
			boolean append,
			boolean positionalInsert,
			boolean positionalRemove,
			boolean membershipQuery,
			boolean keyLookup) {

		/** Initial access facts for a syntactically recognised append-only array seed. */
		public static AccessProfile appendOnlyArraySeed() {
			return new AccessProfile(false, true, true, false, false, false, false);
		}

		/** Returns whether position is already part of the observed contract. */
		public boolean hasPositionalSemantics() {
			return indexedRead || positionalInsert || positionalRemove;
		}
	}

	/** Structured concurrency facts; unknown values are preferable to guessed guarantees. */
	public record ConcurrencyProfile(
			ThreadExposure exposure,
			SynchronizationKind synchronization,
			IterationSemantics iteration,
			AtomicityRequirement atomicity,
			WorkloadShape workload) {

		public ConcurrencyProfile {
			Objects.requireNonNull(exposure, "exposure"); //$NON-NLS-1$
			Objects.requireNonNull(synchronization, "synchronization"); //$NON-NLS-1$
			Objects.requireNonNull(iteration, "iteration"); //$NON-NLS-1$
			Objects.requireNonNull(atomicity, "atomicity"); //$NON-NLS-1$
			Objects.requireNonNull(workload, "workload"); //$NON-NLS-1$
		}

		/** Returns a profile that makes no concurrency claim. */
		public static ConcurrencyProfile unknown() {
			return new ConcurrencyProfile(
					ThreadExposure.UNKNOWN,
					SynchronizationKind.UNKNOWN,
					IterationSemantics.UNKNOWN,
					AtomicityRequirement.UNKNOWN,
					WorkloadShape.UNKNOWN);
		}
	}

	public enum ElementDomain {
		REFERENCE,
		PRIMITIVE,
		ENUM,
		UNKNOWN
	}

	public enum OrderRequirement {
		NONE,
		ENCOUNTER,
		SORTED,
		POSITIONAL,
		UNKNOWN
	}

	public enum UniquenessRequirement {
		REQUIRED,
		DUPLICATES_ALLOWED,
		UNKNOWN
	}

	public enum MutationLifecycle {
		FIXED,
		BUILD_THEN_FREEZE,
		CONTINUOUSLY_MUTABLE,
		SNAPSHOT_PUBLISHED,
		UNKNOWN
	}

	public enum NullContract {
		ALLOWED,
		REJECTED,
		NOT_APPLICABLE,
		UNKNOWN
	}

	public enum AliasingContract {
		SHARED_MUTATION,
		DEFENSIVE_COPY,
		IDENTITY_OBSERVED,
		NO_OBSERVED_ALIAS,
		UNKNOWN
	}

	public enum EscapeLevel {
		LOCAL,
		FIELD,
		METHOD_BOUNDARY,
		OVERRIDE_FAMILY,
		EXTERNAL_OR_BINARY,
		UNKNOWN
	}

	public enum ThreadExposure {
		THREAD_CONFINED,
		PUBLISHED,
		CALLBACK_SHARED,
		WORKER_SHARED,
		UNKNOWN
	}

	public enum SynchronizationKind {
		NONE,
		INTRINSIC_LOCK,
		EXPLICIT_LOCK,
		SYNCHRONIZED_WRAPPER,
		VOLATILE_SNAPSHOT,
		ATOMIC_REFERENCE,
		CONCURRENT_COLLECTION,
		UNKNOWN
	}

	public enum IterationSemantics {
		LIVE,
		EXTERNALLY_LOCKED,
		WEAKLY_CONSISTENT,
		IMMUTABLE_SNAPSHOT,
		COPY_ON_WRITE,
		UNKNOWN
	}

	public enum AtomicityRequirement {
		INDIVIDUAL_OPERATIONS,
		CHECK_THEN_ACT,
		COMPOUND_UPDATE,
		DRAIN,
		REPLACE_ALL,
		TRANSACTION,
		UNKNOWN
	}

	public enum WorkloadShape {
		READ_MOSTLY,
		WRITE_MOSTLY,
		BALANCED,
		REGISTRATION_HEAVY,
		NOTIFICATION_HEAVY,
		PRODUCER_CONSUMER,
		UNKNOWN
	}

	public enum AnalysisCompleteness {
		LOCAL_SEED,
		LOCAL_USAGE_COMPLETE,
		FLOW_COMPLETE,
		REJECTED
	}
}