/* * 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. * * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart * * RCS @(#) $Id: TclObject.java,v 1.9 2003/01/09 02:15:40 mdejong Exp $ * */ using System; using System.Text; namespace 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 class TclObject { /// Returns the handle to the current internal rep. This method should be /// called only by an InternalRep implementation. /// the handle to the current internal rep. /// 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. /// /// the new internal rep. /// public InternalRep InternalRep { get { disposedCheck(); return internalRep; } set { disposedCheck(); if ( value == null ) { throw new TclRuntimeError( "null InternalRep" ); } if ( value == 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. Also note that the makeReference method // will do nothing in the case where the Tcl_Obj inside the // CObject was originally allocated in Java. When converting // to a CObject we need to break the link made earlier. if ( ( internalRep is CObject ) && !( value is 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 ( (System.Object)stringRep == null ) { stringRep = internalRep.ToString(); } ( (CObject)internalRep ).makeReference( this ); } //System.out.println("TclObject setInternalRep for \"" + stringRep + "\""); //System.out.println("from \"" + internalRep.getClass().getName() + // "\" to \"" + rep.getClass().getName() + "\""); internalRep.dispose(); internalRep = value; } } /// Returns true if the TclObject is shared, false otherwise. /// true if the TclObject is shared, false otherwise. /// public bool Shared { get { disposedCheck(); return ( refCount > 1 ); } } /// Returns the refCount of this object. /// /// /// refCount. /// public int RefCount { get { return refCount; } } /// Returns the Tcl_Obj* objPtr member for a CObject or TclList. /// This method is only called from Tcl Blend. /// internal long CObjectPtr { get { if ( internalRep is CObject ) { return ( (CObject)internalRep ).CObjectPtr; } else { return 0; } } } /// Returns 2 if the internal rep is a TclList. /// Returns 1 if the internal rep is a CObject. /// Otherwise returns 0. /// This method provides an optimization over /// invoking getInternalRep() and two instanceof /// checks via JNI. It is only used by Tcl Blend. /// internal int CObjectInst { get { if ( internalRep is CObject ) { if ( internalRep is TclList ) return 2; else return 1; } else { return 0; } } } // Internal representation of the object. protected internal InternalRep internalRep; // Reference count of this object. When 0 the object will be deallocated. protected internal int refCount; // String representation of the object. protected internal string stringRep; // Return true if the TclObject contains a TclList. public bool isListType() { return (internalRep.GetType().ToString().Contains("TclList")); } /// Creates a TclObject with the given InternalRep. This method should be /// called only by an InternalRep implementation. /// /// /// the initial InternalRep for this object. /// public TclObject( InternalRep rep ) { if ( rep == null ) { throw new TclRuntimeError( "null InternalRep" ); } internalRep = rep; stringRep = null; refCount = 0; } /// 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. /// /// /// the initial InternalRep for this object. /// /// the initial string rep for this object. /// protected internal TclObject( TclString rep, string s ) { if ( rep == null ) { throw new TclRuntimeError( "null InternalRep" ); } internalRep = rep; stringRep = s; refCount = 0; } /// Returns the string representation of the object. /// /// /// the string representation of the object. /// public override string ToString() { disposedCheck(); if ( (System.Object)stringRep == null ) { stringRep = internalRep.ToString().Replace( "Infinity", "inf" ); } return stringRep; } /// Returns the UTF8 byte representation of the object. /// /// /// the string representation of the object. /// public byte[] ToBytes() { disposedCheck(); if ( (System.Object)stringRep == null ) { stringRep = internalRep.ToString(); } return Encoding.UTF8.GetBytes( 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. /// /// /// TclRuntimeError if object is not exclusively owned. /// public void invalidateStringRep() { disposedCheck(); if ( refCount > 1 ) { throw new TclRuntimeError( "string representation of object \"" + ToString() + "\" cannot be invalidated: refCount = " + refCount ); } stringRep = null; } /// Tcl_DuplicateObj -> duplicate /// /// Duplicate a TclObject, this method provides the preferred /// means to deal with modification of a shared TclObject. /// It should be invoked in conjunction with isShared instead /// of using the deprecated takeExclusive method. /// /// Example: /// /// if (tobj.isShared()) { /// tobj = tobj.duplicate(); /// } /// TclString.append(tobj, "hello"); /// /// /// an TclObject with a refCount of 0. /// public TclObject duplicate() { disposedCheck(); if ( internalRep is TclString ) { if ( (System.Object)stringRep == null ) { stringRep = internalRep.ToString(); } } TclObject newObj = new TclObject( internalRep.duplicate() ); newObj.stringRep = this.stringRep; newObj.refCount = 0; return newObj; } /// The takeExclusive method has been deprecated /// in favor of the new duplicate() method. The takeExclusive /// method would modify the ref count of the original object /// and return an object with a ref count of 1 instead of 0. /// These two behaviors lead to lots of useless duplication /// of objects that could be modified directly. /// public TclObject takeExclusive() { disposedCheck(); if ( refCount == 1 ) { return this; } else if ( refCount > 1 ) { if ( internalRep is TclString ) { if ( (System.Object)stringRep == null ) { stringRep = internalRep.ToString(); } } TclObject newObj = new TclObject( internalRep.duplicate() ); newObj.stringRep = this.stringRep; newObj.refCount = 1; refCount--; return newObj; } else { throw new TclRuntimeError( "takeExclusive() called on object \"" + ToString() + "\" with: refCount = 0" ); } } /// Tcl_IncrRefCount -> preserve /// /// 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. /// /// /// TclRuntimeError if the object has already been deallocated. /// public void preserve() { disposedCheck(); if ( internalRep is CObject ) { ( (CObject)internalRep ).incrRefCount(); } _preserve(); } /// _preserve /// /// Private implementation of preserve() method. /// This method will be invoked from Native code /// to change the TclObject's ref count without /// effecting the ref count of a CObject. /// private void _preserve() { refCount++; } /// Tcl_DecrRefCount -> release /// /// 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 void release() { disposedCheck(); if ( internalRep is CObject ) { ( (CObject)internalRep ).decrRefCount(); } _release(); } /// _release /// /// Private implementation of preserve() method. /// This method will be invoked from Native code /// to change the TclObject's ref count without /// effecting the ref count of a CObject. /// private 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; } } /// Raise a TclRuntimeError if this TclObject has been /// disposed of before the last ref was released. /// private void disposedCheck() { if ( internalRep == null ) { throw new TclRuntimeError( "TclObject has been deallocated" ); } } /// Return string describing type. public string typePtr { get { if ( this.internalRep == null ) return "null"; string sType = this.internalRep.GetType().ToString().Replace( "tcl.lang.Tcl", "" ).ToLower(); if ( sType == "integer" ) return "int"; if ( sType == "long" ) return "int"; return sType; } } } }