001: /*
002: * TclDouble.java --
003: *
004: * Implements the TclDouble internal object representation, as well
005: * variable traces for the tcl_precision variable.
006: *
007: * Copyright (c) 1997 Sun Microsystems, Inc.
008: *
009: * See the file "license.terms" for information on usage and
010: * redistribution of this file, and for a DISCLAIMER OF ALL
011: * WARRANTIES.
012: *
013: * RCS: @(#) $Id: TclDouble.java,v 1.11 2006/06/24 00:30:42 mdejong Exp $
014: *
015: */
016:
017: package tcl.lang;
018:
019: /*
020: * This class implements the double object type in Tcl.
021: */
022:
023: public class TclDouble implements InternalRep {
024:
025: /*
026: * Internal representation of a double value.
027: * This field is package scoped so that the
028: * expr module can quickly read the value.
029: */
030:
031: double value;
032:
033: // Extra debug checking
034:
035: private final static boolean validate = false;
036:
037: /*
038: *----------------------------------------------------------------------
039: *
040: * TclDouble --
041: *
042: * Construct a TclDouble representation with the given double
043: * value.
044: *
045: * Results:
046: * None.
047: *
048: * Side effects:
049: * None.
050: *
051: *----------------------------------------------------------------------
052: */
053:
054: private TclDouble(double d) // Initial value.
055: {
056: value = d;
057:
058: if (TclObject.saveObjRecords) {
059: String key = "TclDouble";
060: Integer num = (Integer) TclObject.objRecordMap.get(key);
061: if (num == null) {
062: num = new Integer(1);
063: } else {
064: num = new Integer(num.intValue() + 1);
065: }
066: TclObject.objRecordMap.put(key, num);
067: }
068: }
069:
070: /*
071: *----------------------------------------------------------------------
072: *
073: * TclDouble --
074: *
075: * Construct a TclDouble representation with the initial value
076: * taken from the given string.
077: *
078: * Results:
079: * None.
080: *
081: * Side effects:
082: * None.
083: *
084: *----------------------------------------------------------------------
085: */
086:
087: private TclDouble(Interp interp, // Current interpreter.
088: String str) // String that contains the initial value.
089: throws TclException // If error occurs in string conversion.
090: {
091: value = Util.getDouble(interp, str);
092:
093: if (TclObject.saveObjRecords) {
094: String key = "TclDouble";
095: Integer num = (Integer) TclObject.objRecordMap.get(key);
096: if (num == null) {
097: num = new Integer(1);
098: } else {
099: num = new Integer(num.intValue() + 1);
100: }
101: TclObject.objRecordMap.put(key, num);
102: }
103: }
104:
105: /*
106: *----------------------------------------------------------------------
107: *
108: * duplicate --
109: *
110: * Duplicate the current object.
111: *
112: * Results:
113: * A dupilcate of the current object.
114: *
115: * Side effects:
116: * None.
117: *
118: *----------------------------------------------------------------------
119: */
120:
121: public InternalRep duplicate() {
122: if (TclObject.saveObjRecords) {
123: String key = "TclDouble.duplicate()";
124: Integer num = (Integer) TclObject.objRecordMap.get(key);
125: if (num == null) {
126: num = new Integer(1);
127: } else {
128: num = new Integer(num.intValue() + 1);
129: }
130: TclObject.objRecordMap.put(key, num);
131: }
132:
133: return new TclDouble(value);
134: }
135:
136: /**
137: * Implement this no-op for the InternalRep interface.
138: */
139:
140: public void dispose() {
141: }
142:
143: /*
144: *----------------------------------------------------------------------
145: *
146: * newInstance --
147: *
148: * Creates a new instance of a TclObject with a TclDouble internal
149: * representation.
150: *
151: * Results:
152: * The newly created TclObject.
153: *
154: * Side effects:
155: * None.
156: *
157: *----------------------------------------------------------------------
158: */
159:
160: public static TclObject newInstance(double d) // Initial value.
161: {
162: return new TclObject(new TclDouble(d));
163: }
164:
165: /*
166: *----------------------------------------------------------------------
167: *
168: * setDoubleFromAny --
169: *
170: * Called to convert a TclObject's internal rep to TclDouble.
171: *
172: * Results:
173: * None.
174: *
175: * Side effects:
176: * When successful, the internal representation of tobj is
177: * changed to TclDouble, if it is not already so.
178: *
179: *----------------------------------------------------------------------
180: */
181:
182: private static void setDoubleFromAny(Interp interp, // Current interpreter. May be null.
183: TclObject tobj) // The object to convert.
184: throws TclException // If error occurs in type conversion.
185: // Error message will be left inside
186: // the interp if it's not null.
187:
188: {
189: // This method is only ever invoked from TclDouble.get().
190: // This method will never be invoked when the internal
191: // rep is already a TclDouble. This method will always
192: // reparse a double from the string rep so that tricky
193: // special cases like "040" are handled correctly.
194:
195: if (validate) {
196: if (tobj.getInternalRep() instanceof TclDouble) {
197: throw new TclRuntimeError(
198: "should not be TclDouble, was a "
199: + tobj.getInternalRep().getClass()
200: .getName());
201: }
202: }
203:
204: tobj.setInternalRep(new TclDouble(interp, tobj.toString()));
205:
206: if (TclObject.saveObjRecords) {
207: String key = "TclString -> TclDouble";
208: Integer num = (Integer) TclObject.objRecordMap.get(key);
209: if (num == null) {
210: num = new Integer(1);
211: } else {
212: num = new Integer(num.intValue() + 1);
213: }
214: TclObject.objRecordMap.put(key, num);
215: }
216: }
217:
218: /*
219: *----------------------------------------------------------------------
220: *
221: * get --
222: *
223: * Returns the double value of the object.
224: *
225: * Results:
226: * The double value of the object.
227: *
228: * Side effects:
229: * When successful, the internal representation of tobj is
230: * changed to TclDouble, if it is not already so.
231: *
232: *----------------------------------------------------------------------
233: */
234:
235: public static double get(Interp interp, // Current interpreter. May be null.
236: TclObject tobj) // The object to query.
237: throws TclException // If the object does not have a TclDouble
238: // representation and a conversion fails.
239: // Error message will be left inside
240: // the interp if it's not null.
241: {
242: TclDouble tdouble;
243:
244: if (!tobj.isDoubleType()) {
245: if (Util.isJacl()) {
246: // Try to convert to TclDouble. If the string can't be
247: // parsed as a double, then raise a TclException here.
248:
249: setDoubleFromAny(interp, tobj);
250: double dval;
251: tdouble = (TclDouble) tobj.getInternalRep();
252: dval = tdouble.value;
253:
254: // The string can be parsed as a double, but if it
255: // can also be parsed as an integer then we need
256: // to convert the internal rep back to TclInteger.
257: // This logic handles the special case of an octal
258: // string like "040". The most common path through
259: // this code is a normal double like "1.0", so this
260: // code will only attempt a conversion to TclInteger
261: // when the string looks like an integer. This logic
262: // is tricky, but it leads to a speedup in performance
263: // critical expr code since the double value from a
264: // TclDouble can be used without having to check to
265: // see if the double looks like an integer.
266:
267: if (Util.looksLikeInt(tobj.toString())) {
268: try {
269: int ival = TclInteger.get(null, tobj);
270:
271: // A tricky octal like "040" can be parsed as
272: // the double 40.0 or the integer 32, return
273: // the value parsed as a double and leave
274: // the object with a TclInteger internal rep.
275:
276: return dval;
277: } catch (TclException te) {
278: throw new TclRuntimeError(
279: "looksLikeInt() is true, "
280: + "but TclInteger.get() failed for \""
281: + tobj.toString() + "\"");
282: }
283: }
284:
285: if (validate) {
286: // Double check that we did not just create a TclDouble
287: // that looks like an integer.
288:
289: InternalRep tmp = tobj.getInternalRep();
290: if (!(tmp instanceof TclDouble)) {
291: throw new TclRuntimeError(
292: "not a TclDouble, is a "
293: + tmp.getClass().getName());
294: }
295: String stmp = tobj.toString();
296: if (Util.looksLikeInt(stmp)) {
297: throw new TclRuntimeError(
298: "looks like an integer");
299: }
300: }
301: } else {
302: setDoubleFromAny(interp, tobj);
303: tdouble = (TclDouble) tobj.getInternalRep();
304: }
305: } else {
306: tdouble = (TclDouble) tobj.getInternalRep();
307: }
308:
309: return tdouble.value;
310: }
311:
312: /*
313: *----------------------------------------------------------------------
314: *
315: * set --
316: *
317: * Changes the double value of the object.
318: *
319: * Results:
320: * None.
321: *
322: * Side effects:
323: * The internal representation of tobj is
324: * changed to TclDouble, if it is not already so.
325: *
326: *----------------------------------------------------------------------
327: */
328:
329: public static void set(TclObject tobj, // The object to modify.
330: double d) // The new value for the object.
331: {
332: tobj.invalidateStringRep();
333:
334: if (tobj.isDoubleType()) {
335: TclDouble tdouble = (TclDouble) tobj.getInternalRep();
336: tdouble.value = d;
337: } else {
338: tobj.setInternalRep(new TclDouble(d));
339: }
340: }
341:
342: /*
343: *----------------------------------------------------------------------
344: *
345: * toString --
346: *
347: * Called to query the string representation of the Tcl
348: * object. This method is called only by TclObject.toString()
349: * when TclObject.stringRep is null.
350: *
351: * Results:
352: * Returns the string representation of the TclDouble object.
353: *
354: * Side effects:
355: * None.
356: *
357: *----------------------------------------------------------------------
358: */
359:
360: public String toString() {
361: return Util.printDouble(value);
362: }
363:
364: /**
365: * This special helper method is used only by
366: * the Expression module. This method will
367: * change the internal rep to a TclDouble with
368: * the passed in double value. This method does
369: * not invalidate the string rep since the
370: * object's value is not being changed.
371: *
372: * @param tobj the object to operate on.
373: * @param d the new double value.
374: */
375: static void exprSetInternalRep(TclObject tobj, double d) {
376: if (validate) {
377:
378: // Double check that the internal rep is not
379: // already of type TclDouble.
380:
381: InternalRep rep = tobj.getInternalRep();
382:
383: if (rep instanceof TclDouble) {
384: throw new TclRuntimeError(
385: "exprSetInternalRep() called with object"
386: + " that is already of type TclDouble");
387: }
388:
389: // Double check that the new int value and the
390: // string rep would parse to the same integer.
391:
392: double d2;
393: try {
394: d2 = Util.getDouble(null, tobj.toString());
395: } catch (TclException te) {
396: throw new TclRuntimeError(
397: "exprSetInternalRep() called with double"
398: + " value that could not be parsed from the string");
399: }
400: if (d != d2) {
401: throw new TclRuntimeError(
402: "exprSetInternalRep() called with double value "
403: + d
404: + " that does not match parsed double value "
405: + d2 + ", parsed from str \""
406: + tobj.toString() + "\"");
407: }
408:
409: // It should not be possible to parse the TclObject's string
410: // rep as an integer since we know it is a double. An object
411: // that could be parsed as either a double or an integer
412: // should have been parsed as an integer.
413:
414: try {
415: int ival = Util.getInt(null, tobj.toString());
416: throw new TclRuntimeError(
417: "should not be able to parse string rep as int: "
418: + tobj.toString());
419: } catch (TclException e) {
420: // No-op
421: }
422: }
423:
424: tobj.setInternalRep(new TclDouble(d));
425: }
426:
427: // This method is used to set the internal rep for a recycled
428: // object to TclDouble, in the edge case where it might have
429: // been changed. This method exists only because the
430: // TclDouble ctor can't be made package access without
431: // changing signature regression tests.
432:
433: static void setRecycledInternalRep(TclObject tobj) {
434: tobj.setInternalRep(new TclDouble(0.0));
435: }
436:
437: } // end TclDouble
|