/*
* $Id: DefaultZCharTranslator.java 536 2008-02-19 06:03:27Z weiju $
*
* Created on 2006/01/15
* Copyright 2005-2008 by Wei-ju Wu
* This file is part of The Z-machine Preservation Project (ZMPP).
*
* ZMPP is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZMPP 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ZMPP. If not, see <http://www.gnu.org/licenses/>.
*/
package org.zmpp.encoding;
import org.zmpp.encoding.AlphabetTable.Alphabet;
/**
* The default implementation of ZCharTranslator.
*
* @author Wei-ju Wu
* @version 1.0
*/
public class DefaultZCharTranslator implements Cloneable, ZCharTranslator {
private AlphabetTable alphabetTable;
private Alphabet currentAlphabet;
private Alphabet lockAlphabet;
private boolean shiftLock;
/**
* Constructor.
*
* @param alphabetTable the alphabet table
*/
public DefaultZCharTranslator(final AlphabetTable alphabetTable) {
this.alphabetTable = alphabetTable;
reset();
}
/**
* {@inheritDoc}
*/
public final void reset() {
currentAlphabet = Alphabet.A0;
lockAlphabet = null;
shiftLock = false;
}
public void resetToLastAlphabet() {
if (lockAlphabet == null) {
currentAlphabet = Alphabet.A0;
} else {
currentAlphabet = lockAlphabet;
shiftLock = true;
}
}
/**
* {@inheritDoc}
*/
public Object clone() throws CloneNotSupportedException {
DefaultZCharTranslator clone = null;
clone = (DefaultZCharTranslator) super.clone();
clone.reset();
return clone;
}
/**
* {@inheritDoc}
*/
public Alphabet getCurrentAlphabet() {
return currentAlphabet;
}
/**
* {@inheritDoc}
*/
public char translate(final char zchar) {
if (shift(zchar)) {
return '\0';
}
char result;
if (isInAlphabetRange(zchar)) {
switch (currentAlphabet) {
case A0:
result = (char) alphabetTable.getA0Char((byte) zchar);
break;
case A1:
result = (char) alphabetTable.getA1Char((byte) zchar);
break;
case A2:
default:
result = (char) alphabetTable.getA2Char((byte) zchar);
break;
}
} else {
result = '?';
}
// Only reset if the shift lock flag is not set
if (!shiftLock) {
resetToLastAlphabet();
}
return result;
}
/**
* {@inheritDoc}
*/
public boolean willEscapeA2(final char zchar) {
return currentAlphabet == Alphabet.A2 && zchar == AlphabetTable.A2_ESCAPE;
}
/**
* {@inheritDoc}
*/
public boolean isAbbreviation(final char zchar) {
return alphabetTable.isAbbreviation(zchar);
}
/**
* {@inheritDoc}
*/
public AlphabetElement getAlphabetElementFor(final char zsciiChar) {
// Special handling for newline !!
if (zsciiChar == '\n') {
return new AlphabetElement(Alphabet.A2, (short) 7);
}
Alphabet alphabet = null;
int zcharCode = alphabetTable.getA0CharCode(zsciiChar);
if (zcharCode >= 0) {
alphabet = Alphabet.A0;
} else {
zcharCode = alphabetTable.getA1CharCode(zsciiChar);
if (zcharCode >= 0) {
alphabet = Alphabet.A1;
} else {
zcharCode = alphabetTable.getA2CharCode(zsciiChar);
if (zcharCode >= 0) {
alphabet = Alphabet.A2;
}
}
}
if (alphabet == null) {
// It is not in any alphabet table, we are fine with taking the code
// number for the moment
zcharCode = zsciiChar;
}
return new AlphabetElement(alphabet, (short) zcharCode);
}
/**
* Determines if the given byte value falls within the alphabet range.
*
* @param zchar the zchar value
* @return true if the value is in the alphabet range, false, otherwise
*/
private static boolean isInAlphabetRange(final char zchar) {
return 0 <= zchar && zchar <= AlphabetTable.ALPHABET_END;
}
/**
* Performs a shift.
*
* @param zchar a z encoded character
* @return true if a shift was performed, false, otherwise
*/
private boolean shift(final char zchar) {
if (alphabetTable.isShift(zchar)) {
currentAlphabet = shiftFrom(currentAlphabet, zchar);
// Sets the current lock alphabet
if (alphabetTable.isShiftLock(zchar)) {
lockAlphabet = currentAlphabet;
}
return true;
}
return false;
}
/**
* This method contains the rules to shift the alphabets.
*
* @param alphabet the source alphabet
* @param shiftChar the shift character
* @return the resulting alphabet
*/
private Alphabet shiftFrom(final Alphabet alphabet, final char shiftChar) {
Alphabet result = null;
if (alphabetTable.isShift1(shiftChar)) {
if (alphabet == Alphabet.A0) {
result = Alphabet.A1;
} else if (alphabet == Alphabet.A1) {
result = Alphabet.A2;
} else if (alphabet == Alphabet.A2) {
result = Alphabet.A0;
}
} else if (alphabetTable.isShift2(shiftChar)) {
if (alphabet == Alphabet.A0) {
result = Alphabet.A2;
} else if (alphabet == Alphabet.A1) {
result = Alphabet.A0;
} else if (alphabet == Alphabet.A2) {
result = Alphabet.A1;
}
} else {
result = alphabet;
}
shiftLock = alphabetTable.isShiftLock(shiftChar);
return result;
}
}