HintPredicateDefinition.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.List;
import java.util.Objects;

/** Immutable declaration model shared by core parsing and editor tooling. */
public record HintPredicateDefinition(String name, List<String> parameters,
		String expression, int lineNumber) {

	public HintPredicateDefinition {
		name= requireIdentifier(name, "predicate name"); //$NON-NLS-1$
		parameters= List.copyOf(Objects.requireNonNull(parameters, "parameters")); //$NON-NLS-1$
		for (String parameter : parameters) {
			if (parameter == null || parameter.length() < 2 || parameter.charAt(0) != '$') {
				throw new IllegalArgumentException("Predicate parameters must be placeholders: " + parameter); //$NON-NLS-1$
			}
			int end= parameter.endsWith("$") ? parameter.length() - 1 : parameter.length(); //$NON-NLS-1$
			if (end <= 1) {
				throw new IllegalArgumentException("Predicate parameters must be placeholders: " + parameter); //$NON-NLS-1$
			}
			requireIdentifier(parameter.substring(1, end), "predicate parameter"); //$NON-NLS-1$
		}
		if (parameters.stream().distinct().count() != parameters.size()) {
			throw new IllegalArgumentException("Predicate parameters must be unique: " + parameters); //$NON-NLS-1$
		}
		expression= Objects.requireNonNull(expression, "expression").trim(); //$NON-NLS-1$
		if (expression.isEmpty()) {
			throw new IllegalArgumentException("Predicate expression must not be blank"); //$NON-NLS-1$
		}
		if (lineNumber < 1) {
			throw new IllegalArgumentException("Predicate line number must be positive"); //$NON-NLS-1$
		}
	}

	/** Returns a compact signature suitable for outline and content-assist labels. */
	public String signature() {
		return name + '(' + String.join(", ", parameters) + ')'; //$NON-NLS-1$
	}

	private static String requireIdentifier(String value, String label) {
		String identifier= Objects.requireNonNull(value, label).trim();
		if (identifier.isEmpty() || !Character.isJavaIdentifierStart(identifier.charAt(0))) {
			throw new IllegalArgumentException("Invalid " + label + ": " + value); //$NON-NLS-1$ //$NON-NLS-2$
		}
		for (int index= 1; index < identifier.length(); index++) {
			if (!Character.isJavaIdentifierPart(identifier.charAt(index))) {
				throw new IllegalArgumentException("Invalid " + label + ": " + value); //$NON-NLS-1$ //$NON-NLS-2$
			}
		}
		return identifier;
	}
}