/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * See LICENSE.txt included in this distribution for the specific * language governing permissions and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at LICENSE.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ package org.opensolaris.opengrok.analysis; import java.io.FilterReader; import java.io.IOException; import java.io.Reader; import org.opensolaris.opengrok.configuration.Project; /** * Wrapper around Reader to expand tabs to spaces in the input. */ public class ExpandTabsReader extends FilterReader { /** The size of tabs. */ private final int tabSize; /** * The position on the current line. Used to decide how many spaces to * insert to fill up to the next tab stop. */ private int pos; /** * Number of spaces to insert (as replacement for a tab) before reading * more from the underlying stream. */ private int spacesToInsert; /** * Create a new ExpandTabsReader to expand tabs to spaces. * * @param in the original input source * @param tabSize the size of tabs */ ExpandTabsReader(Reader in, int tabSize) { super(in); this.tabSize = tabSize; } /** * Wrap a reader in an ExpandTabsReader if the project has custom tab * size settings. * * @param in the reader to wrap * @param p the project * @return {@code in} if the project doesn't have custom tab settings; * otherwise, an {@code ExpandTabsReader} that wraps {@code in} and expands * tabs as defined by the project's settings */ public static Reader wrap(Reader in, Project p) { return (p != null && p.hasTabSizeSetting()) ? new ExpandTabsReader(in, p.getTabSize()) : in; } @Override public int read() throws IOException { if (spacesToInsert > 0) { pos++; spacesToInsert--; return ' '; } int c = super.read(); if (c == '\t') { // Fill up with spaces up to the next tab stop int spaces = tabSize - (pos % tabSize); pos++; spacesToInsert = spaces - 1; return ' '; } if (c == '\n' || c == '\r') { // Reset position on new line pos = 0; } else { pos++; } return c; } @Override public int read(char[] cbuf, int off, int len) throws IOException { for (int i = 0; i < len; i++) { int c = read(); if (c == -1) { return (i > 0 ? i : -1); } cbuf[off + i] = (char) c; } return len; } @Override public long skip(long n) throws IOException { if (n < 0L) { throw new IllegalArgumentException("n is negative"); } long skipped = 0; for (long l = 0; l < n; l++) { int c = read(); if (c == -1) { break; } skipped++; } return skipped; } @Override public boolean markSupported() { // Support for mark/reset has not been implemented. return false; } }