All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.inet.lib.less.LessLookAheadReader Maven / Gradle / Ivy

/**
 * MIT License (MIT)
 *
 * Copyright (c) 2014 - 2015 Volker Berlin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * UT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @author Volker Berlin
 * @license: The MIT license 
 */
package com.inet.lib.less;

import java.io.Closeable;
import java.io.IOException;
import java.io.Reader;

/**
 * A reader with some special look ahead reading.
 */
class LessLookAheadReader extends LessObject implements Closeable {

    private final Reader        reader;

    private final StringBuilder cache = new StringBuilder();

    private int                 cachePos;

    LessLookAheadReader( Reader reader, String fileName ) {
        super( fileName );
        this.reader = reader;
        line = 1;
        column = 0;
    }

    int nextBlockMarker() {
        cache.setLength( cachePos = 0 );
        int parenthesis = 0;
        boolean isSlash = false;
        try {
            for( ;; ) {
                int ch = reader.read();
                if( ch < 0 ) {
                    for( int i = 0; i < cache.length(); i++ ) {
                        if( !Character.isWhitespace( cache.charAt( i ) ) ) {
                            return ';'; // a not terminated line is like a lime with semicolon
                        }
                    }
                    return -1;
                }
                cache.append( (char)ch );
                switch( ch ) {
                    case '/':
                        if( isSlash ) {
                            if( parenthesis > 0 && cache.indexOf( "url" ) > 0 ) {
                                break; // url function with unquoted url like url(http://xyz)
                            }
                            cache.setLength( cache.length() - 2 );
                            skipLine();
                            ch = 0;
                        }
                        break;
                    case '*':
                        if( isSlash ) {
                            boolean isAsterix = false;
                            for( ;; ) {
                                ch = reader.read();
                                if( ch < 0 ) {
                                    throw createException( "Unrecognized input: '" + cache.toString().trim() + "'" );
                                }
                                cache.append( (char)ch );
                                if( ch == '/' && isAsterix ) {
                                    ch = 0;
                                    break;
                                }
                                isAsterix = ch == '*';
                            }
                        }
                        break;
                    case '{':
                        if( cache.length() > 1 && cache.charAt( cache.length() - 2 ) == '@' ) { // @{  --> a inline variable and not a block start
                            do {
                                ch = reader.read();
                                if( ch < 0 ) {
                                    throw createException( "Unrecognized input: '" + cache.toString().trim() + "'" );
                                }
                                cache.append( (char)ch );
                            } while( ch != '}' );
                            break;
                        }
                        //$FALL-THROUGH$
                    case '}':
                    case ';':
                        if( parenthesis == 0 ) {
                            return ch;
                        }
                        break;
                    case '(':
                        parenthesis++;
                        break;
                    case ')':
                        if( --parenthesis < 0 ) {
                            throw createException( "Unrecognized input: '" + cache.toString().trim() + "'" );
                        }
                        break;
                    case '"':
                    case '\'':
                        int ch2;
                        isSlash = false;
                        for( ;; ) {
                            ch2 = reader.read();
                            if( ch2 < 0 ) {
                                return ';'; // a not terminated line is like a lime with semicolon
                            }
                            cache.append( (char)ch2 );
                            if( ch == ch2 && !isSlash ) {
                                break;
                            }
                            isSlash = ch2 == '\\';
                        }
                        break;
                    case '\\':
                        cache.append( (char)reader.read() );
                        break;
                    default:
                }
                isSlash = ch == '/';
            }
        } catch( IOException ex ) {
            throw new LessException( ex );
        }
    }
    
    String getLookAhead(){
        return cache.toString();
    }


    /**
     * If the next data which are already in the cache are a mixin parameter or part of a selector name.
     * This is call after a left parenthesis.
     * Samples for selectors:
    *
  • (min-resolution: 192dpi) *
  • (.clearfix all) *
  • (audio:not([controls])) *
  • (odd) *
  • (@{color}) *
* Samples for mixin params
    *
  • (.65) *
  • (@color) *
  • () *
  • (...) *
  • (red) *
  • (1) *
* @param isBlock selector of a block or a semicolon line * @return true, if it is a mixin parameter */ boolean nextIsMixinParam( boolean isBlock ) { boolean isFirst = true; for( int i=cachePos; i < cache.length(); i++ ) { char ch = cache.charAt( i ); switch( ch ) { case ')': return true; case '@': return cache.charAt( i+1 ) != '{'; case '~': return true; case '"': return !isBlock; case '.': if( !isFirst ) { continue; } else { if( Character.isDigit( cache.charAt( i + 1 ) ) ) { //Number with a starting point return true; } } if( i + 2 < cache.length() && cache.charAt( i + 1 ) == '.' && cache.charAt( i + 2 ) == '.' ) { // ... return true; } return false; case ':': case '[': return false; case ' ': continue; default: isFirst = false; } } return false; } /** * Read a single character from reader or from back buffer * * @return a character or -1 if EOF * @throws LessException * If an I/O error occurs */ char read() { try { if( cachePos < cache.length() ) { return incLineColumn( cache.charAt( cachePos++ ) ); } int ch = reader.read(); if( ch == -1 ) { throw createException( "Unexpected end of Less data" ); } return incLineColumn( ch ); } catch( IOException ex ) { throw new LessException( ex ); } } /** * Push a char back to the stream * * @param ch * the char */ void back( char ch ) { cachePos--; cache.setCharAt( cachePos, ch ); column--; // reverse of incLineColumn() } /** * Skip all data until a newline occur or an EOF */ void skipLine() { int ch; do { try { ch = reader.read(); } catch( IOException ex ) { throw new LessException( ex ); } incLineColumn( ch ); } while( ch != '\n' && ch != -1 ); } private char incLineColumn( int ch ) { if( ch == '\n' ) { line++; column = 0; } else { column++; } return (char)ch; } int getLine() { return line; } int getColumn() { return column; } String getFileName() { return filename; } @Override public void close() throws IOException { reader.close(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy