/*
* PlainMessageConsole.java
*
* Copyright (C) 2009 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.msg;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
/**
*
* <p>Plain console with only one buffer which stores all messages,
* regardless of their type.</p>
* <p>This implementation is efficient and on average it takes constant time
* to insert a message, which is only affected by the length of the message,
* but not the buffer size.</p>
* <p>This implementation is <b>thread-safe</b></p>.
*
* @author Leo Osvald
* @version 0.952
*/
public class PlainMessageConsole implements MessageConsole {
private final MessagePublisher msgPublisher;
private final Vector<MessageConsoleView> views = new Vector<MessageConsoleView>();
private MessageFilter msgFilter = new DefaultMessageFilter();
private final Vector<Message> buffer = new Vector<Message>();
private StringBuffer currMessage = new StringBuffer();
private boolean isMessagePending;
private int messageCount = 0;
private int charCount = 0;
private int messageCapacity;
private int charCapacity;
private static final int DEFAULT_MESSAGE_CAPACITY = 100;
public PlainMessageConsole(int typeCount, int messageCapacity) {
this.messageCapacity = messageCapacity;
msgPublisher = new DefaultMessagePublisher(typeCount);
msgPublisher.addMessageHandler(this);
}
public PlainMessageConsole(int typeCount) {
this(typeCount, DEFAULT_MESSAGE_CAPACITY);
}
/**
* {@inheritDoc}
*/
@Override
public void addMessageHandler(MessageHandler handler) {
msgPublisher.addMessageHandler(handler);
}
/**
* {@inheritDoc}
*/
@Override
public boolean cancel(MessageType type) {
return msgPublisher.cancel(type);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void clear() {
buffer.clear();
messageCount = 0;
informRefresh();
}
/**
* {@inheritDoc}
*/
@Override
public MessageConsole endMessage(MessageType type) {
messageReceived(new Message(currMessage.toString(), type));
return this;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int getMessageCapacity() {
return messageCapacity;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int getMessageCount() {
return buffer.size();
}
/**
* {@inheritDoc}
*/
@Override
public MessageFilter getMessageFilter() {
return msgFilter;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized List<Message> getMessages() {
//TODO ovo treba pametnije izvest mozda
return new ArrayList<Message>(buffer);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int getSize() {
return charCount;
}
/**
* Ova metoda sluzi samo za interno pozivanje.<br>
* Vanjsko pozivanje je zabranjeno jer poruka ne prolazi kroz filter.
*/
@Override
public synchronized void messageReceived(Message message) {
isMessagePending = false;
if(!msgFilter.accept(message)) return ;
buffer.add(message);
messageCount++;
charCount += message.getText().length();
boolean wasOverflow = false;
if(messageCapacity != 0 && messageCount >= 2*messageCapacity) {
buffer.subList(0, messageCapacity).clear();
messageCount -= messageCapacity;
wasOverflow = true;
}
if(charCapacity != 0 && charCount > 2*charCapacity) {
int cnt = 0;
for(int i = 0; i < buffer.size()-1; ++i) {
cnt += buffer.get(i).getText().length();
if(cnt > charCapacity) {
charCount -= charCapacity;
buffer.subList(0, i+1).clear();
wasOverflow = true;
break;
}
}
}
// System.out.println("Total msgs: " + buffer.size() + "charCount: " + charCount);
if(wasOverflow) informRefresh();
else informReceived(message);
}
/**
* {@inheritDoc}
*/
@Override
public void setMessageFilter(MessageFilter filter) {
this.msgFilter = filter;
}
/**
* {@inheritDoc}
*/
@Override
public MessageConsole prepare(String chunk, MessageType type)
throws IndexOutOfBoundsException {
msgPublisher.prepare(chunk, type);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public MessageConsole prepareAndPublish(String msg, MessageType type) {
messageReceived(new Message(msg, type));
return this;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized MessageConsole print(String s) {
if(!isMessagePending) beginMessage();
currMessage.append(s);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized boolean publish(MessageType type) {
return msgPublisher.publish(type);
}
/**
* {@inheritDoc}
*/
@Override
public void removeMessageHandler(MessageHandler handler) {
msgPublisher.addMessageHandler(handler);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void setCharCapacity(int maximumChars) {
this.charCapacity = maximumChars;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void setMessageCapacity(int maximumMessages) {
this.messageCapacity = maximumMessages;
}
/**
* {@inheritDoc}
*/
@Override
public void addMessageConsoleView(MessageConsoleView consoleView) {
views.add(consoleView);
}
/**
* {@inheritDoc}
*/
@Override
public void removeMessageConsoleView(MessageConsoleView consoleView) {
views.remove(consoleView);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized int getBufferSize(MessageType type) {
return -1; //FIXME
}
private synchronized void beginMessage() {
isMessagePending = true;
currMessage.delete(0, currMessage.length());
}
private synchronized void informRefresh() {
for(int i = 0; i < views.size(); ++i)
views.get(i).refresh(this);
}
private synchronized void informReceived(Message message) {
for(int i = 0; i < views.size(); ++i)
views.get(i).messageReceived(message);
}
private static class DefaultMessageFilter implements MessageFilter {
@Override
public boolean accept(Message message) {
return true;
}
}
}