package iaik.pkcs.pkcs11;

import iaik.pkcs.pkcs11.wrapper.CK_TOKEN_INFO;
import iaik.pkcs.pkcs11.wrapper.Constants;
import iaik.pkcs.pkcs11.wrapper.PKCS11Constants;

import java.util.Date;

/**
 * Objects of this class provide information about a token. Serial number,
 * manufacturer, free memory,... . Notice that this is just a snapshot of the
 * token's status at the time this object was created.
 *
 * @author <a href="mailto:Karl.Scheibelhofer@iaik.at"> Karl Scheibelhofer </a>
 * @version 1.0
 * @invariants (label_ <> null)
 *             and (manufacturerID_ <> null)
 *             and (model_ <> null)
 *             and (serialNumber_ <> null)
 *             and (hardwareVersion_ <> null)
 *             and (firmwareVersion_ <> null)
 *             and (time_ <> null)
 */
public class TokenInfo implements Cloneable {

	/**
	 * This is the value which can be used for ulMaxSessionCount and
	 * ulMaxRwSessionCount to express an infinite number.
	 */
	public static final long EFFECTIVELY_INFINITE = PKCS11Constants.CK_EFFECTIVELY_INFINITE;

	/**
	 * This is the value which can be used for ulMaxSessionCount, ulSessionCount,
	 * ulMaxRwSessionCount, ulRwSessionCount, ulTotalPublicMemory,
	 * ulFreePublicMemory, ulTotalPrivateMemory, and ulFreePrivateMemory to signal
	 * that the information is unavailable.
	 */
	public static final long UNAVAILABLE_INFORMATION = PKCS11Constants.CK_UNAVAILABLE_INFORMATION;

	/**
	 * The label of this token.
	 */
	protected String label_;

	/**
	 * The identifier of the manufacturer of this token.
	 */
	protected String manufacturerID_;

	/**
	 * The model of this token.
	 */
	protected String model_;

	/**
	 * The serial number of this token.
	 */
	protected String serialNumber_;

	/**
	 * The maximum number of concurrent (open) sessions.
	 */
	protected long maxSessionCount_;

	/**
	 * The current number of open sessions.
	 */
	protected long sessionCount_;

	/**
	 * Maximum number of concurrent (open) read-write sessions.
	 */
	protected long maxRwSessionCount_;

	/**
	 * The current number of open read-write sessions.
	 */
	protected long rwSessionCount_;

	/**
	 * The maximum PIN length that this token allows.
	 */
	protected long maxPinLen_;

	/**
	 * The minimum PIN length that this token allows.
	 */
	protected long minPinLen_;

	/**
	 * The total amount of memory for public objects on this token.
	 */
	protected long totalPublicMemory_;

	/**
	 * The amount of free memory for public objects on this token.
	 */
	protected long freePublicMemory_;

	/**
	 * The total amount of memory for private objects on this token.
	 */
	protected long totalPrivateMemory_;

	/**
	 * The amount of free memory for private objects on this token.
	 */
	protected long freePrivateMemory_;

	/**
	 * The version of the hardware of this token.
	 */
	protected Version hardwareVersion_;

	/**
	 * The version of the firmware of this token.
	 */
	protected Version firmwareVersion_;

	/**
	 * The current time on the token. This value only makes sense, if the token
	 * contains a clock.
	 */
	protected Date time_;

	/**
	 * True, if the token has a random numebr generator.
	 */
	protected boolean rng_;

	/**
	 * True, if the token is write protected.
	 */
	protected boolean writeProtected_;

	/**
	 * True, if the token requires the user to login to perform certain
	 * operations.
	 */
	protected boolean loginRequired_;

	/**
	 * True, if the user-PIN is already initialized.
	 */
	protected boolean userPinInitialized_;

	/**
	 * True, if a successful save of a sessions cryptographic operations state
	 * always contains all keys needed to restore the state of the session.
	 */
	protected boolean restoreKeyNotNeeded_;

	/**
	 * True, if the token has a clock.
	 */
	protected boolean clockOnToken_;

	/**
	 * True, if there are different means to authenticate the user than passing
	 * the user-PIN to a login operation.
	 */
	protected boolean protectedAuthenticationPath_;

	/**
	 * True, if the token supports dual crypto operations.
	 */
	protected boolean dualCryptoOperations_;

	/**
	 * True, if the token is already initialized.
	 */
	protected boolean tokenInitialized_;

	/**
	 * True, if the token supports secondary authentication for private key
	 * objects.
	 */
	protected boolean secondaryAuthentication_;

	/**
	 * True, if the user-PIN has been entered incorrectly at least once since the
	 * last successful authentication.
	 */
	protected boolean userPinCountLow_;

	/**
	 * True, if the user has just one try left to supply the correct PIN before
	 * the user-PIN gets locked.
	 */
	protected boolean userPinFinalTry_;

	/**
	 * True, if the user-PIN is locked.
	 */
	protected boolean userPinLocked_;

	/**
	 * True, if the user PIN value is the default value set by token initialization
	 * or manufacturing.
	 */
	protected boolean userPinToBeChanged_;

	/**
	 * True, if the security officer-PIN has been entered incorrectly at least
	 * once since the last successful authentication.
	 */
	protected boolean soPinCountLow_;

	/**
	 * True, if the security officer has just one try left to supply the correct
	 * PIN before the security officer-PIN gets locked.
	 */
	protected boolean soPinFinalTry_;

	/**
	 * True, if the security officer-PIN is locked.
	 */
	protected boolean soPinLocked_;

	/**
	 * True, if the security officer-PIN value is the default value set by token
	 * initialization or manufacturing.
	 */
	protected boolean soPinToBeChanged_;

	/**
	 * Constructor taking CK_TOKEN_INFO as given returned by
	 * PKCS11.C_GetTokenInfo.
	 *
	 * @param ckTokenInfo The CK_TOKEN_INFO object as returned by
	 *                    PKCS11.C_GetTokenInfo.
	 * @preconditions (ckTokenInfo <> null)
	 * @postconditions
	 */
	protected TokenInfo(CK_TOKEN_INFO ckTokenInfo) {
		if (ckTokenInfo == null) {
			throw new NullPointerException("Argument \"ckTokenInfo\" must not be null.");
		}
		label_ = new String(ckTokenInfo.label);
		manufacturerID_ = new String(ckTokenInfo.manufacturerID);
		model_ = new String(ckTokenInfo.model);
		serialNumber_ = new String(ckTokenInfo.serialNumber);
		maxSessionCount_ = ckTokenInfo.ulMaxSessionCount;
		sessionCount_ = ckTokenInfo.ulSessionCount;
		maxRwSessionCount_ = ckTokenInfo.ulMaxRwSessionCount;
		rwSessionCount_ = ckTokenInfo.ulRwSessionCount;
		maxPinLen_ = ckTokenInfo.ulMaxPinLen;
		minPinLen_ = ckTokenInfo.ulMinPinLen;
		totalPublicMemory_ = ckTokenInfo.ulTotalPublicMemory;
		freePublicMemory_ = ckTokenInfo.ulFreePublicMemory;
		totalPrivateMemory_ = ckTokenInfo.ulTotalPrivateMemory;
		freePrivateMemory_ = ckTokenInfo.ulFreePrivateMemory;
		hardwareVersion_ = new Version(ckTokenInfo.hardwareVersion);
		firmwareVersion_ = new Version(ckTokenInfo.firmwareVersion);
		time_ = Util.parseTime(ckTokenInfo.utcTime);
		rng_ = (ckTokenInfo.flags & PKCS11Constants.CKF_RNG) != 0L;
		writeProtected_ = (ckTokenInfo.flags & PKCS11Constants.CKF_WRITE_PROTECTED) != 0L;
		loginRequired_ = (ckTokenInfo.flags & PKCS11Constants.CKF_LOGIN_REQUIRED) != 0L;
		userPinInitialized_ = (ckTokenInfo.flags & PKCS11Constants.CKF_USER_PIN_INITIALIZED) != 0L;
		restoreKeyNotNeeded_ = (ckTokenInfo.flags & PKCS11Constants.CKF_RESTORE_KEY_NOT_NEEDED) != 0L;
		clockOnToken_ = (ckTokenInfo.flags & PKCS11Constants.CKF_CLOCK_ON_TOKEN) != 0L;
		protectedAuthenticationPath_ = (ckTokenInfo.flags & PKCS11Constants.CKF_PROTECTED_AUTHENTICATION_PATH) != 0L;
		dualCryptoOperations_ = (ckTokenInfo.flags & PKCS11Constants.CKF_DUAL_CRYPTO_OPERATIONS) != 0L;
		tokenInitialized_ = (ckTokenInfo.flags & PKCS11Constants.CKF_TOKEN_INITIALIZED) != 0L;
		secondaryAuthentication_ = (ckTokenInfo.flags & PKCS11Constants.CKF_SECONDARY_AUTHENTICATION) != 0L;
		userPinCountLow_ = (ckTokenInfo.flags & PKCS11Constants.CKF_USER_PIN_COUNT_LOW) != 0L;
		userPinFinalTry_ = (ckTokenInfo.flags & PKCS11Constants.CKF_USER_PIN_FINAL_TRY) != 0L;
		userPinLocked_ = (ckTokenInfo.flags & PKCS11Constants.CKF_USER_PIN_LOCKED) != 0L;
		userPinToBeChanged_ = (ckTokenInfo.flags & PKCS11Constants.CKF_USER_PIN_TO_BE_CHANGED) != 0L;
		soPinCountLow_ = (ckTokenInfo.flags & PKCS11Constants.CKF_SO_PIN_COUNT_LOW) != 0L;
		soPinFinalTry_ = (ckTokenInfo.flags & PKCS11Constants.CKF_SO_PIN_FINAL_TRY) != 0L;
		soPinLocked_ = (ckTokenInfo.flags & PKCS11Constants.CKF_SO_PIN_LOCKED) != 0L;
		soPinToBeChanged_ = (ckTokenInfo.flags & PKCS11Constants.CKF_SO_PIN_TO_BE_CHANGED) != 0L;
	}

