/* * Glsl.java * * Created on 24.09.2007, 00:46:53 * */ package net.java.nboglpack.glsleditor.glsl; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.text.AbstractDocument; import net.java.nboglpack.glsleditor.lexer.GlslTokenId; import net.java.nboglpack.glsleditor.vocabulary.GLSLElementDescriptor; import org.netbeans.api.languages.ASTItem; import org.netbeans.api.languages.ASTNode; import org.netbeans.api.languages.ASTToken; import org.netbeans.api.languages.SyntaxContext; import org.netbeans.api.lexer.Token; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.api.lexer.TokenUtilities; /** * Utility methods called from GLSL.nbs. * @author Michael Bien */ public final class Glsl { private final static String KEYWORD_FONT_COLOR = "<font color=808080>"; public final static Map<String, GLSLElementDescriptor> declaredFunctions = new HashMap<String, GLSLElementDescriptor>(); private Glsl() {} /** * Assembles a human readable String containing the declaraton of a function. * Asumes that the current token of the SyntaxContext represents the function name. */ public static final String createFunctionDeclarationString(SyntaxContext context) { AbstractDocument document = (AbstractDocument)context.getDocument(); StringBuilder sb = new StringBuilder(); try { document.readLock(); TokenSequence sequence = TokenHierarchy.get(context.getDocument()).tokenSequence(); sequence.move(context.getOffset()); sequence.moveNext(); sb.append("<html>"); int moved = 0; while(sequence.movePrevious() && isIgnoredToken(sequence.token())) moved++; String type = sequence.token().toString(); while(moved-- >= 0) sequence.moveNext(); // append function name sb.append(sequence.token().text()); while(!TokenUtilities.equals(sequence.token().text(), "(")) sequence.moveNext(); sb.append("("); Token token; boolean first = true; while(sequence.moveNext() && !TokenUtilities.equals(sequence.token().text(), ")")) { token = sequence.token(); if(!isIgnoredToken(token)) { if(first) { sb.append(KEYWORD_FONT_COLOR); }else if(token.id() != GlslTokenId.COMMA && token.id() != GlslTokenId.BRACKET && token.id() != GlslTokenId.INTEGER_LITERAL) { sb.append(" "); } if(!TokenUtilities.equals(token.text(), "void")) { moved = 0; while(sequence.moveNext() && isIgnoredToken(sequence.token())) moved++; if(sequence.token().id() == GlslTokenId.COMMA || TokenUtilities.equals(sequence.token().text(), ")")) sb.append("</font>"); while(moved-- >= 0) sequence.movePrevious(); sb.append(token.text()); if(token.id() == GlslTokenId.COMMA) sb.append(KEYWORD_FONT_COLOR); } first = false; } } sb.append("</font>)"); if(!"void".equals(type)) { sb.append(" : "); sb.append(KEYWORD_FONT_COLOR); sb.append(type); sb.append("</font>"); } sb.append("</html>"); } finally { document.readUnlock(); } return sb.toString(); } /** * Assambles a human readable String containing the declaraton of a field and the field name itself. * Asumes that the current token of the SyntaxContext represents the field name. */ public static final String createFieldDeclarationString(SyntaxContext context) { AbstractDocument document = (AbstractDocument)context.getDocument(); StringBuilder sb = new StringBuilder(); try { document.readLock(); TokenSequence sequence = TokenHierarchy.get(context.getDocument()).tokenSequence(); sequence.move(context.getOffset()); sequence.moveNext(); sb.append("<html>"); sb.append(sequence.token().text()); sb.append(KEYWORD_FONT_COLOR); sb.append(" :"); int insertIndex = sb.length(); // read forward int moved = 0; Token token; while( sequence.moveNext() && sequence.token().id() != GlslTokenId.SEMICOLON && sequence.token().id() != GlslTokenId.COMMA && sequence.token().id() != GlslTokenId.EQ ) { token = sequence.token(); if(!isIgnoredToken(token)) sb.append(token); moved++; } while(moved-- >= 0) sequence.movePrevious(); // read backwards throw the declaration boolean skipToken = false; while( sequence.movePrevious() && sequence.token().id() != GlslTokenId.SEMICOLON && sequence.token().id() != GlslTokenId.END_OF_LINE ) { token = sequence.token(); if(!isIgnoredToken(token)) { // we have a struct declaration; skip everything between { } if(token.id() == GlslTokenId.BRACE && TokenUtilities.equals(token.text(), "}")) { movePreviousUntil(sequence, GlslTokenId.BRACE, "}", "{"); continue; } // skip token in case of an comma seperated identifier list if(skipToken) { if( token.id() == GlslTokenId.BRACKET && TokenUtilities.equals(token.text(), "]") ) { movePreviousUntil(sequence, GlslTokenId.BRACKET, "]", "["); skipToken = false; }else { skipToken = false; } continue; } if(token.id() == GlslTokenId.COMMA) { skipToken = true; continue; } if(!TokenUtilities.equals(token.text(), "struct")) { sb.insert(insertIndex, token.text()); sb.insert(insertIndex, " "); } } } sb.append("</font></html>"); } finally { document.readUnlock(); } return sb.toString(); } public static final String createPreprocessorString(SyntaxContext context) { ASTNode node = (ASTNode)context.getASTPath().getLeaf(); List<ASTItem> children = node.getChildren(); String str = null; for (ASTItem item : children) if (isTokenType(item, GlslTokenId.PREPROCESSOR.name())) str = ((ASTToken)item).getIdentifier(); for(int i = 0; i < str.length(); i++) { char c = str.charAt(i); if(c != '#' && !Character.isWhitespace(c)) for(int j = str.length()-1; j > i; j--) if(!Character.isWhitespace(str.charAt(j))) return str.substring(i, j+1); } return str; } /** * called from withen GLSL_*.nbs each time the document has been modified. */ public static void process(SyntaxContext context) { AbstractDocument document = (AbstractDocument)context.getDocument(); try{ document.readLock(); // remember all declared funktions for auto completion synchronized(declaredFunctions) { declaredFunctions.clear(); } List<ASTItem> declarations = context.getASTPath().getLeaf().getChildren(); for (ASTItem declaration : declarations) { for (ASTItem declarationItem : declaration.getChildren()) { if(isNode(declarationItem, "function")) { List<ASTItem> functionItems = declarationItem.getChildren(); if(functionItems.size() < 3) break; ASTItem nameToken = functionItems.get(0); if(isTokenType(nameToken, GlslTokenId.FUNCTION.name())) { // determine return type StringBuilder returnType = new StringBuilder(); for (ASTItem typeItem : declaration.getChildren()) { if(isNode(typeItem, "function")) break; if(typeItem instanceof ASTNode) { returnType.append(((ASTNode)typeItem).getAsText().trim()); }else if(typeItem instanceof ASTToken) { final ASTToken t = (ASTToken) typeItem; returnType.append(t.getIdentifier().trim()); } } // determine name and parameter list StringBuilder name = new StringBuilder(); name.append("("); ASTItem parameterList = functionItems.get(2); if(isNode(parameterList, "parameter_declaration_list")) name.append(((ASTNode)parameterList).getAsText()); name.append(")"); GLSLElementDescriptor elementDesc = new GLSLElementDescriptor( GLSLElementDescriptor.Category.USER_FUNC, "", "", name.toString(), returnType.toString()); name.insert(0, ((ASTToken) nameToken).getIdentifier()); synchronized(declaredFunctions) { declaredFunctions.put(name.toString(), elementDesc); } // System.out.println("|"+returnType.toString()+"|"+name.toString()+"|"); } break; } } } }finally{ document.readUnlock(); } } private static final void movePreviousUntil(TokenSequence sequence, GlslTokenId id, String countToken, String stopToken) { int counter = 1; while(sequence.movePrevious() && counter > 0) { if(sequence.token().id() == id) { if(TokenUtilities.equals(sequence.token().text(), stopToken)) { counter--; }else if(TokenUtilities.equals(sequence.token().text(), countToken)){ counter++; } } } } private static final boolean isIgnoredToken(Token token) { return token.id() == GlslTokenId.WHITESPACE || token.id() == GlslTokenId.COMMENT || token.id() == GlslTokenId.PREPROCESSOR; } private static final boolean isNode(ASTItem item, String nodeToken) { return item != null && item instanceof ASTNode && ((ASTNode)item).getNT().equals(nodeToken); } private static final boolean isToken(ASTItem item, String id) { return item != null && item instanceof ASTToken && ((ASTToken)item).getIdentifier().equals(id); } private static final boolean isTokenType(ASTItem item, String type) { return item != null && item instanceof ASTToken && ((ASTToken) item).getTypeName().equals(type); } }