ContainerAstFacts.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.List;
import java.util.Optional;
import org.eclipse.jdt.core.dom.ASTMatcher;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
/** Shared, side-effect-free AST facts used by semantic container detectors. */
final class ContainerAstFacts {
private ContainerAstFacts() {
// Static utility.
}
static Expression unwrap(Expression expression) {
Expression result= expression;
while (result instanceof ParenthesizedExpression parenthesized) {
result= parenthesized.getExpression();
}
return result;
}
static ExpressionStatement expressionStatement(Assignment assignment) {
return assignment.getParent() instanceof ExpressionStatement statement ? statement : null;
}
static Assignment assignment(Statement statement) {
if (!(statement instanceof ExpressionStatement expressionStatement)) {
return null;
}
Expression expression= unwrap(expressionStatement.getExpression());
return expression instanceof Assignment assignment ? assignment : null;
}
static Statement nextStatement(Statement statement) {
if (!(statement.getParent() instanceof Block block)) {
return null;
}
List<?> statements= block.statements();
int index= statements.indexOf(statement);
return index >= 0 && index + 1 < statements.size()
? (Statement) statements.get(index + 1)
: null;
}
static boolean isArrayLength(Expression expression, Expression array) {
Expression unwrapped= unwrap(expression);
if (unwrapped instanceof QualifiedName qualifiedName) {
return "length".equals(qualifiedName.getName().getIdentifier()) //$NON-NLS-1$
&& sameVariable(array, qualifiedName.getQualifier());
}
if (unwrapped instanceof FieldAccess fieldAccess) {
return "length".equals(fieldAccess.getName().getIdentifier()) //$NON-NLS-1$
&& sameVariable(array, fieldAccess.getExpression());
}
return false;
}
static boolean isOne(Expression expression) {
Expression unwrapped= unwrap(expression);
Object constant= unwrapped.resolveConstantExpressionValue();
if (constant instanceof Number number) {
return number.longValue() == 1L;
}
return unwrapped instanceof NumberLiteral literal && "1".equals(literal.getToken()); //$NON-NLS-1$
}
static boolean sameVariable(Expression first, Expression second) {
Expression left= unwrap(first);
Expression right= unwrap(second);
Optional<IVariableBinding> leftBinding= variableBinding(left);
Optional<IVariableBinding> rightBinding= variableBinding(right);
if (leftBinding.isPresent() && rightBinding.isPresent()) {
return leftBinding.get().getVariableDeclaration()
.isEqualTo(rightBinding.get().getVariableDeclaration());
}
return left.subtreeMatch(new ASTMatcher(true), right);
}
static Optional<IVariableBinding> variableBinding(Expression expression) {
Expression unwrapped= unwrap(expression);
IBinding binding= null;
if (unwrapped instanceof SimpleName simpleName) {
binding= simpleName.resolveBinding();
} else if (unwrapped instanceof QualifiedName qualifiedName) {
binding= qualifiedName.resolveBinding();
} else if (unwrapped instanceof FieldAccess fieldAccess) {
binding= fieldAccess.resolveFieldBinding();
} else if (unwrapped instanceof SuperFieldAccess superFieldAccess) {
binding= superFieldAccess.resolveFieldBinding();
}
return binding instanceof IVariableBinding variableBinding
? Optional.of(variableBinding)
: Optional.empty();
}
}