/*
* #%~
* org.overture.ide.ui
* %%
* Copyright (C) 2008 - 2014 Overture
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #~%
*/
package org.overture.ide.ui.utility.ast;
import java.io.File;
import java.util.List;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.intf.lex.ILexLocation;
import org.overture.ast.node.INode;
public class AstLocationSearcher2 extends AstLocationSearcherBase
{
/**
*
*/
private static final long serialVersionUID = 1L;
public static class TextReference
{
int offset;
File source;
public TextReference(File source, int offset)
{
this.source = source;
this.offset = offset;
}
public boolean contains(int startOffset, int endOffset, File file)
{
return (source.equals(file) && offset >= startOffset && offset <= endOffset);
}
public boolean canMatch(File file)
{
return source.equals(file);
}
}
public static class LocationFound extends AnalysisException
{
/**
*
*/
private static final long serialVersionUID = 1L;
public AstLocation location;
public LocationFound(AstLocation location)
{
this.location = location;
}
}
public static class AstLocation
{
public INode node;
public ILexLocation location;
public AstLocation(INode node, ILexLocation location)
{
this.node = node;
this.location = location;
}
/**
* Calculates the span of the location.
*
* @return
*/
public int getSpan()
{
return location.getEndOffset() - location.getStartOffset();
}
/**
* Gets the smallest distance from this node to the offset, this might be either the start offset or the end
* offset
*
* @param offset
* @return
*/
public int getDistance(int offset)
{
int startDistance = Math.abs(offset - location.getStartOffset());
int endDistance = Math.abs(offset - location.getEndOffset());
if (startDistance < endDistance)
{
return startDistance;
}
return endDistance;
}
}
/**
* The text reference used to search for a coresponding node
*/
private TextReference reference;
/**
* tracks the last "current" node visited, this have to be the owner of any location
*/
private INode current;
/**
* closest node to text reference
*/
private AstLocation closest;
/**
* Never called by analysis from super
*/
@Override
public void caseILexLocation(ILexLocation node) throws AnalysisException
{
AstLocation location = new AstLocation(current, node);
if (reference.contains(node.getStartOffset(), node.getEndOffset(), node.getFile()))// we need to do set some
// upper limit on the
// precision here. e.g. an
// operation may match but
// it has a body that may
// contain a better match
{
throw new LocationFound(location);
} else if(reference.canMatch(node.getFile()))
{
closest = getClosest(reference, location);
}
}
private AstLocation getClosest(TextReference reference, AstLocation choice)
{
if(closest==null)
{
return choice;
}
int closestDistance = closest.getDistance(reference.offset);
int choiceDistance = choice.getDistance(reference.offset);
if(choiceDistance<closestDistance)
{
return choice;
}else if(choiceDistance == closestDistance)
{
if(choice.getSpan()<closest.getSpan())//consider offset here to prefer after or before nodes
{
return choice;
}
}
return closest;
}
@Override
public void defaultInINode(INode node) throws AnalysisException
{
this.current = node;
super.defaultInINode(node);
}
public INode getNode(TextReference reference, List<INode> ast)
{
this.current = null;
this.reference=reference;
try
{
for (INode iNode : ast)
{
iNode.apply(this);
}
} catch (LocationFound lf)
{
return lf.location.node;
} catch (AnalysisException e)
{
}
if (closest == null)
{
return null;
}
return closest.node;
}
}