/**
* This file Copyright (c) 2005-2008 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain other free and open source software ("FOSS") code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.lexer;
/**
* @author Kevin Lindsey
*/
public class Lexeme implements Cloneable, Comparable<Offset>, IRange
{
private static final int AFTER_EOL = 1;
private static final int HIGHLIGHTED = 2;
private IToken _token;
private String _text;
private int _flags;
/**
* The lexeme's offset within the source text
*/
public int offset;
/**
* The token type index for this lexeme
*/
public int typeIndex;
/**
* The total length of the text contained in this lexeme
*/
public int length;
/**
* Create a new instance of Lexeme
*
* @param token
* The parent token class this lexeme belongs to
* @param text
* The matching text for this lexeme
* @param offset
* The offset at which the match occurred
*/
public Lexeme(IToken token, String text, int offset)
{
this._token = token;
this._text = text;
this.offset = offset;
this.typeIndex = token.getTypeIndex();
this.length = text.length();
}
/**
* Adjust the current offset of this lexeme by the specified delta
*
* @param delta
* The amount by which to adjust this lexeme's offset
*/
public void adjustOffset(int delta)
{
this.offset += delta;
}
/**
* @see java.lang.Object#clone()
*/
public Object clone()
{
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new IllegalStateException();
}
}
/**
* compareTo
*
* @param o
* The key with which to compare against
* @return Returns -1 if object is past this lexeme, 1 if object is before this lexeme, or 0 if object is contained
* within this lexeme
*/
public int compareTo(Offset o)
{
int offset = o.offset;
int result = 0;
if (offset < this.offset)
{
result = 1;
}
else if (this.offset + this.length <= offset)
{
result = -1;
}
return result;
}
/**
* @see com.aptana.ide.lexer.IRange#containsOffset(int)
*/
public boolean containsOffset(int offset)
{
return (this.offset <= offset && offset < this.offset + this.length);
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
boolean result = false;
if (this == obj)
{
result = true;
}
else if (obj instanceof Lexeme)
{
Lexeme that = (Lexeme) obj;
// NOTE: [KEL] Should we compare flags too?
result = this._text.equals(that._text) && this._token == that._token;
}
return result;
}
/**
* Get the lexeme class name of this token
*
* @return Returns this lexeme's category name
*/
public String getCategory()
{
return this._token.getCategory();
}
/**
* Get the lexeme class name index for this token
*
* @return Returns this lexeme's category index
*/
public int getCategoryIndex()
{
return this._token.getCategoryIndex();
}
/**
* @see com.aptana.ide.lexer.IRange#getEndingOffset()
*/
public int getEndingOffset()
{
return this.offset + this.length;
}
/**
* Get the owning language for this lexeme
*
* @return Returns then MIME type of the language to which this lexeme belongs
*/
public String getLanguage()
{
return this._token.getLanguage();
}
/**
* @see com.aptana.ide.lexer.IRange#getLength()
*/
public int getLength()
{
return this.length;
}
/**
* @see com.aptana.ide.lexer.IRange#getStartingOffset()
*/
public int getStartingOffset()
{
return this.offset;
}
/**
* Get the text associated with this token
*
* @return The token text
*/
public String getText()
{
return this._text;
}
/**
* Get the token (class) that this Lexeme is an instance of
*
* @return Returns this lexeme's token class
*/
public IToken getToken()
{
return this._token;
}
/**
* Get the lexeme name (token class) of this token
*
* @return The lexeme type name
*/
public String getType()
{
return this._token.getType();
}
/**
* Determine if this lexeme follows immediately after a line terminator
*
* @return Returns true if this lexeme follows a line terminator
*/
public boolean isAfterEOL()
{
return (this._flags & AFTER_EOL) == AFTER_EOL;
}
/**
* @see com.aptana.ide.lexer.IRange#isEmpty()
*/
public boolean isEmpty()
{
return this.length <= 0;
}
/**
* isHighlighted
*
* @return
*/
public boolean isHighlighted()
{
return (this._flags & HIGHLIGHTED) == HIGHLIGHTED;
}
/**
* Determine if this lexeme and the specified lexeme overlap
*
* @param lexeme
* The lexeme to test
* @return Returns true if the lexemes overlap
*/
public boolean isOverlapping(Lexeme lexeme)
{
int startingOffset1 = this.offset;
int startingOffset2 = lexeme.offset;
int endingOffset1 = this.getEndingOffset() - 1;
int endingOffset2 = lexeme.getEndingOffset() - 1;
return (startingOffset2 <= startingOffset1 && startingOffset1 <= endingOffset2)
|| (startingOffset2 <= endingOffset1 && endingOffset1 <= endingOffset2)
|| (startingOffset1 <= startingOffset2 && startingOffset2 <= endingOffset1)
|| (startingOffset1 <= endingOffset2 && endingOffset2 <= endingOffset1);
}
/**
* Set flag indicating that this lexeme comes immediately after a line terminator
*/
public void setAfterEOL()
{
this._flags |= AFTER_EOL;
}
/**
* setHighlighted
*/
public void setHighlighted(boolean value)
{
if (value)
{
this._flags |= HIGHLIGHTED;
}
else
{
this._flags &= ~HIGHLIGHTED;
}
}
/**
* Sets the token for this lexeme
*
* @param token
*/
public void setToken(IToken token)
{
this._token = token;
this.typeIndex = token.getTypeIndex();
}
/**
* Return a string representation of this token
*
* @return Returns a string representation of this token
*/
public String toString()
{
StringBuffer sb = new StringBuffer();
String className = this.getCategory();
String name = this.getType();
sb.append("[").append(this.getLanguage()).append("] "); //$NON-NLS-1$ //$NON-NLS-2$
if (className.equals("default") == false) //$NON-NLS-1$
{
sb.append(className).append("."); //$NON-NLS-1$
}
sb.append(name);
sb.append("@").append(this.offset).append("-").append(this.getEndingOffset()); //$NON-NLS-1$ //$NON-NLS-2$
sb.append(": ~").append(this._text).append("~"); //$NON-NLS-1$ //$NON-NLS-2$
return sb.toString();
}
/**
*
* @param text
*/
public void setText(String text) {
this._text=text;
}
}