package org.rubypeople.rdt.core.formatter;
import java.util.Map;
import org.eclipse.jface.text.Assert;
import org.rubypeople.rdt.core.RubyCore;
public class Indents {
private Indents() {
}
/**
* Returns the tab width as configured in the given map.
* @param options the map to get the formatter settings from. Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to
* get the most current project options.
* @return the tab width
*/
public static int getTabWidth(Map options) {
if (options == null) {
throw new IllegalArgumentException();
}
return getIntValue(options, DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, 4);
}
/**
* Returns the tab width as configured in the given map.
* @param options the map to get the formatter settings from. Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to
* get the most current project options.
* @return the indent width
*/
public static int getIndentWidth(Map options) {
if (options == null) {
throw new IllegalArgumentException();
}
int tabWidth=getTabWidth(options);
boolean isMixedMode= DefaultCodeFormatterConstants.MIXED.equals(options.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR));
if (isMixedMode) {
return getIntValue(options, DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE, tabWidth);
}
return tabWidth;
}
private static int getIntValue(Map options, String key, int def) {
try {
return Integer.parseInt((String) options.get(key));
} catch (NumberFormatException e) {
return def;
}
}
/**
* Returns the indentation of the given line in indentation units. Odd spaces are
* not counted. This method only analyzes the content of <code>line</code> up to the first
* non-whitespace character.
*
* @param line the string to measure the indent of
* @param tabWidth the width of one tab character in space equivalents
* @param indentWidth the width of one indentation unit in space equivalents
* @return the number of indentation units that line is indented by
*/
public static int measureIndentUnits(CharSequence line, int tabWidth, int indentWidth) {
if (indentWidth <= 0 || tabWidth < 0 || line == null) {
throw new IllegalArgumentException();
}
int visualLength= measureIndentInSpaces(line, tabWidth);
return visualLength / indentWidth;
}
/**
* Creates a string that represents the given number of indentation units.
* The returned string can contain tabs and/or spaces depending on the core
* formatter preferences.
*
* @param indentationUnits the number of indentation units to generate
* @param options the options to get the formatter settings from. Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to
* get the most current project options.
* @return the indent string
*/
public static String createIndentString(int indentationUnits, Map options) {
if (indentationUnits < 0) {
return "" ;
}
if (options == null || indentationUnits < 0) {
throw new IllegalArgumentException();
}
String tabChar= getStringValue(options, DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, RubyCore.TAB);
final int tabs, spaces;
if (RubyCore.SPACE.equals(tabChar)) {
tabs= 0;
spaces= indentationUnits * getIndentWidth(options);
} else if (RubyCore.TAB.equals(tabChar)) {
// indentWidth == tabWidth
tabs= indentationUnits;
spaces= 0;
} else if (DefaultCodeFormatterConstants.MIXED.equals(tabChar)){
int tabWidth= getTabWidth(options);
int spaceEquivalents= indentationUnits * getIndentWidth(options);
if (tabWidth > 0) {
tabs= spaceEquivalents / tabWidth;
spaces= spaceEquivalents % tabWidth;
} else {
tabs= 0;
spaces= spaceEquivalents;
}
} else {
// new indent type not yet handled
Assert.isTrue(false);
return null;
}
StringBuffer buffer= new StringBuffer(tabs + spaces);
for(int i= 0; i < tabs; i++)
buffer.append('\t');
for(int i= 0; i < spaces; i++)
buffer.append(' ');
return buffer.toString();
}
public static String createFixIndentString(int fixIndentation, Map options) {
if (options == null || fixIndentation < 0) {
throw new IllegalArgumentException();
}
String tabChar= getStringValue(options, DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, RubyCore.TAB);
final int tabs, spaces;
if (RubyCore.SPACE.equals(tabChar)) {
tabs= 0;
spaces= fixIndentation;
} else if (RubyCore.TAB.equals(tabChar)) {
int tabWidth= getTabWidth(options);
tabs= fixIndentation / tabWidth;
spaces= 0;
} else if (DefaultCodeFormatterConstants.MIXED.equals(tabChar)){
int tabWidth= getTabWidth(options);
if (tabWidth > 0) {
tabs= fixIndentation / tabWidth;
spaces= fixIndentation % tabWidth;
} else {
tabs= 0;
spaces= fixIndentation;
}
} else {
// new indent type not yet handled
Assert.isTrue(false);
return null;
}
StringBuffer buffer= new StringBuffer(tabs + spaces);
for(int i= 0; i < tabs; i++)
buffer.append('\t');
for(int i= 0; i < spaces; i++)
buffer.append(' ');
return buffer.toString();
}
private static String getStringValue(Map options, String key, String def) {
Object value= options.get(key);
if (value instanceof String)
return (String) value;
return def;
}
/**
* Returns the indentation of the given line in space equivalents.
* Tab characters are counted using the given <code>tabWidth</code> and every other indent
* character as one. This method analyzes the content of <code>line</code> up to the first
* non-whitespace character.
*
* @param line the string to measure the indent of
* @param tabWidth the width of one tab in space equivalents
* @return the measured indent width in space equivalents
*/
public static int measureIndentInSpaces(CharSequence line, int tabWidth) {
if (tabWidth < 0 || line == null) {
throw new IllegalArgumentException();
}
int length= 0;
int max= line.length();
for (int i= 0; i < max; i++) {
char ch= line.charAt(i);
if (ch == '\t') {
int reminder= length % tabWidth;
length += tabWidth - reminder;
} else if (isIndentChar(ch)) {
length++;
} else {
return length;
}
}
return length;
}
/**
* Returns the leading indentation string of the given line. Note that the
* returned string need not be equal to the leading whitespace as odd spaces
* are not considered part of the indentation.
*
* @param line
* the line to scan
* @param tabWidth
* the size of one tab in space equivalents
* @param indentWidth
* the width of one indentation unit in space equivalents
* @return the indent part of <code>line</code>, but no odd spaces
*/
public static String extractIndentString(String line, int tabWidth, int indentWidth) {
if (tabWidth < 0 || indentWidth <= 0 || line == null) { throw new IllegalArgumentException(); }
int size = line.length();
int end = 0;
int spaceEquivs = 0;
int characters = 0;
for (int i = 0; i < size; i++) {
char c = line.charAt(i);
if (c == '\t') {
int remainder = spaceEquivs % tabWidth;
spaceEquivs += tabWidth - remainder;
characters++;
} else if (isIndentChar(c)) {
spaceEquivs++;
characters++;
} else {
break;
}
if (spaceEquivs >= indentWidth) {
end += characters;
characters = 0;
spaceEquivs = spaceEquivs % indentWidth;
}
}
if (end == 0)
return ""; //$NON-NLS-1$
else if (end == size)
return line;
else
return line.substring(0, end);
}
/**
* Tests if a character is an indent character. Indent character are all
* whitespace characters except the line delimiter characters.
*
* @param ch
* The character to test
* @return Returns <code>true</code> if this the character is a indent
* character
*/
public static boolean isIndentChar(char ch) {
return Character.isWhitespace(ch) && !isLineDelimiterChar(ch);
}
/**
* Tests if a character is a line delimiter character.
*
* @param ch
* The character to test
* @return Returns <code>true</code> if this the character is a line
* delimiter character
*/
public static boolean isLineDelimiterChar(char ch) {
return ch == '\n' || ch == '\r';
}
public static String extractIndentString(String line, Map options) {
return extractIndentString(line, getTabWidth(options), getIndentWidth(options));
}
}