#BUGFIX by cg
authorClaus Gittinger <cg@exept.de>
Mon, 18 Sep 2017 09:07:02 +0200
changeset 4514 360a740542a7
parent 4513 2c986c93c992
child 4515 34fbe1d0b79f
#BUGFIX by cg class: UnitConverter class
UnitConverter.st
--- a/UnitConverter.st	Thu Aug 31 14:54:32 2017 +0200
+++ b/UnitConverter.st	Mon Sep 18 09:07:02 2017 +0200
@@ -1,5 +1,3 @@
-"{ Encoding: utf8 }"
-
 "
  COPYRIGHT (c) 1996 eXept Software AG
               All Rights Reserved
@@ -386,8 +384,10 @@
 
     "/ ---------------- temperature ---------------- 
 
-    self addConversion:[:d | d * 1.8 + 32] from:#celsius    to:#fahrenheit.
+    self addConversion:[:d | d * 1.8 + 32]   from:#celsius    to:#fahrenheit.
     self addConversion:[:f | (f - 32) / 1.8] from:#fahrenheit to:#celsius.
+    self addConversion:[:d | d + 273.15]     from:#celsius    to:#kelvin.
+    self addConversion:[:k | k - 273.15]     from:#kelvin     to:#celsius.
 
     "/ ---------------- nature ---------------- 
     Constants at:#planckLength   put:#(1.616e-35   #'m').
@@ -399,8 +399,8 @@
      UnitConverter initializeConversions
     "
 
-    "Created: / 22.7.1997 / 13:56:40 / cg"
-    "Modified: / 10.10.2001 / 14:39:04 / cg"
+    "Created: / 22-07-1997 / 13:56:40 / cg"
+    "Modified: / 18-09-2017 / 08:38:14 / cg"
 !
 
 initializePrintValues
@@ -577,6 +577,55 @@
      to destUnit (symbolic); return nil, if the conversion is
      unknown."
 
+    ^ self convert:howMany from:sourceUnit to:destUnit pairsAlreadyTried:(OrderedCollection new)
+
+    "direct - how many meters are there in two inches:
+     UnitConverter convert:2 from:#inch to:#meter   
+     
+     reverse - how many inches are there in one meter
+     UnitConverter convert:1 from:#meter to:#inch   
+
+     with alias:
+     UnitConverter convert:1 from:#inch to:#m    
+     UnitConverter convert:1 from:#inch to:#mm          
+     UnitConverter convert:1 from:#inch to:#millimeter   
+     UnitConverter convert:1 from:#inch to:#nanometer 
+     UnitConverter convert:1 from:#mm to:#km  
+     UnitConverter convert:1 from:#km to:#foot   
+
+     indirect:
+     UnitConverter convert:1 from:#mm   to:#twip  
+     UnitConverter convert:1 from:#inch to:#twip  
+     UnitConverter convert:1 from:'letterH' to:#point  
+     UnitConverter convert:1 from:'letterlH' to:#point  
+
+     UnitConverter convert:5 from:#barrel to:#liter  
+     UnitConverter convert:10 from:#kilogram to:#carat  
+
+     UnitConverter convert:1 from:#liter to:#floz  
+     UnitConverter convert:1 from:#floz to:#liter  
+     UnitConverter convert:1 from:#oz to:#gram  
+     UnitConverter convert:1 from:#ounce to:#gram  
+
+     UnitConverter convert:37 from:#celsius to:#fahrenheit  -> 98.6    
+     UnitConverter convert:37 from:#celsius to:#kelvin      -> 310.15
+     UnitConverter convert:0 from:#celsius to:#kelvin       -> 273.15  
+     UnitConverter convert:0 from:#kelvin to:#celsius       -> -273.15 
+     UnitConverter convert:0 from:#kelvin to:#fahrenheit    -> -459.67    
+     UnitConverter convert:0 from:#fahrenheit to:#celsius   -> -17.7777777777778    
+     UnitConverter convert:0 from:#fahrenheit to:#kelvin    -> 255.372222222222   
+
+    "
+
+    "Created: / 31-05-1996 / 16:23:38 / cg"
+    "Modified (comment): / 18-09-2017 / 09:02:09 / cg"
+!
+
+convert:howMany from:sourceUnit to:destUnit pairsAlreadyTried:pairsAlready
+    "given a value in sourceUnit (symbolic), try to convert it
+     to destUnit (symbolic); return nil, if the conversion is
+     unknown."
+
     |u conversions alias rslt sU dU const val unit 
      i suNumerator suDenominator duNumerator duDenominator uN uD
      sF1 sF2 dF1 dF2
@@ -592,17 +641,24 @@
     sourceUnit == destUnit ifTrue:[
         ^ howMany
     ].
-
+    thisContext isReallyRecursive ifTrue:[
+        ^ self noConversionFrom:sourceUnit to:destUnit.
+    ].
+    (pairsAlready includes:(sourceUnit->destUnit)) ifTrue:[
+        ^ self noConversionFrom:sourceUnit to:destUnit.
+    ].
+    pairsAlready add:(sourceUnit->destUnit).
+    
     sourceUnit isSymbol ifFalse:[
         s := sourceUnit withoutSeparators.
         sU := s asSymbolIfInterned.
         sU notNil ifTrue:[
-            ^ self convert:howMany from:sU to:destUnit
+            ^ self convert:howMany from:sU to:destUnit pairsAlreadyTried:pairsAlready
         ].
         (s startsWith:$() ifTrue:[
             (s endsWith:$)) ifTrue:[
                 s := s copyFrom:2 to:(s size - 1).
-                ^ self convert:howMany from:s to:destUnit
+                ^ self convert:howMany from:s to:destUnit pairsAlreadyTried:pairsAlready
             ]
         ].
     ].
@@ -610,12 +666,12 @@
         d := destUnit withoutSeparators.
         dU := d asSymbolIfInterned.
         dU notNil ifTrue:[
-            ^ self convert:howMany from:sourceUnit to:dU
+            ^ self convert:howMany from:sourceUnit to:dU pairsAlreadyTried:pairsAlready
         ].
         (d startsWith:$() ifTrue:[
             (d endsWith:$)) ifTrue:[
                 d := d copyFrom:2 to:(d size - 1).
-                ^ self convert:howMany from:sourceUnit to:d
+                ^ self convert:howMany from:sourceUnit to:d pairsAlreadyTried:pairsAlready
             ]
         ].
     ].
@@ -624,23 +680,23 @@
 
     u := self unscaled:sourceUnit.
     u notNil ifTrue:[
-        ^ self convert:(howMany*(u value)) from:(u key) to:destUnit
+        ^ self convert:(howMany*(u value)) from:(u key) to:destUnit pairsAlreadyTried:pairsAlready
     ].
 
     u := self unscaled:destUnit.
     u notNil ifTrue:[
-        ^ self convert:(howMany/(u value)) from:sourceUnit to:(u key)
+        ^ self convert:(howMany/(u value)) from:sourceUnit to:(u key) pairsAlreadyTried:pairsAlready
     ].
 
     "/ and of aliases ...
 
     alias := Aliases at:sourceUnit ifAbsent:nil.
     alias notNil ifTrue:[
-        ^ self convert:howMany from:alias to:destUnit
+        ^ self convert:howMany from:alias to:destUnit pairsAlreadyTried:pairsAlready
     ].
     alias := Aliases at:destUnit ifAbsent:nil.
     alias notNil ifTrue:[
-        ^ self convert:howMany from:sourceUnit to:alias
+        ^ self convert:howMany from:sourceUnit to:alias pairsAlreadyTried:pairsAlready
     ].
 
     "/ any constants ?
@@ -649,7 +705,7 @@
         const := Constants at:sourceUnit.
         val := const at:1.
         unit := const at:2.
-        ^ self convert:(howMany*val) from:unit to:destUnit
+        ^ self convert:(howMany*val) from:unit to:destUnit pairsAlreadyTried:pairsAlready
     ].
 
     "/ compounds (^ , / or *) are very naively parsed
@@ -663,7 +719,7 @@
         sU := (sourceUnit copyButLast:2) asSymbolIfInterned.
         dU := (destUnit copyButLast:2) asSymbolIfInterned.
         (sU notNil and:[dU notNil]) ifTrue:[
-            ^ (self convert:(howMany sqrt) from:sU to:dU) squared
+            ^ (self convert:(howMany sqrt) from:sU to:dU pairsAlreadyTried:pairsAlready) squared 
         ].
         ^ self noConversionFrom:sourceUnit to:destUnit.
     ].
@@ -671,7 +727,7 @@
         sU := (sourceUnit copyButLast:2) asSymbolIfInterned.
         dU := (destUnit copyButLast:2) asSymbolIfInterned.
         (sU notNil and:[dU notNil]) ifTrue:[
-            ^ (self convert:(howMany raisedTo:(1/3)) from:sU to:dU) raisedTo:3
+            ^ (self convert:(howMany raisedTo:(1/3)) from:sU to:dU pairsAlreadyTried:pairsAlready) raisedTo:3
         ].
         ^ self noConversionFrom:sourceUnit to:destUnit.
     ].
@@ -687,9 +743,9 @@
         duNumerator := destUnit copyTo:(i - 1).
         duDenominator := destUnit copyFrom:(i + 1).
 
-        uN := self convert:howMany from:suNumerator to:duNumerator.
+        uN := self convert:howMany from:suNumerator to:duNumerator pairsAlreadyTried:pairsAlready.
         uN notNil ifTrue:[
-            uD := self convert:1 from:suDenominator to:duDenominator.
+            uD := self convert:1 from:suDenominator to:duDenominator pairsAlreadyTried:pairsAlready.
             uD notNil ifTrue:[
                 ^ uN / uD
             ]
@@ -706,9 +762,9 @@
         dF1 := destUnit copyTo:(i - 1).
         dF2 := destUnit copyFrom:(i + 1).
 
-        u := self convert:howMany from:sF1 to:dF1.
+        u := self convert:howMany from:sF1 to:dF1 pairsAlreadyTried:pairsAlready.
         u notNil ifTrue:[
-            u := self convert:u from:sF2 to:dF2.
+            u := self convert:u from:sF2 to:dF2 pairsAlreadyTried:pairsAlready.
             u notNil ifTrue:[
                 ^ u
             ]
@@ -723,22 +779,36 @@
     rslt notNil ifTrue:[^ rslt].
 
     "/ try inverse conversion ...
+    "/ but here, only linear factors (numeric conversions) are allowed!!
+    [
+        |conversions revFactor|
+        
+        conversions := Conversions at:destUnit ifAbsent:nil.
+        conversions notNil ifTrue:[
+            revFactor := conversions at:sourceUnit ifAbsent:nil.
+            revFactor isNumber ifTrue:[
+                ^ howMany / revFactor
+            ].
+        ].
+    ] value.    
 
-    rslt := self convertDirect:1 from:destUnit to:sourceUnit.
-    rslt notNil ifTrue:[ ^ howMany / rslt].
-
+    
     "/ here's the deep recursion ...
 
-
     "/ try indirect conversion from source
-
     conversions := Conversions at:sourceUnit ifAbsent:nil.
     conversions notNil ifTrue:[
         conversions keysAndValuesDo:[:intermediateUnit :factor1 |
-            |factor2|
+            |factor2 intermediateValue result|
 
-            factor2 := self convert:factor1 from:intermediateUnit to:destUnit.
-            factor2 notNil ifTrue:[^ factor2 * howMany].
+            factor1 isBlock ifTrue:[
+                intermediateValue := factor1 value:howMany.
+                result := self convert:intermediateValue from:intermediateUnit to:destUnit pairsAlreadyTried:pairsAlready.
+                result notNil ifTrue:[ ^ result ].
+            ] ifFalse:[    
+                factor2 := self convert:factor1 from:intermediateUnit to:destUnit pairsAlreadyTried:pairsAlready.
+                factor2 notNil ifTrue:[^ factor2 * howMany].
+            ].
         ].
     ].
 
@@ -749,8 +819,10 @@
         conversions keysAndValuesDo:[:intermediateUnit :factor1 |
             |factor2|
 
-            factor2 := self convert:(factor1) from:intermediateUnit to:sourceUnit.
-            factor2 notNil ifTrue:[^ howMany / factor2].
+            factor1 isBlock ifFalse:[    
+                factor2 := self convert:(factor1) from:intermediateUnit to:sourceUnit pairsAlreadyTried:pairsAlready.
+                factor2 notNil ifTrue:[^ howMany / factor2].
+            ].
         ].
     ].
 
@@ -773,11 +845,10 @@
                 "/ what about c->b ?
 
                 iUnit := aUnit copyFrom:pref size + 1.
-                factor2 := self convert:1 from:sF2 to:iUnit.
+                factor2 := self convert:1 from:sF2 to:iUnit pairsAlreadyTried:pairsAlready.
                 factor2 notNil ifTrue:[
                     "/ good ...
-                    rslt := self convert:(factor2 * howMany)
-                                    from:aUnit to:destUnit.
+                    rslt := self convert:(factor2 * howMany) from:aUnit to:destUnit pairsAlreadyTried:pairsAlready.
                     rslt notNil ifTrue:[^ rslt].
                 ]
             ]
@@ -811,10 +882,14 @@
      UnitConverter convert:10 from:#kilogram to:#carat  
 
      UnitConverter convert:1 from:#liter to:#floz  
+     UnitConverter convert:0 from:#fahrenheit to:#kelvin  
+     UnitConverter convert:0 from:#fahrenheit to:#celsius  
+     UnitConverter convert:0 from:#celsius to:#kelvin  
+     UnitConverter convert:0 from:#kelvin to:#celsius  
     "
 
-    "Created: 31.5.1996 / 16:23:38 / cg"
-    "Modified: 6.8.1997 / 18:10:22 / cg"
+    "Created: / 17-09-2017 / 10:11:40 / cg"
+    "Modified: / 18-09-2017 / 08:39:15 / cg"
 !
 
 fileSizeBinaryStringFor:nBytes
@@ -1326,7 +1401,7 @@
 
 !UnitConverter class methodsFor:'private'!
 
-addConversion:factor from:sourceMetric to:destMetric
+addConversion:factorOrBlock from:sourceMetric to:destMetric
     "add a conversion"
 
     |conversion|
@@ -1336,7 +1411,7 @@
 	conversion := IdentityDictionary new.
 	Conversions at:sourceMetric put:conversion
     ].
-    conversion at:destMetric put:factor.
+    conversion at:destMetric put:factorOrBlock.
     ^ conversion
 
     "Created: 31.5.1996 / 13:51:25 / cg"