QuoteHelper.java

  1package eu.siacs.conversations.ui.util;
  2
  3import eu.siacs.conversations.Config;
  4import eu.siacs.conversations.utils.UIHelper;
  5
  6public class QuoteHelper {
  7
  8
  9    public static final char QUOTE_CHAR = '>';
 10    public static final char QUOTE_END_CHAR = '<'; // used for one check, not for actual quoting
 11    public static final char QUOTE_ALT_CHAR = '»';
 12    public static final char QUOTE_ALT_END_CHAR = '«';
 13
 14    public static boolean isPositionQuoteCharacter(CharSequence body, int pos) {
 15        // second part of logical check actually goes against the logic indicated in the method name, since it also checks for context
 16        // but it's very useful
 17        return body.charAt(pos) == QUOTE_CHAR || isPositionAltQuoteStart(body, pos);
 18    }
 19
 20    public static boolean isPositionQuoteEndCharacter(CharSequence body, int pos) {
 21        return body.charAt(pos) == QUOTE_END_CHAR;
 22    }
 23
 24    public static boolean isPositionAltQuoteCharacter(CharSequence body, int pos) {
 25        return body.charAt(pos) == QUOTE_ALT_CHAR;
 26    }
 27
 28    public static boolean isPositionAltQuoteEndCharacter(CharSequence body, int pos) {
 29        return body.charAt(pos) == QUOTE_ALT_END_CHAR;
 30    }
 31
 32    public static boolean isPositionAltQuoteStart(CharSequence body, int pos) {
 33        return isPositionAltQuoteCharacter(body, pos)
 34                && isPositionPrecededByPreQuote(body, pos)
 35                && !isPositionFollowedByAltQuoteEnd(body, pos);
 36    }
 37
 38    public static boolean isPositionFollowedByQuoteChar(CharSequence body, int pos) {
 39        return body.length() > pos + 1 && isPositionQuoteCharacter(body, pos + 1);
 40    }
 41
 42    /**
 43     *  'Prequote' means anything we require or can accept in front of a QuoteChar.
 44     */
 45    public static boolean isPositionPrecededByPreQuote(CharSequence body, int pos) {
 46        return UIHelper.isPositionPrecededByLineStart(body, pos);
 47    }
 48
 49    public static boolean isPositionQuoteStart(CharSequence body, int pos) {
 50        return (isPositionQuoteCharacter(body, pos)
 51                && isPositionPrecededByPreQuote(body, pos)
 52                && (UIHelper.isPositionFollowedByQuoteableCharacter(body, pos)
 53                || isPositionFollowedByQuoteChar(body, pos)));
 54    }
 55
 56    public static boolean bodyContainsQuoteStart(CharSequence body) {
 57        for (int i = 0; i < body.length(); i++) {
 58            if (isPositionQuoteStart(body, i)) {
 59                return true;
 60            }
 61        }
 62        return false;
 63    }
 64
 65    public static boolean isPositionFollowedByAltQuoteEnd(CharSequence body, int pos) {
 66        if (body.length() <= pos + 1 || Character.isWhitespace(body.charAt(pos + 1))) {
 67            return false;
 68        }
 69        boolean previousWasWhitespace = false;
 70        for (int i = pos + 1; i < body.length(); i++) {
 71            char c = body.charAt(i);
 72            if (c == '\n' || isPositionAltQuoteCharacter(body, i)) {
 73                return false;
 74            } else if (isPositionAltQuoteEndCharacter(body, i) && !previousWasWhitespace) {
 75                return true;
 76            } else {
 77                previousWasWhitespace = Character.isWhitespace(c);
 78            }
 79        }
 80        return false;
 81    }
 82
 83    public static boolean isNestedTooDeeply(CharSequence line) {
 84        if (isPositionQuoteStart(line, 0)) {
 85            int nestingDepth = 1;
 86            for (int i = 1; i < line.length(); i++) {
 87                if (isPositionQuoteCharacter(line, i)) {
 88                    nestingDepth++;
 89                } else if (line.charAt(i) != ' ') {
 90                    break;
 91                }
 92            }
 93            return nestingDepth >= (Config.QUOTING_MAX_DEPTH);
 94        }
 95        return false;
 96    }
 97
 98    public static String replaceAltQuoteCharsInText(String text) {
 99        for (int i = 0; i < text.length(); i++) {
100            if (isPositionAltQuoteStart(text, i)) {
101                text = text.substring(0, i) + QUOTE_CHAR + text.substring(i + 1);
102            }
103        }
104        return text;
105    }
106
107    public static String quote(String text) {
108        text = replaceAltQuoteCharsInText(text);
109        return text
110                // first replace all '>' at the beginning of the line with nice and tidy '>>'
111                // for nested quoting
112                .replaceAll("(^|\n)(" + QUOTE_CHAR + ")", "$1$2$2")
113                // then find all other lines and have them start with a '> '
114                .replaceAll("(^|\n)(?!" + QUOTE_CHAR + ")(.*)", "$1> $2")
115        ;
116    }
117}