	/**
	 * Create a (deep) clone of this object.
	 *
	 * @return A clone of this object.
	 * @preconditions
	 * @postconditions (result <> null)
	 *                 and (result instanceof TokenInfo)
	 *                 and (result.equals(this))
	 */
	public java.lang.Object clone() {
		TokenInfo clone;

		try {
			clone = (TokenInfo) super.clone();

			clone.hardwareVersion_ = (Version) this.hardwareVersion_.clone();
			clone.firmwareVersion_ = (Version) this.firmwareVersion_.clone();
			clone.time_ = new Date(this.time_.getTime()); // clone() unsupported in JDK 1.1
		} catch (CloneNotSupportedException ex) {
			// this must not happen, because this class is cloneable
			throw new TokenRuntimeException("An unexpected clone exception occurred.", ex);
		}

		return clone;
	}

	/**
	 * Get the label of this token.
	 *
	 * @return The label of this token.
	 * @preconditions
	 * @postconditions (result <> null)
	 */
	public String getLabel() {
		return label_;
	}

	/**
	 * Get the manufacturer identifier.
	 *
	 * @return A string identifying the manufacturer of this token.
	 * @preconditions
	 * @postconditions (result <> null)
	 */
	public String getManufacturerID() {
		return manufacturerID_;
	}

	/**
	 * Get the model of this token.
	 *
	 * @return A string specifying the model of this token.
	 * @preconditions
	 * @postconditions (result <> null)
	 */
	public String getModel() {
		return model_;
	}

	/**
	 * Get the serial number of this token.
	 *
	 * @return A string holding the serial number of this token.
	 * @preconditions
	 * @postconditions (result <> null)
	 */
	public String getSerialNumber() {
		return serialNumber_;
	}

