/* * TclObject.java * * Copyright (c) 1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * * RCS: @(#) $Id: TclObject.java,v 1.1.1.1 1998/10/14 21:09:14 cvsadmin Exp $ * */ package tcl.lang; /** * This class implements the basic notion of an "object" in Tcl. The * fundamental representation of an object is its string value. However, * an object can also have an internal representation, which is a "cached" * reprsentation of this object in another form. The type of the internal * rep of Tcl objects can mutate. This class provides the storage of the * string rep and the internal rep, as well as the facilities for mutating * the internal rep. */ public final class TclObject { /** * Internal representation of the object. */ protected InternalRep internalRep; /** * Reference count of this object. When 0 the object will be deallocated. */ protected int refCount; /** * String representation of the object. */ protected String stringRep; /** * Creates a TclObject with the given InternalRep. This method should be * called only by an InternalRep implementation. * * @param rep the initial InternalRep for this object. */ public TclObject(InternalRep rep) { internalRep = rep; stringRep = null; } /** * Creates a TclObject with the given InternalRep and stringRep. * This constructor is used by the TclString class only. No other place * should call this constructor. * * @param rep the initial InternalRep for this object. * @param s the initial string rep for this object. */ protected TclObject(TclString rep, String s) { internalRep = rep; stringRep = s; } /** * Returns the handle to the current internal rep. This method should be * called only by an InternalRep implementation. * * @return the handle to the current internal rep. */ public final InternalRep getInternalRep() { return internalRep; } /** * Change the internal rep of the object. The old internal rep * will be deallocated as a result. This method should be * called only by an InternalRep implementation. * * @param rep the new internal rep. */ public final void setInternalRep(InternalRep rep) { if (rep == internalRep) { return; } /* * In the special case where the internal representation is a CObject, * we want to call the special interface to convert the underlying * native object into a reference to the Java TclObject. Note that * this test will always fail if we are not using the native * implementation. */ if ((internalRep instanceof CObject) && !(rep instanceof CObject)) { /* * We must ensure that the string rep is copied into Java * before we lose the reference to the underlying CObject. * Otherwise we will lose the original string information * when the backpointer is lost. */ if (stringRep == null) { stringRep = internalRep.toString(); } ((CObject)internalRep).makeReference(this); } internalRep.dispose(); internalRep = rep; } /** * Returns the string representation of the object. * * @return the string representation of the object. */ public final String toString() { if (stringRep == null) { stringRep = internalRep.toString(); } return stringRep; } /** * Sets the string representation of the object to null. Next * time when toString() is called, getInternalRep().toString() will * be called. This method should be called ONLY when an InternalRep * is about to modify the value of a TclObject. * * @exception TclRuntimeError if object is not exclusively owned. */ public final void invalidateStringRep() throws TclRuntimeError { if (refCount > 1) { throw new TclRuntimeError("string representation of object \"" + toString() + "\" cannot be invalidated: refCount = " + refCount); } stringRep = null; } /** * Returns true if the TclObject is shared, false otherwise. * @return true if the TclObject is shared, false otherwise. */ public final boolean isShared() { return (refCount > 1); } /** * Takes exclusive ownership of this object. This method should be * called before invoking any method that will modify the value of * the object. E.g. * * tobj = tobj.takeExclusive(); * TclString.append(tobj, "hello"); * * The result of this method depends on the refCount of the object: * * refCount == 1: the object itself is returned. * refCount > 1: a copy of the object will be returned. The refCount * of the copy is set to 1. * refCount < 1: TclRuntimeError will be thrown. * * @return an TclObject with a refCount of 1. * @exception TclRuntimeError if the refCount <= 0 */ public final TclObject takeExclusive() throws TclRuntimeError { if (refCount == 1) { return this; } else if (refCount > 1) { if (internalRep instanceof TclString) { if (stringRep == null) { stringRep = internalRep.toString(); } } TclObject newObj = new TclObject(internalRep.duplicate()); newObj.stringRep = this.stringRep; newObj.refCount = 1; this.refCount --; return newObj; } else { throw new TclRuntimeError("takeExclusive() called on object \"" + toString() + "\" with: refCount = 0"); } } /** * Increments the refCount to indicate the caller's intent to * preserve the value of this object. Each preserve() call must be matched * by a corresponding release() call. * * @exception TclRuntimeError if the object has already been deallocated. */ public final void preserve() throws TclRuntimeError { if (internalRep == null) { throw new TclRuntimeError("Attempting to preserve object " + "after it was deallocated"); } refCount ++; } /** * Decrements the refCount to indicate that the caller is no longer * interested in the value of this object. If the refCount reaches 0, * the obejct will be deallocated. */ public final void release() { refCount --; if (refCount <= 0) { internalRep.dispose(); /* * Setting these to null will ensure that any attempt to use * this object will result in a Java NullPointerException. */ internalRep = null; stringRep = null; } } /** * Returns the refCount of this object. This is used only fot debugging * purposes by JavainfoCmd. * * @return refCount. */ protected final int getRefCount() { return refCount; } }