package com.liferay.ide.velocity.editor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IWordDetector;
/**
* DOCUMENT ME!
*
* @version $Revision: 8 $
* @author <a href="mailto:akmal.sarhan@gmail.com">Akmal Sarhan </a>
*/
public class EditorsUtil
{
private static final char[] CR = { '\r' };
private static final char[] LF = { '\n' };
private static final char[] CRLF = { '\r', '\n' };
private static final char[] EMPTY = {};
/**
* Characters used for escape operations
*/
private static final String[][] HTML_ESCAPE_CHARS = { { "<", "<" }, { //$NON-NLS-1$ //$NON-NLS-2$
">", ">" }, { //$NON-NLS-1$ //$NON-NLS-2$
"&", "&" }, { //$NON-NLS-1$ //$NON-NLS-2$
""", "\"" }, { //$NON-NLS-1$ //$NON-NLS-2$
"à", "\u00e0" }, { //$NON-NLS-1$ //$NON-NLS-2$
"À", "\u00c0" }, { //$NON-NLS-1$ //$NON-NLS-2$
"â", "\u00e2" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ä", "\u00e4" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ä", "\u00c4" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Â", "\u00c2" }, { //$NON-NLS-1$ //$NON-NLS-2$
"å", "\u00e5" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Å", "\u00c5" }, { //$NON-NLS-1$ //$NON-NLS-2$
"æ", "\u00e6" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Æ", "\u00c6" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ç", "\u00e7" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ç", "\u00c7" }, { //$NON-NLS-1$ //$NON-NLS-2$
"é", "\u00e9" }, { //$NON-NLS-1$ //$NON-NLS-2$
"É", "\u00c9" }, { //$NON-NLS-1$ //$NON-NLS-2$
"á", "\u00e1" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Á", "\u00c1" }, { //$NON-NLS-1$ //$NON-NLS-2$
"è", "\u00e8" }, { //$NON-NLS-1$ //$NON-NLS-2$
"È", "\u00c8" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ê", "\u00ea" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ê", "\u00ca" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ë", "\u00eb" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ë", "\u00cb" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ï", "\u00ef" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ï", "\u00cf" }, { //$NON-NLS-1$ //$NON-NLS-2$
"í", "\u00ed" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Í", "\u00cd" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ã", "\u00e3" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ã", "\u00c3" }, { //$NON-NLS-1$ //$NON-NLS-2$
"õ", "\u00f5" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Õ", "\u00d5" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ó", "\u00f3" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ó", "\u00d3" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ô", "\u00f4" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ô", "\u00d4" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ö", "\u00f6" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ö", "\u00d6" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ø", "\u00f8" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ø", "\u00d8" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ß", "\u00df" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ù", "\u00f9" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ù", "\u00d9" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ú", "\u00fa" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ú", "\u00da" }, { //$NON-NLS-1$ //$NON-NLS-2$
"û", "\u00fb" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Û", "\u00db" }, { //$NON-NLS-1$ //$NON-NLS-2$
"ü", "\u00fc" }, { //$NON-NLS-1$ //$NON-NLS-2$
"Ü", "\u00dc" }, { //$NON-NLS-1$ //$NON-NLS-2$
" ", " " }, { //$NON-NLS-1$ //$NON-NLS-2$
"®", "\u00AE" }, { //$NON-NLS-1$ //$NON-NLS-2$
"©", "\u00A9" }, { //$NON-NLS-1$ //$NON-NLS-2$
"€", "\u20A0" }, { //$NON-NLS-1$ //$NON-NLS-2$
"€", "\u20AC" } //$NON-NLS-1$ //$NON-NLS-2$
};
/**
* Get html entity for escape character
* @return null, if no entity found for given character
*/
public static final String getEntityForChar(char ch)
{
switch (ch)
{
case '<':
return "<"; //$NON-NLS-1$
case '>':
return ">"; //$NON-NLS-1$
case '&':
return "&"; //$NON-NLS-1$
case '"':
return """; //$NON-NLS-1$
case '\u00e0':
return "à"; //$NON-NLS-1$
case '\u00e1':
return "á"; //$NON-NLS-1$
case '\u00c0':
return "À"; //$NON-NLS-1$
case '\u00c1':
return "Á"; //$NON-NLS-1$
case '\u00e2':
return "â"; //$NON-NLS-1$
case '\u00c2':
return "Â"; //$NON-NLS-1$
case '\u00e4':
return "ä"; //$NON-NLS-1$
case '\u00c4':
return "Ä"; //$NON-NLS-1$
case '\u00e5':
return "å"; //$NON-NLS-1$
case '\u00c5':
return "Å"; //$NON-NLS-1$
case '\u00e3':
return "ã"; //$NON-NLS-1$
case '\u00c3':
return "Ã"; //$NON-NLS-1$
case '\u00e6':
return "æ"; //$NON-NLS-1$
case '\u00c6':
return "Æ"; //$NON-NLS-1$
case '\u00e7':
return "ç"; //$NON-NLS-1$
case '\u00c7':
return "Ç"; //$NON-NLS-1$
case '\u00e9':
return "é"; //$NON-NLS-1$
case '\u00c9':
return "É"; //$NON-NLS-1$
case '\u00e8':
return "è"; //$NON-NLS-1$
case '\u00c8':
return "È"; //$NON-NLS-1$
case '\u00ea':
return "ê"; //$NON-NLS-1$
case '\u00ca':
return "Ê"; //$NON-NLS-1$
case '\u00eb':
return "ë"; //$NON-NLS-1$
case '\u00cb':
return "Ë"; //$NON-NLS-1$
case '\u00ed':
return "í"; //$NON-NLS-1$
case '\u00cd':
return "Í"; //$NON-NLS-1$
case '\u00ef':
return "ï"; //$NON-NLS-1$
case '\u00cf':
return "Ï"; //$NON-NLS-1$
case '\u00f5':
return "õ"; //$NON-NLS-1$
case '\u00d5':
return "Õ"; //$NON-NLS-1$
case '\u00f3':
return "ó"; //$NON-NLS-1$
case '\u00f4':
return "ô"; //$NON-NLS-1$
case '\u00d3':
return "Ó"; //$NON-NLS-1$
case '\u00d4':
return "Ô"; //$NON-NLS-1$
case '\u00f6':
return "ö"; //$NON-NLS-1$
case '\u00d6':
return "Ö"; //$NON-NLS-1$
case '\u00f8':
return "ø"; //$NON-NLS-1$
case '\u00d8':
return "Ø"; //$NON-NLS-1$
case '\u00df':
return "ß"; //$NON-NLS-1$
case '\u00f9':
return "ù"; //$NON-NLS-1$
case '\u00d9':
return "Ù"; //$NON-NLS-1$
case '\u00fa':
return "ú"; //$NON-NLS-1$
case '\u00da':
return "Ú"; //$NON-NLS-1$
case '\u00fb':
return "û"; //$NON-NLS-1$
case '\u00db':
return "Û"; //$NON-NLS-1$
case '\u00fc':
return "ü"; //$NON-NLS-1$
case '\u00dc':
return "Ü"; //$NON-NLS-1$
case '\u00AE':
return "®"; //$NON-NLS-1$
case '\u00A9':
return "©"; //$NON-NLS-1$
case '\u20A0':
return "€"; //$NON-NLS-1$
case '\u20AC':
return "€"; //$NON-NLS-1$
// case '' : return "€"; //$NON-NLS-1$
// case '\u20AC': return "€"; // euro
// be carefull with this one (non-breaking white space)
// case ' ' : return " "; //$NON-NLS-1$
default:
{
int ci = 0xffff & ch;
if (ci < 160)
{
// nothing special only 7 Bit
return null;
}
// Not 7 Bit use the unicode system
return "" + ci + ";"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
/**
*
* @param s
* string to be modified
* @return string with escape characters, changed to html entities
*/
public static final String escapeText(String s)
{
if (s == null) { return null; }
StringBuffer sb = new StringBuffer();
int n = s.length();
char c;
String entity;
for (int i = 0; i < n; i++)
{
c = s.charAt(i);
entity = getEntityForChar(c);
if (entity != null)
{
sb.append(entity);
} else
{
sb.append(c);
}
}
return sb.toString();
}
/**
*
* @param s
* string to unescape
* @return new string with html entities changed to escape characters
*/
public static final String unescapeText(String s)
{
int i, j, k;
int arraySize = HTML_ESCAPE_CHARS.length;
if (s != null && (i = s.indexOf("&")) > -1) { //$NON-NLS-1$
j = s.indexOf(";", i); //$NON-NLS-1$
if (j > i)
{
String temp = s.substring(i, j + 1);
// search in escape[][] if temp is there
k = 0;
while (k < arraySize)
{
if (HTML_ESCAPE_CHARS[k][0].equals(temp))
{
break;
}
k++;
}
// now we found html escape character
if (k < arraySize)
{
// replace it to ASCII
s = new StringBuffer(s.substring(0, i)).append(HTML_ESCAPE_CHARS[k][1]).append(s.substring(j + 1)).toString();
return unescapeText(s); // recursive call
} else if (k == arraySize)
{
s = new StringBuffer(s.substring(0, i)).append("&") //$NON-NLS-1$
.append(unescapeText(s.substring(i + 1))).toString();
return s;
}
}
}
return s;
}
public EditorsUtil()
{
}
/**
* DOCUMENT ME!
*
* @param d
* DOCUMENT ME!
* @param txt
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static boolean isLineDelimiter(IDocument d, String txt)
{
String[] delimiters = d.getLegalLineDelimiters();
for (int i = 0; i < delimiters.length; i++)
{
if (txt.equals(delimiters[i])) { return true; }
}
return false;
}
/**
* DOCUMENT ME!
*
* @param d
* DOCUMENT ME!
* @param txt
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static boolean endsWithDelimiter(IDocument d, String txt)
{
String[] delimiters = d.getLegalLineDelimiters();
for (int i = 0; i < delimiters.length; i++)
{
if (txt.equals(delimiters[i])) { return true; }
}
return false;
}
/**
* DOCUMENT ME!
*
* @param document
* DOCUMENT ME!
* @param offset
* DOCUMENT ME!
* @param tabwidth
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static int getIndentLength(IDocument document, int offset, int tabwidth)
{
int ret = 0;
try
{
int lineNr = document.getLineOfOffset(offset);
int start = document.getLineOffset(lineNr);
int len = document.getLineLength(lineNr);
for (int i = start; i < (start + len);)
{
char c = document.getChar(start);
if (c == '\t')
{
ret += (tabwidth - (ret % tabwidth));
} else if (c == ' ')
{
ret++;
} else
{
return ret;
}
}
return ret;
}
catch (BadLocationException badlocationexception)
{
return ret;
}
}
/**
* DOCUMENT ME!
*
* @param scanner
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static int skipWhitespace(ICharacterScanner scanner)
{
for (int c = scanner.read(); c != -1; c = scanner.read())
{
if (!Character.isWhitespace((char) c)) { return c; }
}
return -1;
}
/**
* DOCUMENT ME!
*
* @param delim
* DOCUMENT ME!
* @param scanner
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static boolean skipString(int delim, ICharacterScanner scanner)
{
for (int c = scanner.read(); c != -1; c = scanner.read())
{
if (c == delim) { return true; }
if ((c == 92) && (scanner.read() == -1))
{
scanner.unread();
}
}
scanner.unread();
return false;
}
/**
* DOCUMENT ME!
*
* @param detector
* DOCUMENT ME!
* @param scanner
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static String getWord(IWordDetector detector, ICharacterScanner scanner)
{
StringBuffer ret = new StringBuffer();
int c = scanner.read();
if (detector.isWordStart((char) c))
{
ret.append((char) c);
for (c = scanner.read(); c != -1; c = scanner.read())
{
if (!detector.isWordPart((char) c))
{
break;
}
ret.append((char) c);
}
}
scanner.unread();
return ret.toString();
}
/**
* DOCUMENT ME!
*
* @param d
* DOCUMENT ME!
* @param line
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static boolean isBlankLine(IDocument d, int line)
{
try
{
int start = d.getLineOffset(line);
int end;
for (end = start + d.getLineLength(line); start < end; start++)
{
if (!Character.isWhitespace(d.getChar(start))) { return false; }
}
return start >= end;
}
catch (BadLocationException badlocationexception)
{
return false;
}
}
/**
* DOCUMENT ME!
*
* @param offset
* DOCUMENT ME!
* @param doc
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static boolean isEscapedPartition(int offset, IDocument doc)
{
try
{
ITypedRegion partition = doc.getPartition(offset);
String type = partition.getType();
if ((offset != partition.getOffset())
&& (type.equals("__javadoc_partition") || type.equals("__comment_partition") || type.equals("__comment1_partition") || type.equals("__string_partition"))) { return true; }
}
catch (BadLocationException e)
{
}
return false;
}
/**
* DOCUMENT ME!
*
* @param offset
* DOCUMENT ME!
* @param partitions
* DOCUMENT ME!
* @param doc
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static boolean isInsidePartition(int offset, String[] partitions, IDocument doc)
{
return isInsidePartition(offset, partitions, false, doc);
}
/**
* DOCUMENT ME!
*
* @param offset
* DOCUMENT ME!
* @param partitions
* DOCUMENT ME!
* @param include_start
* DOCUMENT ME!
* @param doc
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public static boolean isInsidePartition(int offset, String[] partitions, boolean include_start, IDocument doc)
{
try
{
ITypedRegion partition = doc.getPartition(offset);
if (include_start || (offset != partition.getOffset()))
{
String type = partition.getType();
for (int i = 0; i < partitions.length; i++)
{
if (type.equals(partitions[i])) { return true; }
}
}
}
catch (BadLocationException e)
{
}
return false;
}
private static char[] getLineEnd(StringBuffer line)
{
if (line == null) { return EMPTY; }
int lastIdx = line.length() - 1;
if (lastIdx < 0) { return EMPTY; }
char last = line.charAt(lastIdx);
if (last == '\n')
{
if (lastIdx > 0)
{
if (line.charAt(lastIdx - 1) == '\r') { return CRLF; // windows
}
}
return LF; // unix
} else if (last == '\r')
{
return CR; // mac
} else
{
return EMPTY;
}
}
private static boolean removeTrailingSpace(StringBuffer line)
{
boolean changed = false;
char lastChar;
int lineLength = line.length();
int lastCharsLength = getLineEnd(line).length;
int lastIdx = lineLength - lastCharsLength - 1;
while (lastIdx >= 0)
{
lastChar = line.charAt(lastIdx);
if (lastChar != ' ' && lastChar != '\t')
{
break;
}
lastIdx--;
}
if (lastIdx != lineLength - lastCharsLength - 1)
{
line.delete(lastIdx + 1, lineLength - lastCharsLength);
changed = true;
}
return changed;
}
public static boolean convertSpacesToTabs(StringBuffer line, int tabWidth, boolean removeTrailing)
{
char lastChar;
boolean changed = false;
if (removeTrailing)
{
changed = removeTrailingSpace(line);
}
int lineLength = line.length();
int spacesCount = 0;
int tabsCount = 0;
int i = 0;
for (; i < lineLength; i++)
{
lastChar = line.charAt(i);
if (lastChar == ' ')
{
changed = true;
spacesCount++;
} else if (lastChar == '\t')
{
tabsCount++;
} else
{
break;
}
}
if (spacesCount > 0)
{
tabsCount += spacesCount / tabWidth;
// modulo rest
int extraSpaces = spacesCount % tabWidth;
if (i - extraSpaces <= 0 || spacesCount - extraSpaces <= 0) { return false; }
// delete whitespace to 'i' index, replace with tabs
line.delete(0, i);
line.insert(0, fillWith(tabsCount, '\t'));
// if some last spaces exists, add them back
if (extraSpaces > 0)
{
line.insert(tabsCount, fillWith(extraSpaces, ' '));
}
}
return changed;
}
private static char[] fillWith(int length, char c)
{
char[] chars = new char[length];
for (int i = 0; i < length; i++)
{
chars[i] = c;
}
return chars;
}
}