	/**
	 * Get the maximum allowed number of (open) concurrent sessions.
	 *
	 * @return The maximum allowed number of (open) concurrent sessions.
	 * @preconditions
	 * @postconditions
	 */
	public long getMaxSessionCount() {
		return maxSessionCount_;
	}

	/**
	 * Get the current number of open sessions.
	 *
	 * @return The current number of open sessions.
	 * @preconditions
	 * @postconditions
	 */
	public long getSessionCount() {
		return sessionCount_;
	}

	/**
	 * Get the maximum allowed number of (open) concurrent read-write sessions.
	 *
	 * @return The maximum allowed number of (open) concurrent read-write
	 *         sessions.
	 * @preconditions
	 * @postconditions
	 */
	public long getMaxRwSessionCount() {
		return maxRwSessionCount_;
	}

	/**
	 * Get the current number of open read-write sessions.
	 *
	 * @return The current number of open read-write sessions.
	 * @preconditions
	 * @postconditions
	 */
	public long getRwSessionCount() {
		return rwSessionCount_;
	}

	/**
	 * Get the maximum length for the PIN.
	 *
	 * @return The maximum length for the PIN.
	 * @preconditions
	 * @postconditions
	 */
	public long getMaxPinLen() {
		return maxPinLen_;
	}

	/**
	 * Get the minimum length for the PIN.
	 *
	 * @return The minimum length for the PIN.
	 * @preconditions
	 * @postconditions
	 */
	public long getMinPinLen() {
		return minPinLen_;
	}

	/**
	 * Get the total amount of memory for public objects.
	 *
	 * @return The total amount of memory for public objects.
	 * @preconditions
	 * @postconditions
	 */
	public long getTotalPublicMemory() {
		return totalPublicMemory_;
	}

	/**
	 * Get the amount of free memory for public objects.
	 *
	 * @return The amount of free memory for public objects.
	 * @preconditions
	 * @postconditions
	 */
	public long getFreePublicMemory() {
		return freePublicMemory_;
	}

	/**
	 * Get the total amount of memory for private objects.
	 *
	 * @return The total amount of memory for private objects.
	 * @preconditions
	 * @postconditions
	 */
	public long getTotalPrivateMemory() {
		return totalPrivateMemory_;
	}

	/**
	 * Get the amount of free memory for private objects.
	 *
	 * @return The amount of free memory for private objects.
	 * @preconditions
	 * @postconditions
	 */
	public long getFreePrivateMemory() {
		return freePrivateMemory_;
	}

	/**
	 * Get the version of the token's hardware.
	 *
	 * @return The version of the token's hardware.
	 * @preconditions
	 * @postconditions (result <> null)
	 */
	public Version getHardwareVersion() {
		return hardwareVersion_;
	}

	/**
	 * Get the version of the token's firmware.
	 *
	 * @return The version of the token's firmware.
	 * @preconditions
	 * @postconditions (result <> null)
	 */
	public Version getFirmwareVersion() {
		return firmwareVersion_;
	}

	/**
	 * Get the current time of the token's clock. This value does only make sense
	 * if the token has a clock. Remind that, this is the time this object was
	 * created and not the time the application called this method.
	 *
	 * @return The current time on the token's clock.
	 * @see #isClockOnToken()
	 * @preconditions
	 * @postconditions (result <> null)
	 */
	public Date getTime() {
		return time_;
	}

