/*
* BufferedTextArea.java
*
* Copyright (C) 2010 Leo Osvald <leo.osvald@gmail.com>
*
* This file is part of SGLJ.
*
* SGLJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SGLJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sglj.swing;
import javax.swing.JTextArea;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
/**
* <p>Text area which has it's own buffer. The size of the buffer is equal
* to the number of characters that can be displayed at a time. Buffer size
* can be set via constructor or by calling {@link #setBufferSize(int)}
* method.<br>
* If the size is not set explicitly, it will be set to the default value
* which can be obtained by calling {@link #defaultBufferSize()} method.</p>
* <p>The buffer size is actually two times bigger than specified;
* whenever the size exceeds that number, first half of the characters
* are discarded.
* In other words, this component displays at most 2*{@link #getBufferSize()}
* characters.<br>
* This strategy ensures that appending takes linear time proportional to
* the length of the appended text (regardless of buffer size).
* </p>
* <p><b>Note:</b> Insertion in the middle is not as efficient as appending
* to the end.
* </p>
*
* @author Leo Osvald
*
* @version 0.86
*/
public class BufferedTextArea extends JTextArea {
private int bufferSize = DEFAULT_BUFFER_SIZE;
private int charCount = 0;
private final transient DocumentListener docListener
= new MyDocumentListener();
private static final int DEFAULT_BUFFER_SIZE = 10000;
private static final long serialVersionUID = 1L;
public BufferedTextArea(int bufferSize) {
this.bufferSize = bufferSize;
getDocument().addDocumentListener(docListener);
}
public BufferedTextArea() {
this(DEFAULT_BUFFER_SIZE);
}
public BufferedTextArea(Document doc) {
super(doc);
getDocument().addDocumentListener(docListener);
}
public BufferedTextArea(String text) {
super(text);
charCount = text.length();
getDocument().addDocumentListener(docListener);
}
public BufferedTextArea(int rows, int cols) {
super(rows, cols);
getDocument().addDocumentListener(docListener);
}
public BufferedTextArea(String text, int rows, int cols) {
super(text, rows, cols);
charCount = text.length();
getDocument().addDocumentListener(docListener);
}
@Override
public void append(String str) {
//u slucaju da je netko umetao u sredinu documenta i tako prepunio buffer
cutIfNeeded();
//normalni slucajevi
if(str.length() >= bufferSize)
setText(str);
else if(charCount < bufferSize) {
super.append(str);
}
else {
int overflow = charCount + str.length() - 2*bufferSize;
if(overflow >= 0) {
int toStay = bufferSize - str.length();
try {
getDocument().remove(0, charCount-toStay);
super.append(str);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
else {
super.append(str);
}
}
}
@Override
public void setText(String t) {
String s = getLastChars(t);
super.setText(s);
}
@Override
public void setDocument(Document doc) {
// System.out.println("Postavljam document");
//remove listener from old document
if(getDocument() != null)
getDocument().removeDocumentListener(docListener);
//set new document and add listener
super.setDocument(doc);
getDocument().addDocumentListener(docListener);
}
public int getBufferSize() {
return bufferSize;
}
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
public int defaultBufferSize() {
return DEFAULT_BUFFER_SIZE;
}
private String getLastChars(String s) {
int len = (s != null ? s.length() : 0);
if(len <= bufferSize) return s;
return s.substring(len-bufferSize);
}
private void cutIfNeeded() {
if(charCount >= 2*bufferSize)
try {
getDocument().remove(0, charCount-bufferSize);
throw new IllegalStateException("Inserting in the middle is forbidden!");
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
private class MyDocumentListener implements DocumentListener {
@Override
public void removeUpdate(DocumentEvent e) {
charCount = e.getDocument().getLength();
}
@Override
public void insertUpdate(DocumentEvent e) {
charCount = e.getDocument().getLength();
// cutIfNeeded(); //TODO throws exception Write lock?
}
@Override
public void changedUpdate(DocumentEvent e) {
}
}
}