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; } }