php.smarty/src/org/netbeans/modules/php/smarty/editor/lexer/TplTopLexer.java
author Martin Fousek <marfous@netbeans.org>
Thu, 03 Mar 2011 00:03:37 +0100
changeset 17309 60f4cc6038b3
parent 17288 e1387a5d34c8
child 17329 54d8f8c364b0
permissions -rw-r--r--
removing UI freezing by change of default Smarty delimiters
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
     5  *
     6  * The contents of this file are subject to the terms of either the GNU
     7  * General Public License Version 2 only ("GPL") or the Common
     8  * Development and Distribution License("CDDL") (collectively, the
     9  * "License"). You may not use this file except in compliance with the
    10  * License. You can obtain a copy of the License at
    11  * http://www.netbeans.org/cddl-gplv2.html
    12  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    13  * specific language governing permissions and limitations under the
    14  * License.  When distributing the software, include this License Header
    15  * Notice in each file and include the License file at
    16  * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    17  * particular file as subject to the "Classpath" exception as provided
    18  * by Sun in the GPL Version 2 section of the License file that
    19  * accompanied this code. If applicable, add the following below the
    20  * License Header, with the fields enclosed by brackets [] replaced by
    21  * your own identifying information:
    22  * "Portions Copyrighted [year] [name of copyright owner]"
    23  *
    24  * If you wish your version of this file to be governed by only the CDDL
    25  * or only the GPL Version 2, indicate your decision by adding
    26  * "[Contributor] elects to include this software in this distribution
    27  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    28  * single choice of license, a recipient has the option to distribute
    29  * your version of this file under either the CDDL, the GPL Version 2 or
    30  * to extend the choice of license to its licensees as provided above.
    31  * However, if you add GPL Version 2 code and therefore, elected the GPL
    32  * Version 2 license, then the option applies only if the new code is
    33  * made subject to such option by the copyright holder.
    34  *
    35  * Contributor(s):
    36  *
    37  * Portions Copyrighted 2008 Sun Microsystems, Inc.
    38  */
    39 package org.netbeans.modules.php.smarty.editor.lexer;
    40 
    41 import org.netbeans.api.lexer.InputAttributes;
    42 import org.netbeans.api.lexer.LanguagePath;
    43 import org.netbeans.api.lexer.Token;
    44 import org.netbeans.lib.editor.util.CharSequenceUtilities;
    45 import org.netbeans.modules.php.smarty.editor.TplMetaData;
    46 import org.netbeans.modules.php.smarty.editor.utlis.LexerUtils;
    47 import org.netbeans.modules.php.smarty.editor.utlis.TplUtils;
    48 import org.netbeans.spi.lexer.Lexer;
    49 import org.netbeans.spi.lexer.LexerInput;
    50 import org.netbeans.spi.lexer.LexerRestartInfo;
    51 import org.netbeans.spi.lexer.TokenFactory;
    52 
    53 /**
    54  *
    55  * @author Martin Fousek
    56  */
    57 public class TplTopLexer implements Lexer<TplTopTokenId> {
    58 
    59     private final TplTopColoringLexer scanner;
    60     private TokenFactory<TplTopTokenId> tokenFactory;
    61     private final InputAttributes inputAttributes;
    62     private final TplMetaData tplMetaData;
    63 
    64     private class CompoundState {
    65         private State lexerState;
    66         private SubState lexerSubState;
    67 
    68         public CompoundState(State lexerState, SubState lexerSubState) {
    69             this.lexerState = lexerState;
    70             this.lexerSubState = lexerSubState;
    71         }
    72 
    73         @Override
    74         public boolean equals(Object obj) {
    75             if (obj == null) {
    76                 return false;
    77             }
    78             if (getClass() != obj.getClass()) {
    79                 return false;
    80             }
    81             final CompoundState other = (CompoundState) obj;
    82             if (this.lexerState != other.lexerState) {
    83                 return false;
    84             }
    85             if (this.lexerSubState != other.lexerSubState) {
    86                 return false;
    87             }
    88             return true;
    89         }
    90 
    91         @Override
    92         public int hashCode() {
    93             int hash = 3;
    94             hash = 17 * hash + this.lexerState.ordinal();
    95             hash = 17 * hash + this.lexerSubState.ordinal();
    96             return hash;
    97         }
    98 
    99         @Override
   100         public String toString() {
   101             return "State(hash=" + hashCode() + ",s=" + lexerState + ",ss=" + lexerSubState + ")"; //NOI18N
   102         }
   103 
   104     }
   105 
   106     private TplTopLexer(LexerRestartInfo<TplTopTokenId> info) {
   107         CompoundState state = null;
   108         if (info.state() == null) {
   109             state = new CompoundState(State.INIT, SubState.NO_SUB_STATE);
   110         } else {
   111             state = (CompoundState)info.state();
   112         }
   113         this.tokenFactory = info.tokenFactory();
   114         this.inputAttributes = info.inputAttributes();
   115         if (inputAttributes != null) {
   116             this.tplMetaData = (TplMetaData) inputAttributes.getValue(LanguagePath.get(TplTopTokenId.language()), TplMetaData.class);
   117         } else {
   118             this.tplMetaData = TplUtils.getProjectPropertiesForFileObject(null);
   119         }
   120         scanner = new TplTopColoringLexer(info, state, tplMetaData);
   121     }
   122 
   123     /**
   124      * Create new top lexer.
   125      * @param info where was the parsing started
   126      * @return new lexer for additional parsing
   127      */
   128     public static synchronized TplTopLexer create(LexerRestartInfo<TplTopTokenId> info) {
   129         return new TplTopLexer(info);
   130     }
   131 
   132     public Token<TplTopTokenId> nextToken() {
   133         TplTopTokenId tokenId = scanner.nextToken();
   134         Token<TplTopTokenId> token = null;
   135         if (tokenId != null) {
   136             token = tokenFactory.createToken(tokenId);
   137         }
   138         return token;
   139     }
   140 
   141     public Object state() {
   142         return scanner.getState();
   143     }
   144 
   145     public void release() {
   146     }
   147 
   148     private enum State {
   149         INIT,
   150         OUTER,
   151         AFTER_DELIMITER,
   152         OPEN_DELIMITER,
   153         CLOSE_DELIMITER,
   154         IN_COMMENT,
   155         IN_SMARTY,
   156         IN_PHP,
   157         AFTER_SUBSTATE,
   158         IN_LITERAL
   159     }
   160 
   161     private enum SubState {
   162         NO_SUB_STATE,
   163         PHP_CODE,
   164         LITERAL
   165     }
   166 
   167     private class TplTopColoringLexer {
   168 
   169         private State state;
   170         private final LexerInput input;
   171         private SubState subState;
   172         private final TplMetaData metadata;
   173 
   174         public TplTopColoringLexer(LexerRestartInfo<TplTopTokenId> info, CompoundState state, TplMetaData metadata) {
   175             this.input = info.input();
   176             this.state = state.lexerState;
   177             this.subState = state.lexerSubState;
   178             if (metadata != null)
   179                 this.metadata = metadata;
   180             else
   181                 this.metadata = TplUtils.getProjectPropertiesForFileObject(null);
   182         }
   183 
   184         public TplTopTokenId nextToken() {
   185             int c = input.read();
   186             CharSequence text;
   187             int textLength;
   188             int openDelimiterLength = getOpenDelimiterLength();
   189             int closeDelimiterLength = getCloseDelimiterLength();
   190             if (c == LexerInput.EOF) {
   191                 return null;
   192             }
   193             while (c != LexerInput.EOF) {
   194                 char cc = (char) c;
   195                 text = input.readText();
   196                 textLength = text.length();
   197                switch (state) {
   198                 case INIT:
   199                 case OUTER:
   200                     if (isSmartyOpenDelimiter(text)) {
   201                         state = State.OPEN_DELIMITER;
   202                         input.backup(openDelimiterLength);
   203                         if (textLength > openDelimiterLength) {
   204                             return TplTopTokenId.T_HTML;
   205                         }
   206                     }
   207 //                    if (cc == '\n') {
   208 //                        return TplTopTokenId.T_HTML;
   209 //                    }
   210                     break;
   211 
   212                 case OPEN_DELIMITER:
   213                     if (textLength < openDelimiterLength) {
   214                         break;
   215                     }
   216                     state = State.AFTER_DELIMITER;
   217                     if (subState == subState.NO_SUB_STATE) {
   218                         return TplTopTokenId.T_SMARTY_OPEN_DELIMITER;
   219                     } else {
   220                         if (input.readLength() > openDelimiterLength) {
   221                             input.backup(input.readLength() - openDelimiterLength);
   222                             if (subState == subState.LITERAL)
   223                                 return TplTopTokenId.T_HTML;
   224                             else
   225                                 return TplTopTokenId.T_PHP;
   226                         }
   227                         break;
   228                     }
   229 
   230                 case AFTER_DELIMITER:
   231                     if (LexerUtils.isWS(c)) {
   232                         if (subState == subState.NO_SUB_STATE) {
   233                             return TplTopTokenId.T_SMARTY;
   234                         } else {
   235                             break;
   236                         }
   237                     }
   238                     else {
   239                         String lastWord = readNextWord(c);
   240                         switch(subState){
   241                             case LITERAL:
   242                                 if (lastWord.startsWith("/literal")) {
   243                                     subState = SubState.NO_SUB_STATE;
   244                                     state = State.OPEN_DELIMITER;
   245                                     input.backup(input.readLength());
   246                                     break;
   247                                 } else {
   248                                     input.backup(input.readLength()-1);
   249                                     state = State.IN_LITERAL;
   250                                 }
   251                                 return TplTopTokenId.T_HTML;
   252                             case PHP_CODE:
   253                                 if (lastWord.startsWith("/php")) {
   254                                     subState = SubState.NO_SUB_STATE;
   255                                     state = State.OPEN_DELIMITER;
   256                                     input.backup(input.readLength());
   257                                     break;
   258                                 } else {
   259                                     state = State.IN_PHP;
   260                                 }
   261                                 return TplTopTokenId.T_PHP;
   262                             default:
   263                                if (lastWord.charAt(0) == '*') {
   264                                     state = State.IN_COMMENT;
   265                                     input.backup(lastWord.length()-1);
   266                                     return TplTopTokenId.T_COMMENT;
   267                                 }
   268                                 else if (lastWord.startsWith("literal")) {
   269                                     subState = SubState.LITERAL;
   270                                     state = State.AFTER_SUBSTATE;
   271                                     input.backup(lastWord.length()-7);
   272                                     return TplTopTokenId.T_SMARTY;
   273                                 }
   274                                 else if (lastWord.startsWith("php")) {
   275                                     subState = SubState.PHP_CODE;
   276                                     state = State.AFTER_SUBSTATE;
   277                                     input.backup(lastWord.length()-3);
   278                                     return TplTopTokenId.T_SMARTY;
   279                                 }
   280                                 else {
   281                                     state = State.IN_SMARTY;
   282                                     input.backup(lastWord.length());
   283                                 }
   284                         }
   285                     }
   286                     break;
   287 
   288                 case IN_COMMENT:
   289                     if (cc == '*') {
   290                         state = State.AFTER_SUBSTATE;
   291                         return TplTopTokenId.T_COMMENT;
   292                     }
   293                     return TplTopTokenId.T_COMMENT;
   294 
   295                 case AFTER_SUBSTATE:
   296                     if (LexerUtils.isWS(c)) {
   297                         return TplTopTokenId.T_SMARTY;
   298                     }
   299                     else if (isSmartyCloseDelimiter(text)) {
   300                         state = State.CLOSE_DELIMITER;
   301                         input.backup(closeDelimiterLength);
   302                         break;
   303                     } else {
   304                         break;
   305                     }
   306 
   307                 case CLOSE_DELIMITER:
   308                     if (textLength < closeDelimiterLength) {
   309                         break;
   310                     }
   311                     switch(subState){
   312                         case LITERAL:
   313                             state = State.IN_LITERAL;
   314                             break;
   315                         case PHP_CODE:
   316                             state = State.IN_PHP;
   317                             break;
   318                         default:
   319                             state = State.OUTER;
   320                             break;
   321                     }
   322                     return TplTopTokenId.T_SMARTY_CLOSE_DELIMITER;
   323 
   324                 case IN_PHP:
   325                     if (isSmartyOpenDelimiter(text)) {
   326                         state = State.OPEN_DELIMITER;
   327                         input.backup(openDelimiterLength);
   328                         if (input.readLength() > 0)
   329                             return TplTopTokenId.T_PHP;
   330                     }
   331                     if (input.readLength() > 1) {
   332                         return TplTopTokenId.T_PHP;
   333                     }
   334                     break;
   335 
   336                 case IN_LITERAL:
   337                     if (isSmartyOpenDelimiter(text)) {
   338                         state = State.OPEN_DELIMITER;
   339                         input.backup(openDelimiterLength);
   340                         if (input.readLength() > 0) {
   341                             return TplTopTokenId.T_HTML;
   342                         }
   343                     }
   344                     if (LexerUtils.isWS(c)) {
   345                         return TplTopTokenId.T_HTML;
   346                     }
   347                     break;
   348 
   349                 case IN_SMARTY:
   350                     if (isSmartyCloseDelimiter(text)) {
   351                         if (textLength == closeDelimiterLength) {
   352                             state = State.OUTER;
   353                             return TplTopTokenId.T_SMARTY_CLOSE_DELIMITER;
   354                         }
   355                         else {
   356                             state = State.CLOSE_DELIMITER;
   357                             input.backup(closeDelimiterLength);
   358                             if (input.readLength() != 0) {
   359                                 return TplTopTokenId.T_SMARTY;
   360                             }
   361                         }
   362                     }
   363                     switch(c) {
   364                         case '\n':
   365                            return TplTopTokenId.T_SMARTY;
   366                         case LexerInput.EOF:
   367                            return TplTopTokenId.T_SMARTY;
   368 //                        case '<':
   369 //                           state = State.OUTER;
   370 //                           input.backup(1);
   371 //                           if (input.readLength() > 1) {
   372 //                                return TplTopTokenId.T_SMARTY;
   373 //                           }
   374                     }
   375                     break;
   376                 }
   377                 c = input.read();
   378             }
   379 
   380             return getTokenId(state);
   381         }
   382 
   383         private TplTopTokenId getTokenId(State state) {
   384             switch (state) {
   385                 case IN_SMARTY:
   386                     return TplTopTokenId.T_SMARTY;
   387                 case OPEN_DELIMITER:
   388                     return TplTopTokenId.T_SMARTY_OPEN_DELIMITER;
   389                 case CLOSE_DELIMITER:
   390                     return TplTopTokenId.T_SMARTY_CLOSE_DELIMITER;
   391                 default:
   392                     return TplTopTokenId.T_HTML;
   393             }
   394         }
   395 
   396         Object getState() {
   397             return new CompoundState(state, subState);
   398         }
   399 
   400         private boolean isSmartyOpenDelimiter(CharSequence text) {
   401             return CharSequenceUtilities.endsWith(text, metadata.getOpenDelimiter());
   402         }
   403 
   404         private boolean isSmartyCloseDelimiter(CharSequence text) {
   405             return CharSequenceUtilities.endsWith(text, metadata.getCloseDelimiter());
   406         }
   407         
   408         private int getOpenDelimiterLength() {
   409             return metadata.getOpenDelimiter().length();
   410         }
   411 
   412         private int getCloseDelimiterLength() {
   413             return metadata.getCloseDelimiter().length();
   414         }
   415 
   416         private String readNextWord(int lastChar) {
   417             String word = Character.toString((char)lastChar);
   418             int c;
   419             while (!LexerUtils.isWS(c = input.read()) && c != LexerInput.EOF) {
   420                 word += Character.toString((char)c);
   421             }
   422             input.backup(1);
   423             return word;
   424         }
   425     }
   426 }