package net.zuckerfrei.jcfd;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* DOCUMENT ME!
*
* @author Davor Cengija
* @version $Revision: 1.1.1.1 $
*/
public abstract class DictImpl
implements Dict
{
//~ Static variables/initializers =========================================
static Log log = LogFactory.getLog(DictImpl.class);
/** DOCUMENT ME! */
private static String host;
/** DOCUMENT ME! */
private static int port;
static DatabaseList dbList = new DatabaseList();
static StrategyList strList = new StrategyList();
//~ Instance variables ====================================================
/** DOCUMENT ME! */
protected boolean connected = false;
/** DOCUMENT ME! */
private Socket socket;
/** DOCUMENT ME! */
protected DataOutputStream dos;
/** DOCUMENT ME! */
BufferedReader br;
//~ Constructors ==========================================================
/**
* Constructor.
*
* @param host
* @param port
*
* @throws DictException DOCUMENT ME!
*/
public DictImpl(String host, int port)
throws DictException
{
this.host = host;
this.port = port;
connect();
}
/**
* Creates a new DictImpl object.
*/
protected DictImpl() {
}
//~ Methods ===============================================================
/**
* @see net.zuckerfrei.jcfd.Dict#close()
*/
public void close()
throws DictException
{
try {
writeCommand(Command.QUIT);
socket.close();
connected = false;
}
catch (IOException e) {
;
}
}
/**
* @see net.zuckerfrei.jcfd.Dict#define(String)
*/
public DefinitionList define(String word)
throws DictException
{
return define(word, Database.ANY);
}
/**
* @see net.zuckerfrei.jcfd.Dict#define(String, boolean)
*/
public DefinitionList define(String word, boolean firstOnly)
throws DictException
{
if (firstOnly) {
return define(word, Database.FIRST);
}
else {
return define(word, Database.ANY);
}
}
/**
* @see net.zuckerfrei.jcfd.Dict#define(String, Database)
*/
public DefinitionList define(String word, Database database)
throws DictException
{
if (!connected) {
throw new IllegalStateException("Client not connected!");
}
DefinitionList list;
try {
writeCommand(Command.define(word,
database.getCode()));
String result = readResponse();
list = createDefinitionList();
if (Response.noMatch(result)) {
log.debug("No match: " + result);
return list;
}
if (Response.invalidDatabase(result)) {
throw new InvalidDatabaseException("Invalid database: " + result);
}
while (Response.isOk(result)) {
if (log.isDebugEnabled()) {
log.debug("Response: " + result);
}
// how many?
result = readResponse();
// ovo je celavo, kako znam da cu dobiti bas dobiti
// 150 response
if (Response.definitionsCountFollows(result)) {
int definitionsCount = Response.findCount(result);
if (log.isDebugEnabled()) {
log.debug("Nasao " + definitionsCount + " definicija: " + result);
}
for (int i = 0; i < definitionsCount; i++) {
result = readResponse();
String[] split = Response.splitQuotedString(result, '"');
Definition definition = DefinitionFactory.getInstance()
.createDefinition(split[1],
DatabaseList.findDatabase(split[2]),
readBody());
list.addDefinition(definition);
}
}
// result = readResponse();
}
}
catch (IOException ioe) {
throw new DictException("Error while querying for definition", ioe);
}
return list;
}
/**
* @see net.zuckerfrei.jcfd.Dict#define(Match)
*/
public DefinitionList define(Match match)
throws DictException
{
return define(match.getWord(), match.getDatabase());
}
/**
* @see net.zuckerfrei.jcfd.Dict#define(MatchList)
*/
public DefinitionList define(MatchList matchList)
throws DictException
{
matchList.goBeforeFirst();
DefinitionList defList = new DefinitionList();
Match match;
while(matchList.hasNext()) {
match = matchList.next();
defList.addDefinitionList(define(match.getWord(), match.getDatabase()));
}
return defList;
}
/**
* @see net.zuckerfrei.jcfd.Dict#listDatabases()
*/
public DatabaseList listDatabases() {
return dbList;
}
/**
* @see net.zuckerfrei.jcfd.Dict#listStrategies()
*/
public StrategyList listStrategies() {
return strList;
}
/**
* @see net.zuckerfrei.jcfd.Dict#match(String, Database)
*/
public MatchList match(String word, Database database)
throws DictException
{
return match(word, Strategy.DEFAULT, database);
}
/**
* @see net.zuckerfrei.jcfd.Dict#match(String, Strategy)
*/
public MatchList match(String word, Strategy strategy)
throws DictException
{
return match(word, strategy, Database.ANY);
}
/**
* @see net.zuckerfrei.jcfd.Dict#match(String)
*/
public MatchList match(String word)
throws DictException
{
return match(word, Strategy.DEFAULT, Database.ANY);
}
/**
* @see net.zuckerfrei.jcfd.Dict#match(String, boolean)
*/
public MatchList match(String word, boolean firstDbOnly)
throws DictException
{
if (firstDbOnly) {
return match(word, Database.FIRST);
}
else {
return match(word);
}
}
/**
* @see net.zuckerfrei.jcfd.Dict#match(String, Strategy, boolean)
*/
public MatchList match(String word, Strategy strategy, boolean firstDbOnly)
throws DictException
{
if (firstDbOnly) {
return match(word, strategy, Database.FIRST);
}
else {
return match(word, strategy);
}
}
/**
* @see net.zuckerfrei.jcfd.Dict#match(String, Strategy, Database)
*/
public MatchList match(String word, Strategy strategy, Database database)
throws DictException
{
if (!connected) {
throw new IllegalStateException("Client not connected");
}
MatchList list;
try {
writeCommand(Command.match(word,
strategy.getCode(),
database.getCode()));
String result = readResponse();
list = createMatchList();
if (Response.isOk(result)) {
result = readResponse();
if (Response.noMatch(result)) {
log.debug("No match: " + result);
return list;
}
if (Response.invalidDatabase(result)) {
throw new InvalidDatabaseException("Invalid database: " + result);
}
if (Response.invalidStrategy(result)) {
throw new InvalidStrategyException("Invalid strategy: " + result);
}
int matchesCount = Response.findCount(result);
for (int i = 0; i < matchesCount; i++) {
result = readResponse();
String[] split = StringUtils.split(result, " ", 2);
Match match = new Match(DatabaseList.findDatabase(split[0]), StringUtils.strip(split[1], "\""));
list.addMatch(match);
}
flushResponse();
}
else {
throw new DictException("Unexpected code: " + result);
}
}
catch (IOException ioe) {
throw new DictException(ioe.getMessage(), ioe);
}
return list;
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
protected DefinitionList createDefinitionList() {
return new DefinitionList();
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
protected MatchList createMatchList() {
return new MatchList();
}
/**
* Fills the list of <code>Database</code>s available on this DICT server.
*
* @throws DictException
* @throws IOException
* @throws InvalidResponseException DOCUMENT ME!
* @throws NoDatabasesException DOCUMENT ME!
*/
protected void fillDatabaseList()
throws DictException,
IOException
{
writeCommand(Command.SHOW_DB);
String result = readResponse();
// This part is confusing, RFC doesn't specify 250 ok response,
// while dictd 1.5.5/rf on Linux 2.4.18-6mdk from Mandrake 8.1
// distribution sends that response code.
if (!Response.isOk(result)) {
throw new InvalidResponseException("Unexpected response, expecting 250 ok, got " + result);
}
result = readResponse();
if (Response.noDatabases(result)) {
throw new NoDatabasesException("No databases found. Check your server configuration! Server response: " + result);
}
int dbCount = Response.findCount(result);
for (int i = 0; i < dbCount; i++) {
result = readResponse();
String[] split = StringUtils.split(result, " ", 2);
String dbCode = split[0];
String dbName = StringUtils.strip(split[1], "\"");
Database db = new Database(dbCode, dbName);
dbList.addDatabase(db);
}
flushResponse();
}
/**
* DOCUMENT ME!
*
* @throws DictException DOCUMENT ME!
* @throws IOException DOCUMENT ME!
* @throws InvalidResponseException DOCUMENT ME!
* @throws NoStrategiesException DOCUMENT ME!
*/
protected void fillStrategyList()
throws DictException,
IOException
{
writeCommand(Command.SHOW_STRAT);
String result = readResponse();
// This part is confusing, RFC doesn't specify 250 ok response,
// while dictd 1.5.5/rf on Linux 2.4.18-6mdk from Mandrake 8.1
// distribution sends that response code.
if (!Response.isOk(result)) {
throw new InvalidResponseException("Unexpected response, expecting 250 ok, got " + result);
}
result = readResponse();
// maybe we could fall back on default strategy?
if (Response.noStrategies(result)) {
throw new NoStrategiesException("No strategies found. Check your server configuration! Server response: " + result);
}
int dbCount = Response.findCount(result);
for (int i = 0; i < dbCount; i++) {
result = readResponse();
String[] split = StringUtils.split(result, " ", 2);
String strCode = split[0];
String strName = StringUtils.strip(split[1], "\"");
Strategy strategy = new Strategy(strCode, strName);
strList.addStrategy(strategy);
}
flushResponse();
}
/**
* Flushes the response buffer preparing it for the next read. Every DICT
* reponse ends with "<CR><LF>.<CR><LF>". This
* method simply traverses through the reponse while the dot is
* encountered.
*
* @throws IOException when error while sending command is encountered.
*/
protected void flushResponse()
throws IOException
{
String result = readResponse();
while (!result.equals(DEFINITION_END)) {
result = readResponse();
}
}
/**
* Send an identification string to DICT server. This string is not
* mandatory but it's nice to introduce yourself when connected.
*
* @return DOCUMENT ME!
*
* @throws DictException DOCUMENT ME!
*/
protected boolean identify()
throws DictException
{
try {
writeCommand(Command.CLIENT);
}
catch (IOException ioe) {
throw new DictException("Identification failed", ioe);
}
return true;
}
/**
* Method <code>readBody</code> return <code>String</code> representing
* definition body for the requested word. Override this method if you
* want specially formatted body (e.g. XML formatted links etc.)
*
* @return String
*
* @throws IOException when error while reading is encountered.
*/
protected String readBody()
throws IOException
{
String result = readResponse();
StringBuffer buff = new StringBuffer(result);
buff.append(DEFINITION_LINE_SEPARATOR);
while (!result.equals(DEFINITION_END)) {
result = readResponse();
buff.append(result)
.append(DEFINITION_LINE_SEPARATOR);
if (log.isDebugEnabled()) {
log.debug("readBody response: " + result);
}
}
return buff.toString();
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws IOException DOCUMENT ME!
*/
protected String readResponse()
throws IOException
{
return br.readLine();
}
/**
* Sends a string representing command to DICT server. This method
* properly ends command with CRLF. Before that it firstly trims all
* whitespaces from the beggining and the end of the command string.
*
* @param command Commant to be sent to DICT server.
*
* @throws IOException when error while sending command is encountered.
* @throws IllegalArgumentException if <code>command</code> is null
*/
protected final void writeCommand(String command)
throws IOException
{
if (command == null) {
throw new IllegalArgumentException("Parametar cannot be null");
}
dos.writeBytes(command.trim() + COMMAND_LINE_END);
}
/**
* Connects to DICT server. Uses host and port defined in the constructor.
*
* @throws DictException
*/
private void connect()
throws DictException
{
try {
socket = new Socket(host, port);
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
dos = new DataOutputStream(socket.getOutputStream());
identify();
if (!Response.isConnected(readResponse())) {
throw new DictException("Invalid server response");
}
connected = true;
fillDatabaseList();
fillStrategyList();
}
catch (UnknownHostException e) {
throw new DictException(e.getMessage(), e);
}
catch (IOException e) {
throw new DictException(e.getMessage(), e);
}
}
}