/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package ca.weblite.netbeans.mirah.lexer;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import mirah.impl.Tokens;
import mirah.lang.ast.ClassDefinition;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.editor.BaseDocument;
import org.openide.filesystems.FileObject;
import org.openide.util.Exceptions;
/**
*
* @author shannah
*/
public class DocumentQuery {
Document doc;
Set<MirahTokenId> skipTokens = new HashSet<MirahTokenId>();
public DocumentQuery(Document doc){
this.doc = doc;
}
public void addSkipToken(MirahTokenId tok){
skipTokens.add(tok);
}
public void removeSkipToken(MirahTokenId tok){
skipTokens.remove(tok);
}
public void clearSkipTokens(){
skipTokens.clear();
}
public int getEOL(int offset){
int out = offset;
try {
int len = doc.getLength();
while ( out >=0 && out < len && !"\n".equals(doc.getText(out, 1)) ){
out++;
}
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
return out;
}
public int getBOL(int offset){
int out = offset;
try {
int len = doc.getLength();
while ( out >=0 && out < len-1 && !"\n".equals(doc.getText(out, 1)) ){
out--;
}
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
try {
if ( out >=0 && out < doc.getLength()-1 && "\n".equals(doc.getText(out, 1) )){
out++;
}
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
return out;
}
public int getIndent(int offset){
try {
int bol = getBOL(offset);
int eol = getEOL(offset);
int i = bol;
int len = doc.getLength();
while ( i > 0 && i<eol && " ".equals(doc.getText(i, 1))){
i++;
}
return i-bol;
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
return 0;
}
public List<Token<MirahTokenId>> findLambdaTypes(){
List<Token<MirahTokenId>> out = new ArrayList<Token<MirahTokenId>>();
TokenHierarchy<?> hi = TokenHierarchy.get(doc);
int caretOffset = 0;
TokenSequence<MirahTokenId> seq = getTokens(caretOffset, false);
while ( seq.moveNext() ){
if ( seq.token().id().ordinal() == Tokens.tIDENTIFIER.ordinal()
&& "lambda".equals(String.valueOf(seq.token().text()))){
while ( seq.moveNext() ){
if ( seq.token().id().ordinal() == Tokens.tCONSTANT.ordinal() ){
out.add(seq.token());
break;
}
}
}
}
return out;
}
public List<Token<MirahTokenId>> findConstants(){
List<Token<MirahTokenId>> out = new ArrayList<Token<MirahTokenId>>();
TokenHierarchy<?> hi = TokenHierarchy.get(doc);
int caretOffset = 0;
TokenSequence<MirahTokenId> seq = getTokens(caretOffset, false);
while ( seq.moveNext() ){
if ( seq.token().id().ordinal() == Tokens.tCONSTANT.ordinal()){
out.add(seq.token());
}
}
return out;
}
public void addImport(String fqn) throws BadLocationException{
if ( requiresImport(fqn) ){
TokenHierarchy<?> hi = TokenHierarchy.get(doc);
int caretOffset = 0;
TokenSequence<MirahTokenId> seq = mirahTokenSequence(doc, caretOffset, false);
// Find the first package or import and place the import after that.
MirahTokenId PKG = MirahTokenId.get(Tokens.tPackage.ordinal());
MirahTokenId IMPORT = MirahTokenId.get(Tokens.tImport.ordinal());
MirahTokenId EOL = MirahTokenId.get(Tokens.tNL.ordinal());
int pos = 0;
Token pkg = null;
Token firstImport = null;
do {
Token curr = seq.token();
MirahTokenId currTok = (MirahTokenId)curr.id();
if ( PKG.equals(currTok) ){
pkg = curr;
} else if ( IMPORT.equals(currTok)){
firstImport = curr;
}
} while ( seq.moveNext());
if ( firstImport != null ){
try {
doc.insertString(firstImport.offset(hi), "import "+fqn+"\n", new SimpleAttributeSet());
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
} else if ( pkg != null ){
seq.move(pkg.offset(hi));
while ( seq.moveNext() ){
if ( EOL.equals(seq.token().id())){
doc.insertString(seq.token().offset(hi), "\nimport "+fqn+"\n", new SimpleAttributeSet());
break;
}
}
} else {
doc.insertString(0, "import "+fqn+"\n", new SimpleAttributeSet());
}
}
}
public List<String> getImports() {
//Document doc = source.getDocument(true);
TokenHierarchy<?> hi = TokenHierarchy.get(doc);
int caretOffset = 0;
TokenSequence<MirahTokenId> seq = getTokens(caretOffset, false);
// Find the first package or import and place the import after that.
MirahTokenId PKG = MirahTokenId.get(Tokens.tPackage.ordinal());
MirahTokenId IMPORT = MirahTokenId.get(Tokens.tImport.ordinal());
MirahTokenId EOL = MirahTokenId.get(Tokens.tNL.ordinal());
int pos = 0;
Token pkg = null;
Token firstImport = null;
List<String> out = new ArrayList<String>();
do {
Token curr = seq.token();
MirahTokenId currTok = (MirahTokenId)curr.id();
if ( IMPORT.equals(currTok)){
while ( seq.moveNext() && seq.token().id().ordinal() != Tokens.tIDENTIFIER.ordinal()){
}
StringBuilder sb = new StringBuilder();
do {
sb.append(seq.token().text());
} while (
(
seq.token().id().ordinal() == Tokens.tDot.ordinal()
|| seq.token().id().ordinal() == Tokens.tIDENTIFIER.ordinal()
|| seq.token().id().ordinal() == Tokens.tStar.ordinal()
) && seq.moveNext()
);
out.add(sb.toString());
}
} while ( seq.moveNext());
return out;
}
public boolean requiresImport(String fqn){
if ( fqn == null ){
return false;
}
String pkg = "";
String simpleName = fqn;
if ( fqn.indexOf(".") != -1 ){
pkg = fqn.substring(0, fqn.lastIndexOf("."));
simpleName = fqn.substring(pkg.length()+1);
}
String pkgGlob = pkg+".*";
List<String> imports = getImports();
for ( String line : imports ){
if ( pkgGlob.equals(line)){
return false;
}
if ( fqn.equals(line)){
return false;
}
}
return true;
}
public TokenSequence<MirahTokenId> getTokens(int caretOffset, boolean backwardBias){
return mirahTokenSequence(doc, caretOffset, backwardBias);
}
public static void consumeLine(TokenSequence<MirahTokenId> sequence){
while (sequence.token() != null && sequence.moveNext() ){
if ( sequence.token() != null && sequence.token().id().ordinal() == Tokens.tNL.ordinal()){
return;
}
}
}
/*
public int getIndent(int offset){
TokenSequence<MirahTokenId> seq = getTokens(offset, false);
while ( seq.token() != null && seq.token().id().ordinal() != Tokens.tNL.ordinal()){
seq.movePrevious();
}
if ( seq.token() == null ){
return 0;
}
int bol = seq.token().offset(TokenHierarchy.get(doc));
while ( seq.token() != null && seq.token().id().ordinal() != Tokens.tWhitespace.ordinal()){
seq.moveNext();
}
if ( seq.token() == null ){
return 0;
}
return seq.token().offset(TokenHierarchy.get(doc))-bol;
}*/
public static boolean findNext(TokenSequence<MirahTokenId> sequence,
Set<MirahTokenId> skipTokens,
MirahTokenId endToken,
MirahTokenId needleToken){
return findNext(sequence, skipTokens, Collections.singleton(endToken), Collections.singleton(needleToken) );
}
public static boolean findNext(TokenSequence<MirahTokenId> sequence,
Set<MirahTokenId> skipTokens,
Set<MirahTokenId> endTokens,
Set<MirahTokenId> needleTokens){
while ( true ){
if ( sequence.token() == null ){
return false;
}
MirahTokenId tok = sequence.token().id();
if ( needleTokens.contains(tok)){
return true;
}
if ( skipTokens != null && !skipTokens.contains(tok) ){
return false;
}
if ( endTokens != null && endTokens.contains(tok)){
return false;
}
sequence.moveNext();
}
}
public static boolean findPrevious(TokenSequence<MirahTokenId> sequence,
Set<MirahTokenId> skipTokens,
MirahTokenId endToken,
MirahTokenId needleToken){
return findPrevious(sequence, skipTokens, Collections.singleton(endToken), Collections.singleton(needleToken) );
}
public static boolean findPrevious(TokenSequence<MirahTokenId> sequence,
Set<MirahTokenId> skipTokens,
Set<MirahTokenId> endTokens,
Set<MirahTokenId> needleTokens){
while ( true ){
if ( sequence.token() == null){
return false;
}
MirahTokenId tok = sequence.token().id();
if ( needleTokens.contains(tok)){
return true;
}
if ( skipTokens != null && !skipTokens.contains(tok) ){
return false;
}
if ( endTokens != null && endTokens.contains(tok)){
return false;
}
if (!sequence.movePrevious()){
return false;
}
}
}
public int getAfterNextDo(int caretOffset){
//System.out.println("Doc len is "+doc.getLength());
TokenSequence<MirahTokenId> seq = getTokens(caretOffset, true);
while ( seq.moveNext() ){
//System.out.println("Tok is "+seq.token());
if ( seq.token().id().ordinal() == Tokens.tDo.ordinal() ){
//System.out.println("Found do... checking if there is something after");
if ( seq.moveNext() ){
return seq.token().offset(TokenHierarchy.get(doc));
} else {
return -1;
}
}
}
return -1;
}
public int getBeforePrevEnd(int caretOffset ){
TokenSequence<MirahTokenId> seq = getTokens(caretOffset, true);
if ( seq.token().id().ordinal() == Tokens.tEnd.ordinal()){
seq.movePrevious();
return seq.token().offset(TokenHierarchy.get(doc));
}
while ( seq.movePrevious()){
//System.out.println("Tok is "+seq.token());
if ( seq.token().id().ordinal() == Tokens.tEnd.ordinal() ){
//System.out.println("Found do... checking if there is something after");
if ( seq.movePrevious() ){
return seq.token().offset(TokenHierarchy.get(doc));
} else {
return -1;
}
}
}
return -1;
}
public String getPackageName(int caretOffset){
TokenSequence<MirahTokenId> seq = getTokens(caretOffset, true);
while ( seq != null && seq.movePrevious()){
//System.out.println("Tok is "+seq.token());
if ( seq.token().id().ordinal() == Tokens.tPackage.ordinal() ){
//System.out.println("Found do... checking if there is something after");
StringBuilder sb = null;
while (seq.moveNext()){
if ( sb == null && seq.token().id().ordinal() == Tokens.tIDENTIFIER.ordinal() ){
sb = new StringBuilder();
sb.append(seq.token().text());
} else if ( sb != null
&& ( seq.token().id().ordinal() == Tokens.tIDENTIFIER.ordinal()
|| seq.token().id().ordinal() == Tokens.tDot.ordinal())){
sb.append(seq.token().text());
} else if ( sb != null ){
return sb.toString();
}
}
break;
}
}
return null;
}
/**
* Get token sequence positioned over a token.
*
* @param doc
* @param caretOffset
* @param backwardBias
* @return token sequence positioned over a token that "contains" the offset
* or null if the document does not contain any java token sequence or the
* offset is at doc-or-section-start-and-bwd-bias or
* doc-or-section-end-and-fwd-bias.
*/
private static TokenSequence<MirahTokenId> mirahTokenSequence(Document doc, int caretOffset, boolean backwardBias) {
BaseDocument bdoc = (BaseDocument)doc;
bdoc.readLock();
try {
TokenHierarchy<?> hi = TokenHierarchy.get(doc);
List<TokenSequence<?>> tsList = hi.embeddedTokenSequences(caretOffset, backwardBias);
//System.out.println(tsList+" off "+caretOffset+" back: "+backwardBias);
// Go from inner to outer TSes
for (int i = tsList.size() - 1; i >= 0; i--) {
TokenSequence<?> ts = tsList.get(i);
if (ts.languagePath().innerLanguage() == MirahTokenId.getLanguage()) {
TokenSequence<MirahTokenId> javaInnerTS = (TokenSequence<MirahTokenId>) ts;
return javaInnerTS;
}
}
return null;
} finally {
bdoc.readUnlock();
}
}
private TokenSequence<MirahTokenId> getTokens(int caretOffset) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
public MirahTokenId firstNonWhiteToken(int offset){
int eol = getEOL(offset+1);
TokenSequence<MirahTokenId> seq = getTokens(offset, false);
while ( seq.token() != null && seq.offset() < eol ){
if ( !MirahTokenId.WHITESPACE_AND_COMMENTS.contains(seq.token().id())){
return seq.token().id();
}
seq.moveNext();
}
return null;
}
public MirahTokenId lastNonWhiteTokenOfLine(int offset){
int bol = getBOL(offset+1);
int eol = getEOL(bol+1);
TokenSequence<MirahTokenId> seq = getTokens(offset, false);
MirahTokenId lastNonWhite = null;
while ( seq.token() != null && seq.offset() < eol ){
if ( !MirahTokenId.WHITESPACE_AND_COMMENTS.contains(seq.token().id())){
lastNonWhite = seq.token().id();
}
int prevOffset = seq.offset();
seq.moveNext();
if ( seq.offset() == prevOffset ){
break;
}
}
return lastNonWhite;
}
private void skipWhitespaceBack(TokenSequence<MirahTokenId> seq){
while ( seq.movePrevious() && MirahTokenId.WHITESPACE_AND_COMMENTS.contains(seq.token().id())){}
}
public String guessType(TokenSequence<MirahTokenId> seq, FileObject fo){
MirahTokenId tok = seq.token().id();
int offset = seq.offset();
try {
if ( tok.ordinal() == Tokens.tIDENTIFIER.ordinal()){
String id = seq.token().text().toString();
skipWhitespaceBack(seq);
if ( seq.token().id().ordinal() == Tokens.tDot.ordinal()){
skipWhitespaceBack(seq);
if ( seq.token().id().ordinal() == Tokens.tIDENTIFIER.ordinal()){
String prevType = guessType(seq, fo);
if ( prevType == null ){
return null;
}
ClassQuery cq = new ClassQuery(prevType, fo, true);
for ( Method m : cq.getMethods()){
if ( id.equals(m.getName()) ){
return m.getReturnType().getName();
}
}
}
} else {
SourceQuery sq = new SourceQuery(doc);
SourceQuery method = sq.findMethod(offset);
if ( method.size() > 0 ){
SourceQuery localVars = method.findLocalVars(id);
String type = localVars.getType();
if ( type != null ){
return type;
}
}
SourceQuery parent = sq.findClass(offset);
if ( !parent.isEmpty()){
//System.out.println("Found parent class");
ClassDefinition node = (ClassDefinition)parent.get(0);
String type = parent.getFQN(node.name().identifier(), offset);
if ( type != null && type.indexOf(".") == 0 ){
type = type.substring(1);
}
//System.out.println("fqn "+type);
//System.out.println("Parent type "+node.name().identifier());
ClassQuery cq = new ClassQuery(type, fo, true);
for ( Method m : cq.getMethods()){
if ( id.equals(m.getName()) ){
return m.getReturnType().getName();
}
}
}
}
}
} finally {
while ( seq.offset() < offset && seq.moveNext()){}
}
return null;
}
}