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

com.google.common.css.compiler.passes.CodeBuffer Maven / Gradle / Ivy

Go to download

Closure Stylesheets is an extension to CSS that adds variables, functions, conditionals, and mixins to standard CSS. The tool also supports minification, linting, RTL flipping, and CSS class renaming.

There is a newer version: 20160212
Show newest version
/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.css.compiler.passes;

import com.google.common.base.Preconditions;

import javax.annotation.Nullable;

/**
 * Class to write/delete characters used by {@link CodePrinter} subclasses to print out code.
 * It has finalized core public APIs to endure correct update of character index and line index
 * while operating on the buffer.
 *
 * 

This class can be extended and with helper methods if necessary. * @see TemplateCompactPrinter * *

{@code char} is used as operation unit for methods, there is no support for surrogates. * * @author [email protected] (Chenyun Yang) */ public class CodeBuffer { private final StringBuilder sb; /** * The index within the line of the next character to be written to the buffer. * Indices start at 0, following source map v3. */ private int nextCharIndex; /** * The index of the line that will contain the character at nextCharIndex. * Indices start at 0, following source map v3. */ private int nextLineIndex; /** * The index of the last character written to the buffer. * Indices start at 0, following source map v3. */ private int lastCharIndex; CodeBuffer () { this.sb = new StringBuilder(); resetIndex(); } /** Returns buffer as String. */ public final String getOutput() { return sb.toString(); } /** Returns the current length of the buffer. */ public final int getCurrentLength() { return sb.length(); } /** Returns the last character in the buffer. */ public final char getLastChar() { return sb.charAt(sb.length() - 1); } /** * Returns {@link #nextCharIndex}. */ public final int getNextCharIndex() { return nextCharIndex; } /** * Returns {@link #nextLineIndex}. */ public final int getNextLineIndex() { return nextLineIndex; } /** * Returns {@link #lastCharIndex}. */ public final int getLastCharIndex() { return lastCharIndex; } /** * Returns the index of the line which contains the last character at lastCharIndex. * It is always the same or 1 index behind {@link #nextLineIndex}. */ public final int getLastLineIndex() { return nextLineIndex - ( (sb.length() > 0 && sb.charAt(sb.length() - 1) == '\n') ? 1 : 0); } /** * Appends {@code str} to the buffer. The string is safe to contain newlines. * {@code nextCharIndex} and {@code nextLineIndex} will be updated accordingly. * *

Prefer to use append(char c) API to append characters for efficiency. */ public final CodeBuffer append(@Nullable String str) { if (str == null) { return this; } if (str.length() == 1) { append(str.charAt(0)); } else { String[] parts = str.split("\n", -1); for (int i = 0; i < parts.length; i++) { sb.append(parts[i]); incrementIndexBy(parts[i].length()); if (i != (parts.length - 1)) { startNewLine(); } } } return this; } /** * Appends {@code c} to the buffer and update {@code nextCharIndex}. */ public final CodeBuffer append(char c) { if (c == '\n') { startNewLine(); } else { sb.append(c); incrementIndexBy(1); } return this; } /** Appends the {@code toString} representation of {@code o} to the buffer. */ public final CodeBuffer append(Object o) { append(o.toString()); return this; } /** * Appends a newline to buffer, resetting {@code nextCharIndex} and incrementing * {@code nextLineIndex}. */ public final CodeBuffer startNewLine() { sb.append('\n'); incrementIndexForNewline(); return this; } /** * Deletes the last character in the buffer and updates the {@code nextCharIndex} and * {@code nextLineIndex}. */ public final CodeBuffer deleteLastChar() { if (getCurrentLength() > 0) { decrementIndex(); sb.deleteCharAt(getCurrentLength() - 1); } return this; } /** Deletes last {@code n} characters in the buffer. */ public final CodeBuffer deleteLastChars(int n) { for (int i = 0; i < n; i++) { deleteLastChar(); } return this; } /** * Clears the contents of the buffer and resets {@code nextCharIndex} * and {@code nextLineIndex}. */ public final CodeBuffer reset() { resetIndex(); sb.setLength(0); sb.trimToSize(); return this; } /** * Deletes the last character from the string builder if the character is as given. * *

Subclasses can modify this method in order to delete more in cases where they've added extra * delimiters. * * @param ch the character to delete */ public void deleteLastCharIfCharIs(char ch) { if (getCurrentLength() > 0 && getLastChar() == ch) { deleteLastChar(); } } /** Deletes the end of the buffer if it exactly equals {@code s}. */ public void deleteEndingIfEndingIs(String s) { if (sb.subSequence(sb.length() - s.length(), sb.length()).equals(s)) { deleteLastChars(s.length()); } } /** * Updates character-related indexes before or after writing some non-newline characters * to the buffer. Use {@link #incrementIndexForNewline} when writing '\n'. * * @param step numbers of characters writes to buffer */ private void incrementIndexBy(int step) { if (step > 0) { lastCharIndex = nextCharIndex + step - 1; nextCharIndex = nextCharIndex + step; } } /** * Updates character-related indexes before/after writing a newline character to the buffer. */ private void incrementIndexForNewline() { // '\n' is written at {@code nextCharIndex} lastCharIndex = nextCharIndex; nextCharIndex = 0; // future writing starts at a new line nextLineIndex++; } /** * Updates character-related indexes before deleting a character in buffer. * *

Note: Indexes updates must take place before deletion as the last two chars determine * the behavior. */ private void decrementIndex() { Preconditions.checkState(getCurrentLength() > 0); nextCharIndex = lastCharIndex; // Need to look at the last character to determine how to update indexes int lastIndex = getCurrentLength() - 1; // As a '\n' will be removed, {@code nextLineIndex} should be moved to previous line if (sb.charAt(lastIndex) == '\n') { nextLineIndex--; } // When the second to last char is a newline, needs to recalculate the {@code lastCharIndex} if (lastIndex - 1 > 0 && sb.charAt(lastIndex - 1) == '\n') { int lastNewline = sb.lastIndexOf("\n"); int secondToLastNewLine = sb.substring(0, lastNewline - 1).lastIndexOf('\n'); if (secondToLastNewLine == -1) { // when only one line left after deletion lastCharIndex = lastNewline - 1; } else { // Adjust to 0-based index for {@code lastCharIndex}. lastCharIndex = lastNewline - secondToLastNewLine - 1; } } else { // Otherwise, when deletion happens on same line, move the index one character to the left lastCharIndex = lastCharIndex - 1; } } private void resetIndex() { nextCharIndex = 0; nextLineIndex = 0; lastCharIndex = -1; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy