JunitHolder.java

package org.sandbox.jdt.internal.corext.fix.helper.lib;

/*-
 * #%L
 * Sandbox junit cleanup
 * %%
 * Copyright (C) 2024 hammer
 * %%
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 * 
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License, v. 2.0 are satisfied: GNU General Public License, version 2
 * with the GNU Classpath Exception which is
 * available at https://www.gnu.org/software/classpath/license.html.
 * 
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 * #L%
 */

import java.util.Map;
import java.util.HashMap;
import java.util.Set;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.sandbox.jdt.triggerpattern.cleanup.MatchHolder;

/**
 * Data holder for JUnit migration operations.
 * <p>
 * This class stores information about AST nodes found during the find phase
 * that need to be transformed during the rewrite phase of JUnit migration.
 * Different plugins use this holder to pass different types of AST nodes
 * (annotations, method invocations, field declarations, etc.) to the rewrite operation.
 * </p>
 * <p>
 * Refactored to use private fields with fluent setters for better encapsulation.
 * A Builder inner class is provided for convenient construction.
 * </p>
 * <p>
 * Implements {@link MatchHolder} interface for type-safe integration with
 * {@link org.sandbox.jdt.triggerpattern.cleanup.AbstractPatternCleanupPlugin}.
 * </p>
 */
public class JunitHolder implements MatchHolder {
	/** The main AST node to be transformed (can be Annotation, MethodInvocation, FieldDeclaration, TypeDeclaration, or ImportDeclaration) */
	private ASTNode minv;
	
	/** Name or identifier associated with the node (e.g., annotation name, method name) */
	private String minvname;
	
	/** Set of already processed nodes to avoid duplicate transformations */
	private Set<ASTNode> nodesprocessed;
	
	/** Additional string value for the transformation */
	private String value;
	
	/** Method invocation reference (if applicable) */
	private MethodInvocation method;
	
	/** Counter for tracking multiple transformations */
	private int count;
	
	/** Additional context information for complex transformations */
	private Object additionalInfo;
	
	/** Placeholder bindings from TriggerPattern (e.g., "$x" -> ASTNode or "$args$" -> List<ASTNode>) */
	private Map<String, Object> bindings = new HashMap<>();

	// ========== Getters ==========
	
	/**
	 * Gets the main AST node.
	 * @return the AST node
	 */
	@Override
	public ASTNode getMinv() {
		return minv;
	}
	
	/**
	 * Gets the name or identifier associated with the node.
	 * @return the name
	 */
	public String getMinvname() {
		return minvname;
	}
	
	/**
	 * Gets the set of already processed nodes.
	 * @return the set of processed nodes
	 */
	public Set<ASTNode> getNodesprocessed() {
		return nodesprocessed;
	}
	
	/**
	 * Gets the additional string value.
	 * @return the value
	 */
	public String getValue() {
		return value;
	}
	
	/**
	 * Gets the method invocation reference.
	 * @return the method invocation
	 */
	public MethodInvocation getMethod() {
		return method;
	}
	
	/**
	 * Gets the counter.
	 * @return the count
	 */
	public int getCount() {
		return count;
	}
	
	/**
	 * Gets the additional context information.
	 * @return the additional info
	 */
	public Object getAdditionalInfo() {
		return additionalInfo;
	}
	
	/**
	 * Gets the placeholder bindings map.
	 * @return the bindings map
	 */
	@Override
	public Map<String, Object> getBindings() {
		return bindings;
	}

	/**
	 * Gets the node as an Annotation.
	 * @return the AST node cast to Annotation
	 */
	@Override
	public Annotation getAnnotation() {
		return (Annotation) minv;
	}

	/**
	 * Gets the node as a MethodInvocation.
	 * @return the AST node cast to MethodInvocation
	 */
	public MethodInvocation getMethodInvocation() {
		return (MethodInvocation) minv;
	}

	/**
	 * Gets the node as an ImportDeclaration.
	 * @return the AST node cast to ImportDeclaration
	 */
	public ImportDeclaration getImportDeclaration() {
		return (ImportDeclaration) minv;
	}

	/**
	 * Gets the node as a FieldDeclaration.
	 * @return the AST node cast to FieldDeclaration
	 */
	public FieldDeclaration getFieldDeclaration() {
		return (FieldDeclaration) minv;
	}

	/**
	 * Gets the node as a TypeDeclaration.
	 * @return the AST node cast to TypeDeclaration
	 */
	public TypeDeclaration getTypeDeclaration() {
		return (TypeDeclaration) minv;
	}
	
	// ========== Fluent Setters for Backward Compatibility ==========
	
	/**
	 * Sets the main AST node.
	 * @param minv the AST node
	 * @return this holder for fluent API
	 */
	public JunitHolder setMinv(ASTNode minv) {
		this.minv = minv;
		return this;
	}
	
	/**
	 * Sets the name or identifier.
	 * @param minvname the name
	 * @return this holder for fluent API
	 */
	public JunitHolder setMinvname(String minvname) {
		this.minvname = minvname;
		return this;
	}
	
	/**
	 * Sets the set of already processed nodes.
	 * @param nodesprocessed the set of processed nodes
	 * @return this holder for fluent API
	 */
	public JunitHolder setNodesprocessed(Set<ASTNode> nodesprocessed) {
		this.nodesprocessed = nodesprocessed;
		return this;
	}
	
	/**
	 * Sets the additional string value.
	 * @param value the value
	 * @return this holder for fluent API
	 */
	public JunitHolder setValue(String value) {
		this.value = value;
		return this;
	}
	
	/**
	 * Sets the method invocation reference.
	 * @param method the method invocation
	 * @return this holder for fluent API
	 */
	public JunitHolder setMethod(MethodInvocation method) {
		this.method = method;
		return this;
	}
	
	/**
	 * Sets the counter.
	 * @param count the count
	 * @return this holder for fluent API
	 */
	public JunitHolder setCount(int count) {
		this.count = count;
		return this;
	}
	
	/**
	 * Sets the additional context information.
	 * @param additionalInfo the additional info
	 * @return this holder for fluent API
	 */
	public JunitHolder setAdditionalInfo(Object additionalInfo) {
		this.additionalInfo = additionalInfo;
		return this;
	}
	
	/**
	 * Sets the placeholder bindings map.
	 * @param bindings the bindings map, may be {@code null} to reset to an empty map
	 * @return this holder for fluent API
	 */
	public JunitHolder setBindings(Map<String, Object> bindings) {
		this.bindings = bindings != null ? bindings : new HashMap<>();
		return this;
	}
	
	/**
	 * Gets a placeholder binding as an Expression.
	 * 
	 * @param placeholder the placeholder name (e.g., "$x")
	 * @return the bound expression, or null if not found or if it's a list binding
	 */
	@Override
	public Expression getBindingAsExpression(String placeholder) {
		Object value = bindings.get(placeholder);
		if (value instanceof Expression) {
			return (Expression) value;
		}
		return null;
	}
	
	/**
	 * Gets a placeholder binding.
	 * 
	 * @param placeholder the placeholder name (e.g., "$x")
	 * @return the bound AST node, or null if not found or if it's a list binding
	 */
	public ASTNode getBinding(String placeholder) {
		Object value = bindings.get(placeholder);
		if (value instanceof ASTNode) {
			return (ASTNode) value;
		}
		return null;
	}
	
	/**
	 * Checks if a placeholder binding exists.
	 * 
	 * @param placeholder the placeholder name
	 * @return true if the binding exists
	 */
	public boolean hasBinding(String placeholder) {
		return bindings.containsKey(placeholder);
	}
	
	// ========== Builder ==========
	
	/**
	 * Builder for creating JunitHolder instances with a fluent API.
	 * Provides a type-safe way to construct holders with only the required fields set.
	 */
	public static class Builder {
		private final JunitHolder holder;
		
		/**
		 * Creates a new builder instance.
		 */
		public Builder() {
			this.holder = new JunitHolder();
		}
		
		/**
		 * Sets the main AST node.
		 * @param minv the AST node
		 * @return this builder
		 */
		public Builder minv(ASTNode minv) {
			holder.setMinv(minv);
			return this;
		}
		
		/**
		 * Sets the name or identifier.
		 * @param minvname the name
		 * @return this builder
		 */
		public Builder minvname(String minvname) {
			holder.setMinvname(minvname);
			return this;
		}
		
		/**
		 * Sets the set of already processed nodes.
		 * @param nodesprocessed the set of processed nodes
		 * @return this builder
		 */
		public Builder nodesprocessed(Set<ASTNode> nodesprocessed) {
			holder.setNodesprocessed(nodesprocessed);
			return this;
		}
		
		/**
		 * Sets the additional string value.
		 * @param value the value
		 * @return this builder
		 */
		public Builder value(String value) {
			holder.setValue(value);
			return this;
		}
		
		/**
		 * Sets the method invocation reference.
		 * @param method the method invocation
		 * @return this builder
		 */
		public Builder method(MethodInvocation method) {
			holder.setMethod(method);
			return this;
		}
		
		/**
		 * Sets the counter.
		 * @param count the count
		 * @return this builder
		 */
		public Builder count(int count) {
			holder.setCount(count);
			return this;
		}
		
		/**
		 * Sets the additional context information.
		 * @param additionalInfo the additional info
		 * @return this builder
		 */
		public Builder additionalInfo(Object additionalInfo) {
			holder.setAdditionalInfo(additionalInfo);
			return this;
		}
		
		/**
		 * Sets the placeholder bindings map.
		 * @param bindings the bindings map
		 * @return this builder
		 * @throws IllegalArgumentException if {@code bindings} is {@code null}
		 */
		public Builder bindings(Map<String, Object> bindings) {
			if (bindings == null) {
				throw new IllegalArgumentException("bindings must not be null");
			}
			holder.setBindings(bindings);
			return this;
		}
		
		/**
		 * Builds the JunitHolder instance.
		 * @return the constructed holder
		 */
		public JunitHolder build() {
			return holder;
		}
	}
	
	/**
	 * Creates a new builder instance.
	 * @return a new builder
	 */
	public static Builder builder() {
		return new Builder();
	}
}