blob: 1458f92cc03498d1ac7f2d2352c8888855640b44 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 BEA Systems, Inc.
* 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:
* tyeung@bea.com - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.apt.core.internal.env;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.SimpleName;
/*package*/ class EnvUtil {
/**
* Handling the following 2 cases
* 1) For IProblems that does not have a starting and ending offset,
* place the problem at the class name.
*
* 2) For IProblems that does not have an ending offset, place the ending
* offset at the end of the tightest ast node.
* We will only walk the ast once to determine the ending
* offsets of all the problems that do not have the information set.
*/
static void updateProblemLength(List<APTProblem> problems, CompilationUnit astUnit)
{
// for those problems that doesn't have an ending offset, figure it out by
// traversing the ast.
// we do it once just before we post the marker so we only have to walk the ast
// once.
int count = 0;
int[] classNameRange = null;
for(IProblem problem : problems ){
if( problem.getSourceStart() < 0 ){
if( classNameRange == null )
classNameRange = getClassNameRange(astUnit);
problem.setSourceStart(classNameRange[0]);
problem.setSourceEnd(classNameRange[1]);
problem.setSourceLineNumber(classNameRange[2]);
}
if( problem.getSourceEnd() < 0 ){
count ++;
}
}
if( count > 0 ){
if( astUnit != null ){
final int[] startingOffsets = new int[count];
int index = 0;
for( IProblem problem : problems ){
if( problem.getSourceEnd() < 0 )
startingOffsets[index++] = problem.getSourceStart();
}
final EndingOffsetFinder lfinder = new EndingOffsetFinder(startingOffsets);
astUnit.accept( lfinder );
for(IProblem problem : problems ){
if( problem.getSourceEnd() < 0 ){
int startingOffset = problem.getSourceStart();
int endingOffset = lfinder.getEndingOffset(startingOffset);
if( endingOffset == 0 )
endingOffset = startingOffset;
problem.setSourceEnd(endingOffset-1);
}
}
}
else{
for(IProblem problem : problems){
// set the -1 source end to be the same as the source start.
if( problem.getSourceEnd() < problem.getSourceStart() )
problem.setSourceEnd(problem.getSourceStart());
}
}
}
}
/**
* @param file
* @return length 3 int array with the following information.
* at index 0: contains the starting offset, always >= 0
* at index 1: contains the ending offset, may be a negative number.
* at index 2: the line number
*
*/
private static int[] getClassNameRange(final CompilationUnit astUnit){
int[] startAndEnd = null;
if( astUnit != null){
@SuppressWarnings({"unchecked", "nls"})
final List<AbstractTypeDeclaration> topTypes = astUnit.types();
if( topTypes != null && topTypes.size() > 0 ){
final AbstractTypeDeclaration topType = topTypes.get(0);
startAndEnd = new int[3];
final SimpleName typename = topType.getName();
if( typename != null ){
startAndEnd[0] = typename.getStartPosition();
// ending offsets need to be exclusive.
startAndEnd[1] = startAndEnd[0] + typename.getLength() - 1;
startAndEnd[2] = astUnit.getLineNumber(typename.getStartPosition());
if( startAndEnd[2] < 1 )
startAndEnd[2] = 1;
}
else{
startAndEnd[0] = topType.getStartPosition();
// let case 2 in updateProblemLength() kicks in.
startAndEnd[1] = -2;
startAndEnd[2] = astUnit.getLineNumber(topType.getStartPosition());
if( startAndEnd[2] < 1 )
startAndEnd[2] = 1;
}
}
}
if( startAndEnd == null )
// let case 2 in updateProblemLength() kicks in.
return new int[]{0, -2, 1};
return startAndEnd;
}
/**
* Responsible for finding the ending offset of the ast node that has the tightest match
* for a given offset. This ast visitor can operator on an array of offsets in one pass.
* @author tyeung
*/
private static class EndingOffsetFinder extends ASTVisitor
{
private final int[] _sortedStartingOffset;
/**
* parallel to <code>_sortedOffsets</code> and contains
* the ending offset of the ast node that has the tightest match for the
* corresponding starting offset.
*/
private final int[] _endingOffsets;
/**
* @param offsets the array of offsets which will be sorted.
* @throws IllegalArgumentException if <code>offsets</code> is <code>null</code>.
*/
private EndingOffsetFinder(int[] offsets)
{
if(offsets == null)
throw new IllegalArgumentException("argument cannot be null."); //$NON-NLS-1$
// sort the array first
Arrays.sort(offsets);
// look for duplicates.
int count = 0;
for( int i=0, len=offsets.length; i<len; i++){
if( i > 0 && offsets[i-1] == offsets[i] )
continue;
count ++;
}
if( count != offsets.length ){
_sortedStartingOffset = new int[count];
int index = 0;
for( int i=0, len=offsets.length; i<len; i++){
if( i > 0 && offsets[i-1] == offsets[i] )
continue;
_sortedStartingOffset[index++] = offsets[i];
}
}
else{
_sortedStartingOffset = offsets;
}
_endingOffsets = new int[count];
for( int i=0; i<count; i++ )
_endingOffsets[i] = 0;
}
public void preVisit(ASTNode node)
{
final int startingOffset = node.getStartPosition();
final int endingOffset = startingOffset + node.getLength();
// starting offset is inclusive
int startIndex = Arrays.binarySearch(_sortedStartingOffset, startingOffset);
// ending offset is exclusive
int endIndex = Arrays.binarySearch(_sortedStartingOffset, endingOffset);
if( startIndex < 0 )
startIndex = - startIndex - 1;
if( endIndex < 0 )
endIndex = - endIndex - 1;
else
// endIndex needs to be exclusive and we want to
// include the 'endIndex'th entry in our computation.
endIndex ++;
if( startIndex >= _sortedStartingOffset.length )
return;
for( int i=startIndex; i<endIndex; i++ ){
if( _endingOffsets[i] == 0 )
_endingOffsets[i] = endingOffset;
else if( endingOffset < _endingOffsets[i] )
_endingOffsets[i] = endingOffset;
}
}
public int getEndingOffset(final int startingOffset)
{
int index = Arrays.binarySearch(_sortedStartingOffset, startingOffset);
if( index == -1 ) return 0;
return _endingOffsets[index];
}
}
}