/*
* Copyright (C) 2015-2017 Emanuel Moecklin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.onegravity.rteditor.utils;
import android.text.Spanned;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class finds the Paragraphs in a Spanned text.
*
* A paragraph spans from one \n (exclusive) to the next one (inclusive):
* first paragraph\nsecond paragraph\nthird paragraph
* |_______________||________________||_____________|
*
* We need this for all the paragraph formatting (bullet points, indentation etc.).
* While it's optimized for performance, it should still be used with caution.
*/
public class RTLayout implements Serializable {
private static final long serialVersionUID = 2210969820444215580L;
private static final Pattern LINEBREAK_PATTERN = Pattern.compile("\\r\\n|\\r|\\n");
private int mNrOfLines = 0;
private final ArrayList<Paragraph> mParagraphs = new ArrayList<>();
public RTLayout(final Spanned spanned) {
if (spanned != null) {
final String s = spanned.toString();
// find the line breaks and the according lines / paragraphs
mNrOfLines = 1;
final Matcher m = LINEBREAK_PATTERN.matcher(s);
int groupStart = 0;
while (m.find()) {
// the line feeds are part of the paragraph isFirst isLast
Paragraph paragraph = new Paragraph(groupStart, m.end(), mNrOfLines == 1, false);
mParagraphs.add(paragraph);
groupStart = m.end();
mNrOfLines++;
}
// even an empty line after the last cr/lf is considered a paragraph
if (mParagraphs.size() < mNrOfLines) {
Paragraph paragraph = new Paragraph(groupStart, s.length(), mNrOfLines == 1, true);
mParagraphs.add(paragraph);
}
}
}
/**
* @return all Paragraphs for this layout / spanned text.
*/
public ArrayList<Paragraph> getParagraphs() {
return mParagraphs;
}
/**
* @return the line for a certain position in the spanned text
*/
public int getLineForOffset(final int offset) {
int lineNr = 0;
while (lineNr < mNrOfLines && offset >= mParagraphs.get(lineNr).end()) {
lineNr++;
}
return Math.min(Math.max(0, lineNr), mParagraphs.size() - 1);
}
/**
* @return the start position of a certain line in the spanned text
*/
public int getLineStart(final int line) {
return mNrOfLines == 0 || line < 0 ? 0 :
line < mNrOfLines ? mParagraphs.get(line).start() :
mParagraphs.get(mNrOfLines - 1).end() - 1;
}
/**
* @return the end position of a certain line in the spanned text
*/
public int getLineEnd(final int line) {
return mNrOfLines == 0 || line < 0 ? 0 :
line < mNrOfLines ? mParagraphs.get(line).end() :
mParagraphs.get(mNrOfLines - 1).end() - 1;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
int line = 1;
for (Paragraph p : mParagraphs) {
s.append(line++).append(": ").append(p.start()).append("-").append(p.end())
.append(p.isLast() ? "" : ", ");
}
return s.toString();
}
}