/******************************************************************************* * Copyright (c) 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org2.eclipse.php.internal.core.search; import org2.eclipse.php.internal.core.CoreMessages; import org2.eclipse.php.internal.core.ast.nodes.*; public class BreakContinueTargetFinder extends AbstractOccurrencesFinder { private static final String TARGET_OF = CoreMessages .getString("BreakContinueTargetFinder.0"); //$NON-NLS-1$ public static final String ID = "BreakContinueTargetFinder"; //$NON-NLS-1$ private static final int[] TARGETS = { ASTNode.FOR_STATEMENT, ASTNode.WHILE_STATEMENT, ASTNode.SWITCH_STATEMENT, ASTNode.FOR_EACH_STATEMENT, ASTNode.DO_STATEMENT }; private static final int[] STOPPERS = { ASTNode.PROGRAM, ASTNode.FUNCTION_DECLARATION }; private FunctionDeclaration fFunctionDeclaration; private Statement statement; private int nestingLevel; /** * @param root * the AST root * @param node * the selected node * @return returns a message if there is a problem */ public String initialize(Program root, ASTNode node) { fASTRoot = root; nestingLevel = 0; if (node != null) { if (node.getType() == ASTNode.BREAK_STATEMENT) { BreakStatement bStatement = (BreakStatement) node; nestingLevel = getNestingLevel(bStatement.getExpression()); statement = bStatement; return null; } if (node.getType() == ASTNode.CONTINUE_STATEMENT) { ContinueStatement cStatement = (ContinueStatement) node; nestingLevel = getNestingLevel(cStatement.getExpression()); statement = cStatement; return null; } } fDescription = "BreakContinueTargetFinder_occurrence_description"; //$NON-NLS-1$ return fDescription; } /* * Returns the nesting level of the expression. Since the Break and the * Continue can provide an optional nesting level to break or continue, the * expression attached to the statements hold this information. In case the * (optional) expression is null, this method returns a default value of 1. * Also note that the break argument accepts any expression, including a * function result. But since this result is dynamically determined at * runtime, we return a nesting level of 0 in this case. Zero nesting level * will not mark any occurrence for the break or the continue. * * @param expression * * @return The nesting level determined from the expression (>=0). */ protected final int getNestingLevel(Expression expression) { if (expression == null) { return 1; } if (expression.getType() == ASTNode.SCALAR) { Scalar scalar = (Scalar) expression; return Integer.parseInt(scalar.getStringValue()); } return 0; } /* * Returns true if the given node type is one of the TARGET nodes. */ private boolean isTarget(int nodeType) { for (int target : TARGETS) { if (target == nodeType) { return true; } } return false; } /* * Returns true if the given node type is one of the STOPPER nodes. */ private boolean isStopper(int nodeType) { return nodeType == STOPPERS[0] || nodeType == STOPPERS[1]; } /* * (non-Javadoc) * * @see * org2.eclipse.php.internal.ui.search.AbstractOccurrencesFinder#findOccurrences * () */ protected void findOccurrences() { if (nestingLevel == 0) { // do nothing return; } String nestingStr = ""; //$NON-NLS-1$ if (nestingLevel > 1) { nestingStr = ' ' + Integer.toString(nestingLevel); } fDescription = Messages .format( TARGET_OF, (statement.getType() == ASTNode.BREAK_STATEMENT) ? "break" + nestingStr : "continue" + nestingStr); //$NON-NLS-1$ //$NON-NLS-2$ // No need for the visitor. Just traverse up the AST tree and locate the // target. addOccurrences(); } /* * Traverse up the AST tree and locate the node to add as an occurrence * target. */ private void addOccurrences() { boolean targetFound = false; int nestingCount = 0; @SuppressWarnings("unused") int blockEnd = -1; ASTNode parent = statement.getParent(); while (!targetFound) { int type = parent.getType(); if (isStopper(type)) { break; } if (isTarget(type)) { nestingCount++; if (nestingCount == nestingLevel) { // Found the target level fResult.add(new OccurrenceLocation(parent.getStart(), getLength(parent), getOccurrenceType(null), fDescription)); // In cases where we have a block, mark the closing curly // bracket // [Aptana Mod - Removed] // if (blockEnd > -1) { // fResult.add(new OccurrenceLocation(blockEnd - 1, 1, // getOccurrenceType(null), fDescription)); // } targetFound = true; } else { blockEnd = -1; } } else if (type == ASTNode.BLOCK) { blockEnd = parent.getEnd(); } else { blockEnd = -1; } parent = parent.getParent(); } } /* * Returns the length of the target node start. For example: A * WhileStatement will return a length of 5, which is the length of the * keyword 'while'. */ private int getLength(ASTNode parent) { switch (parent.getType()) { case ASTNode.FOR_STATEMENT: return 3; case ASTNode.WHILE_STATEMENT: return 5; case ASTNode.SWITCH_STATEMENT: return 6; case ASTNode.FOR_EACH_STATEMENT: return 7; case ASTNode.DO_STATEMENT: return 2; } return 0; } /* * (non-Javadoc) * * @see org2.eclipse.php.internal.ui.search.AbstractOccurrencesFinder# * getOccurrenceReadWriteType * (org2.eclipse.php.internal.core.ast.nodes.ASTNode) */ protected int getOccurrenceType(ASTNode node) { return IOccurrencesFinder.K_EXIT_POINT_OCCURRENCE; } // public boolean visit(ReturnStatement node) { // fResult.add(new OccurrenceLocation(node.getStart(), node.getLength(), // getOccurrenceType(null), fDescription)); // return super.visit(node); // } public Program getASTRoot() { return fASTRoot; } public String getElementName() { return fFunctionDeclaration.getFunctionName().getName(); } public String getID() { return ID; } public String getJobLabel() { return "BreakContinueTargetFinder_job_label"; //$NON-NLS-1$ } public int getSearchKind() { return IOccurrencesFinder.K_BREAK_TARGET_OCCURRENCE; } public String getUnformattedPluralLabel() { return "BreakContinueTargetFinder_label_plural"; //$NON-NLS-1$ } public String getUnformattedSingularLabel() { return "BreakContinueTargetFinder_label_singular"; //$NON-NLS-1$ } }