	/**
	 * Check, if the token has a random number generator.
	 *
	 * @return True, if the token has a random number generator. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isRNG() {
		return rng_;
	}

	/**
	 * Check, if the token is write protected.
	 *
	 * @return True, if the token is write protected. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isWriteProtected() {
		return writeProtected_;
	}

	/**
	 * Check, if the token requires the user to log in before certain operations
	 * can be performed.
	 *
	 * @return True, if the token requires the user to log in before certain
	 *         operations can be performed. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isLoginRequired() {
		return loginRequired_;
	}

	/**
	 * Check, if the user-PIN is already initialized.
	 *
	 * @return True, if the user-PIN is already initialized. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isUserPinInitialized() {
		return userPinInitialized_;
	}

	/**
	 * Check, if a successful save of a sessions cryptographic operations
	 * state always contains all keys needed to restore the state of the
	 * session.
	 *
	 * @return True, if a successful save of a sessions cryptographic operations
	 *         state always contains all keys needed to restore the state of the
	 *         session. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isRestoreKeyNotNeeded() {
		return restoreKeyNotNeeded_;
	}

	/**
	 * Check, if the token has an own clock.
	 *
	 * @return True, if the token has its own clock. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isClockOnToken() {
		return clockOnToken_;
	}

	/**
	 * Check, if the token has an protected authentication path. This means that
	 * a user may log in without providing a PIN to the login method, because the
	 * token has other means to authenticate the user; e.g. a PIN-pad on the
	 * reader or some biometric authentication.
	 *
	 * @return True, if the token has an protected authentication path. False,
	 *         otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isProtectedAuthenticationPath() {
		return protectedAuthenticationPath_;
	}

	/**
	 * Check, if the token supports dual crypto operations.
	 *
	 * @return True, if the token supports dual crypto operations. False,
	 *         otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isDualCryptoOperations() {
		return dualCryptoOperations_;
	}

	/**
	 * Check, if the token is already initialized.
	 *
	 * @return True, if the token is already initialized. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isTokenInitialized() {
		return tokenInitialized_;
	}

	/**
	 * Check, if the token supports secondary authentication for private key
	 * objects.
	 *
	 * @return True, if the token supports secondary authentication. False,
	 *         otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isSecondaryAuthentication() {
		return secondaryAuthentication_;
	}

	/**
	 * Check, if the user-PIN has been entered incorrectly at least once since the
	 * last successful authentication.
	 *
	 * @return True, if the the user-PIN has been entered incorrectly at least one
	 *         since the last successful authentication. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isUserPinCountLow() {
		return userPinCountLow_;
	}

	/**
	 * Check, if the user has just one try left to supply the correct PIN before
	 * the user-PIN gets locked.
	 *
	 * @return True, if the user has just one try left to supply the correct PIN
	 *         before the user-PIN gets locked. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isUserPinFinalTry() {
		return userPinFinalTry_;
	}

	/**
	 * Check, if the user-PIN is locked.
	 *
	 * @return True, if the user-PIN is locked. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isUserPinLocked() {
		return userPinLocked_;
	}

	/**
	 * Check, if the user PIN value is the default value set by token
	 * initialization or manufacturing.
	 *
	 * @return True, if the user PIN value is the default value set by token
	 *         initialization or manufacturing. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isUserPinToBeChanged() {
		return userPinToBeChanged_;
	}

	/**
	 * Check, if the security officer-PIN has been entered incorrectly at least
	 * once since the last successful authentication.
	 *
	 * @return True, if the the security officer-PIN has been entered incorrectly
	 *         at least one since the last successful authentication. False,
	 *         otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isSoPinCountLow() {
		return soPinCountLow_;
	}

	/**
	 * Check, if the security officer has just one try left to supply the correct
	 * PIN before the security officer-PIN gets locked.
	 *
	 * @return True, if the security officer has just one try left to supply the
	 *         correct PIN before the security officer-PIN gets locked. False,
	 *         otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isSoPinFinalTry() {
		return soPinFinalTry_;
	}

	/**
	 * Check, if the security officer-PIN is locked.
	 *
	 * @return True, if the security officer-PIN is locked. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isSoPinLocked() {
		return soPinLocked_;
	}

	/**
	 * Check, if the security officer PIN value is the default value set by token
	 * initialization or manufacturing.
	 *
	 * @return True, if the security officer PIN value is the default value set by
	 *         token initialization or manufacturing. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isSoPinToBeChanged() {
		return soPinToBeChanged_;
	}

	/**
	 * Returns the string representation of this object.
	 *
	 * @return the string representation of object
	 */
	public String toString() {
		StringBuffer buffer = new StringBuffer();

		buffer.append("Label: ");
		buffer.append(label_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Manufacturer ID: ");
		buffer.append(manufacturerID_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Model: ");
		buffer.append(model_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Serial Number: ");
		buffer.append(serialNumber_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Random Number Generator: ");
		buffer.append(rng_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Write protected: ");
		buffer.append(writeProtected_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Login required: ");
		buffer.append(loginRequired_);

		buffer.append(Constants.NEWLINE);
		buffer.append("User PIN initialized: ");
		buffer.append(userPinInitialized_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Restore Key not needed: ");
		buffer.append(restoreKeyNotNeeded_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Clock on Token: ");
		buffer.append(clockOnToken_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Protected Authentication Path: ");
		buffer.append(protectedAuthenticationPath_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Dual Crypto Operations: ");
		buffer.append(dualCryptoOperations_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Token initialized: ");
		buffer.append(tokenInitialized_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Secondary Authentication: ");
		buffer.append(secondaryAuthentication_);

		buffer.append(Constants.NEWLINE);
		buffer.append("User PIN-Count low: ");
		buffer.append(userPinCountLow_);

		buffer.append(Constants.NEWLINE);
		buffer.append("User PIN final Try: ");
		buffer.append(userPinFinalTry_);

		buffer.append(Constants.NEWLINE);
		buffer.append("User PIN locked: ");
		buffer.append(userPinLocked_);

		buffer.append(Constants.NEWLINE);
		buffer.append("User PIN to be changed: ");
		buffer.append(userPinToBeChanged_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Security Officer PIN-Count low: ");
		buffer.append(soPinCountLow_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Security Officer PIN final Try: ");
		buffer.append(soPinFinalTry_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Security Officer PIN locked: ");
		buffer.append(soPinLocked_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Security Officer PIN to be changed: ");
		buffer.append(soPinToBeChanged_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Maximum Session Count: ");
		buffer
		    .append((maxSessionCount_ == UNAVAILABLE_INFORMATION) ? "<Information unavailable>"
		        : (maxSessionCount_ == EFFECTIVELY_INFINITE) ? "<effectively infinite>"
		            : Long.toString(maxSessionCount_));

		buffer.append(Constants.NEWLINE);
		buffer.append("Session Count: ");
		buffer
		    .append((sessionCount_ == UNAVAILABLE_INFORMATION) ? "<Information unavailable>"
		        : Long.toString(sessionCount_));

		buffer.append(Constants.NEWLINE);
		buffer.append("Maximum Read/Write Session Count: ");
		buffer
		    .append((maxRwSessionCount_ == UNAVAILABLE_INFORMATION) ? "<Information unavailable>"
		        : (maxRwSessionCount_ == EFFECTIVELY_INFINITE) ? "<effectively infinite>"
		            : Long.toString(maxRwSessionCount_));

		buffer.append(Constants.NEWLINE);
		buffer.append("Read/Write Session Count: ");
		buffer
		    .append((rwSessionCount_ == UNAVAILABLE_INFORMATION) ? "<Information unavailable>"
		        : Long.toString(rwSessionCount_));

		buffer.append(Constants.NEWLINE);
		buffer.append("Maximum PIN Length: ");
		buffer.append(maxPinLen_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Minimum PIN Length: ");
		buffer.append(minPinLen_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Total Public Memory: ");
		buffer
		    .append((totalPublicMemory_ == UNAVAILABLE_INFORMATION) ? "<Information unavailable>"
		        : Long.toString(totalPublicMemory_));

		buffer.append(Constants.NEWLINE);
		buffer.append("Free Public Memory: ");
		buffer
		    .append((freePublicMemory_ == UNAVAILABLE_INFORMATION) ? "<Information unavailable>"
		        : Long.toString(freePublicMemory_));

		buffer.append(Constants.NEWLINE);
		buffer.append("Total Private Memory: ");
		buffer
		    .append((totalPrivateMemory_ == UNAVAILABLE_INFORMATION) ? "<Information unavailable>"
		        : Long.toString(totalPrivateMemory_));

		buffer.append(Constants.NEWLINE);
		buffer.append("Free Private Memory: ");
		buffer
		    .append((freePrivateMemory_ == UNAVAILABLE_INFORMATION) ? "<Information unavailable>"
		        : Long.toString(freePrivateMemory_));

		buffer.append(Constants.NEWLINE);
		buffer.append("Hardware Version: ");
		buffer.append(hardwareVersion_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Firmware Version: ");
		buffer.append(firmwareVersion_);

		buffer.append(Constants.NEWLINE);
		buffer.append("Time: ");
		buffer.append(time_);

		return buffer.toString();
	}

	/**
	 * Compares all member variables of this object with the other object.
	 * Returns only true, if all are equal in both objects.
	 *
	 * @param otherObject The other TokenInfo object.
	 * @return True, if other is an instance of Info and all member variables of
	 *         both objects are equal. False, otherwise.
	 * @preconditions
	 * @postconditions
	 */
	public boolean equals(java.lang.Object otherObject) {
		boolean equal = false;

		if (otherObject instanceof TokenInfo) {
			TokenInfo other = (TokenInfo) otherObject;
			equal = (this == other)
			    || (this.label_.equals(other.label_)
			        && this.manufacturerID_.equals(other.manufacturerID_)
			        && this.model_.equals(other.model_)
			        && this.serialNumber_.equals(other.serialNumber_)
			        && (this.maxSessionCount_ == other.maxSessionCount_)
			        && (this.sessionCount_ == other.sessionCount_)
			        && (this.maxRwSessionCount_ == other.maxRwSessionCount_)
			        && (this.rwSessionCount_ == other.rwSessionCount_)
			        && (this.maxPinLen_ == other.maxPinLen_)
			        && (this.minPinLen_ == other.minPinLen_)
			        && (this.totalPublicMemory_ == other.totalPublicMemory_)
			        && (this.freePublicMemory_ == other.freePublicMemory_)
			        && (this.totalPrivateMemory_ == other.totalPrivateMemory_)
			        && (this.freePrivateMemory_ == other.freePrivateMemory_)
			        && this.hardwareVersion_.equals(other.hardwareVersion_)
			        && this.firmwareVersion_.equals(other.firmwareVersion_)
			        && this.time_.equals(other.time_)
			        && (this.rng_ == other.rng_)
			        && (this.writeProtected_ == other.writeProtected_)
			        && (this.loginRequired_ == other.loginRequired_)
			        && (this.userPinInitialized_ == other.userPinInitialized_)
			        && (this.restoreKeyNotNeeded_ == other.restoreKeyNotNeeded_)
			        && (this.clockOnToken_ == other.clockOnToken_)
			        && (this.protectedAuthenticationPath_ == other.protectedAuthenticationPath_)
			        && (this.dualCryptoOperations_ == other.dualCryptoOperations_)
			        && (this.tokenInitialized_ == other.tokenInitialized_)
			        && (this.secondaryAuthentication_ == other.secondaryAuthentication_)
			        && (this.userPinCountLow_ == other.userPinCountLow_)
			        && (this.userPinFinalTry_ == other.userPinFinalTry_)
			        && (this.userPinLocked_ == other.userPinLocked_)
			        && (this.userPinToBeChanged_ == other.userPinToBeChanged_)
			        && (this.soPinCountLow_ == other.soPinCountLow_)
			        && (this.soPinFinalTry_ == other.soPinFinalTry_)
			        && (this.soPinLocked_ == other.soPinLocked_) && (this.soPinToBeChanged_ == other.soPinToBeChanged_));
		}

		return equal;
	}

	/**
	 * The overriding of this method should ensure that the objects of this class
	 * work correctly in a hashtable.
	 *
	 * @return The hash code of this object. Gained from the label_,
	 *         manufacturerID_, model_ and serialNumber_.
	 * @preconditions
	 * @postconditions
	 */
	public int hashCode() {
		return label_.hashCode() ^ manufacturerID_.hashCode() ^ model_.hashCode()
		    ^ serialNumber_.hashCode();
	}

}
