(* Ulm's Oberon Library Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany ---------------------------------------------------------------------------- Ulm's Oberon Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Ulm's Oberon Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ---------------------------------------------------------------------------- E-mail contact: oberon@mathematik.uni-ulm.de ---------------------------------------------------------------------------- $Id: Scales.om,v 1.3 2004/09/03 09:31:53 borchert Exp $ ---------------------------------------------------------------------------- $Log: Scales.om,v $ Revision 1.3 2004/09/03 09:31:53 borchert bug fixes: Services.Init added in CreateOperand Scales.Measure changed to Measure Revision 1.2 1995/01/16 21:40:39 borchert - assertions of Assertions converted into real assertions - fixes due to changed if of PersistentObjects Revision 1.1 1994/02/22 20:10:03 borchert Initial revision ---------------------------------------------------------------------------- AFB 12/91 ---------------------------------------------------------------------------- *) MODULE ulmScales; IMPORT Disciplines := ulmDisciplines, Events := ulmEvents, Objects := ulmObjects, Operations := ulmOperations, PersistentObjects := ulmPersistentObjects, RelatedEvents := ulmRelatedEvents, Services := ulmServices, SYS := SYSTEM; TYPE Scale* = POINTER TO ScaleRec; Family* = POINTER TO FamilyRec; FamilyRec* = RECORD (Disciplines.ObjectRec) (* private components *) reference: Scale; END; TYPE Unit* = POINTER TO UnitRec; UnitList = POINTER TO UnitListRec; UnitListRec = RECORD unit: Unit; next: UnitList; END; Interface* = POINTER TO InterfaceRec; ScaleRec* = RECORD (Disciplines.ObjectRec) (* private components *) if: Interface; family: Family; head, tail: UnitList; nextUnit: UnitList; END; CONST unitNameLength* = 32; TYPE UnitName* = ARRAY unitNameLength OF CHAR; UnitRec* = RECORD (Disciplines.ObjectRec) name: UnitName; scale: Scale; END; CONST undefined = 0; absolute* = 1; relative* = 2; TYPE Measure* = POINTER TO MeasureRec; MeasureRec* = RECORD (Operations.OperandRec) scale: Scale; type: SHORTINT; (* absolute or relative? *) END; VAR measureType: Services.Type; TYPE Value* = LONGINT; CONST add* = Operations.add; sub* = Operations.sub; TYPE Operation* = SHORTINT; (* add or sub *) TYPE CreateProc* = PROCEDURE (scale: Scale; VAR measure: Measure; abs: BOOLEAN); GetValueProc* = PROCEDURE (measure: Measure; unit: Unit; VAR value: Value); SetValueProc* = PROCEDURE (measure: Measure; unit: Unit; value: Value); AssignProc* = PROCEDURE (target: Measure; source: Measure); OperatorProc* = PROCEDURE (op: Operation; op1, op2, result: Measure); CompareProc* = PROCEDURE (op1, op2: Measure) : INTEGER; ConvertProc* = PROCEDURE (from, to: Measure); InterfaceRec* = RECORD (Objects.ObjectRec) create*: CreateProc; getvalue*: GetValueProc; setvalue*: SetValueProc; assign*: AssignProc; op*: OperatorProc; compare*: CompareProc; (* the conversion routines are only to be provided if the scaling system belongs to a family *) scaleToReference*: ConvertProc; referenceToScale*: ConvertProc; END; VAR invalidOperation*: Events.EventType; (* operation cannot be performed for the given combination of types (absolute or relative) *) incompatibleScales*: Events.EventType; (* the scales of the operands do not belong to the same family *) badCombination*: Events.EventType; (* SetValue or GetValue: given measure and unit do not belong to the same scaling system *) (* our interface to Operations *) opif: Operations.Interface; opcaps: Operations.CapabilitySet; (* ======= private procedures ===================================== *) PROCEDURE DummyConversion(from, to: Measure); BEGIN from.scale.if.assign(to, from); END DummyConversion; (* ======== exported procedures ==================================== *) PROCEDURE InitFamily*(family: Family; reference: Scale); BEGIN family.reference := reference; (* the reference scale becomes now a member of the family *) reference.family := family; reference.if.scaleToReference := DummyConversion; reference.if.referenceToScale := DummyConversion; END InitFamily; PROCEDURE Init*(scale: Scale; family: Family; if: Interface); (* reference scales are to be initialized with family = NIL *) BEGIN scale.if := if; scale.family := family; scale.head := NIL; scale.tail := NIL; scale.nextUnit := NIL; END Init; PROCEDURE InitUnit*(scale: Scale; unit: Unit; name: UnitName); VAR listp: UnitList; BEGIN unit.name := name; unit.scale := scale; NEW(listp); listp.unit := unit; listp.next := NIL; IF scale.head # NIL THEN scale.tail.next := listp; ELSE scale.head := listp; END; scale.tail := listp; END InitUnit; PROCEDURE CreateMeasure*(scale: Scale; VAR measure: Measure; type: SHORTINT); BEGIN scale.if.create(scale, measure, type = absolute); Operations.Init(measure, opif, opcaps); measure.scale := scale; measure.type := type; END CreateMeasure; PROCEDURE CreateAbsMeasure*(scale: Scale; VAR measure: Measure); (* init measure to the origin of the given system *) BEGIN CreateMeasure(scale, measure, absolute); END CreateAbsMeasure; PROCEDURE CreateRelMeasure*(scale: Scale; VAR measure: Measure); (* init relative measure to 0 *) BEGIN CreateMeasure(scale, measure, relative); END CreateRelMeasure; PROCEDURE ConvertMeasure*(scale: Scale; VAR measure: Measure); (* convert measure to the given scale which must belong to the same family as the original scale of measure *) VAR newMeasure: Measure; refMeasure: Measure; reference: Scale; BEGIN IF scale = measure.scale THEN (* trivial case -- nothing is to be done *) RETURN END; (* check that both scales belong to the same family *) ASSERT((scale.family # NIL) & (scale.family = measure.scale.family)); CreateMeasure(scale, newMeasure, measure.type); reference := scale.family.reference; CreateMeasure(reference, refMeasure, measure.type); measure.scale.if.scaleToReference(measure, refMeasure); scale.if.referenceToScale(refMeasure, newMeasure); measure := newMeasure; END ConvertMeasure; PROCEDURE GetReference*(family: Family; VAR reference: Scale); BEGIN reference := family.reference; END GetReference; PROCEDURE GetFamily*(scale: Scale; VAR family: Family); BEGIN family := scale.family; END GetFamily; PROCEDURE GetScaleOfUnit*(unit: Unit; VAR scale: Scale); BEGIN scale := unit.scale; END GetScaleOfUnit; PROCEDURE GetScale*(measure: Measure; VAR scale: Scale); BEGIN scale := measure.scale; END GetScale; PROCEDURE TraverseUnits*(scale: Scale); BEGIN scale.nextUnit := scale.head; END TraverseUnits; PROCEDURE NextUnit*(scale: Scale; VAR unit: Unit) : BOOLEAN; BEGIN IF scale.nextUnit # NIL THEN unit := scale.nextUnit.unit; scale.nextUnit := scale.nextUnit.next; RETURN TRUE ELSE RETURN FALSE END; END NextUnit; PROCEDURE GetName*(unit: Unit; VAR name: UnitName); BEGIN name := unit.name; END GetName; PROCEDURE GetValue*(measure: Measure; unit: Unit; VAR value: Value); VAR scale: Scale; BEGIN scale := measure.scale; ASSERT(unit.scale = scale); scale.if.getvalue(measure, unit, value); END GetValue; PROCEDURE SetValue*(measure: Measure; unit: Unit; value: Value); VAR scale: Scale; BEGIN scale := measure.scale; ASSERT(unit.scale = scale); scale.if.setvalue(measure, unit, value); END SetValue; PROCEDURE IsAbsolute*(measure: Measure) : BOOLEAN; BEGIN RETURN measure.type = absolute END IsAbsolute; PROCEDURE IsRelative*(measure: Measure) : BOOLEAN; BEGIN RETURN measure.type = relative END IsRelative; PROCEDURE MeasureType*(measure: Measure) : SHORTINT; BEGIN RETURN measure.type END MeasureType; (* ======== interface procedures for Operations ================= *) PROCEDURE CreateOperand(VAR op: Operations.Operand); (* at this time we don't know anything about the associated scale -- so we've have to delay this decision *) VAR measure: Measure; BEGIN NEW(measure); measure.type := undefined; measure.scale := NIL; Services.Init(measure, measureType); op := measure; Operations.Init(op, opif, {Operations.add..Operations.cmp}); END CreateOperand; PROCEDURE Assign(VAR target: Operations.Operand; source: Operations.Operand); BEGIN (*WITH source: Measure DO WITH target: Measure DO*) WITH source: Measure DO IF target IS Measure THEN (* WITH is replaced by IS -- noch *) (* target is already initialized but possibly to a dummy operand by CreateOperand *) IF target(Measure).type = undefined THEN (* type guard introduced *) (* init target with the scale of source *) CreateMeasure(source.scale, SYS.VAL(Measure, target), source.type); (* need to cast *) END; IF target(Measure).scale # source.scale THEN (* adapt scale type from source -- this could lead to a type guard failure if target is not of the appropiate type *) CreateMeasure(source.scale, SYS.VAL(Measure, target), source.type); END; IF target(Measure).type # source.type THEN (* adapt measure type from source *) CreateMeasure(target(Measure).scale, SYS.VAL(Measure, target), source.type); END; source.scale.if.assign(SYS.VAL(Measure, target), source); END; END; END Assign; PROCEDURE CheckCompatibility(op1, op2: Operations.Operand; VAR m1, m2: Measure); (* is needed by Op and Compare: both operands are checked to be members of the same family; if they have different scales of the same family a conversion is done; *) VAR scale1, scale2: Scale; BEGIN WITH op1: Measure DO WITH op2: Measure DO scale1 := op1.scale; scale2 := op2.scale; IF scale1 # scale2 THEN ASSERT((scale1.family # NIL) & (scale1.family = scale2.family)); (* convert both operands to the reference scale *) CreateMeasure(scale1.family.reference, m1, op1.type); scale1.if.scaleToReference(op1, m1); CreateMeasure(scale2.family.reference, m2, op2.type); scale2.if.scaleToReference(op2, m2); ELSE m1 := op1; m2 := op2; END; END; END; END CheckCompatibility; PROCEDURE Op(op: Operations.Operation; op1, op2: Operations.Operand; VAR result: Operations.Operand); VAR restype: SHORTINT; (* type of result -- set by CheckTypes *) m1, m2: Measure; PROCEDURE CheckTypes(VAR restype: SHORTINT); (* check operands for correct typing; sets restype to the correct result type; *) VAR ok: BOOLEAN; BEGIN (*WITH op1: Measure DO WITH op2: Measure DO*) IF op1 IS Measure THEN IF op2 IS Measure THEN CASE op OF | Operations.add: (* only abs + abs is invalid *) ok := (op1(Measure).type = relative) OR (op2(Measure).type = relative); IF op1(Measure).type = op2(Measure).type THEN (* both are relative *) restype := relative; ELSE (* exactly one absolute type is involved *) restype := absolute; END; | Operations.sub: (* only rel - abs is invalid *) ok := op1(Measure).type <= op2(Measure).type; IF op1(Measure).type # op2(Measure).type THEN (* abs - rel *) restype := absolute; ELSE (* abs - abs or rel - rel *) restype := relative; END; END; ASSERT(ok); (* invalid operation *) END; END; END CheckTypes; BEGIN (* Op *) (* result is already of type Measure; this is guaranteed by Operations *) IF result IS Measure THEN CheckTypes(restype); CheckCompatibility(op1, op2, m1, m2); CreateMeasure(m1.scale, SYS.VAL(Measure, result), restype); m1.scale.if.op(op, m1, m2, SYS.VAL(Measure, result)); END; END Op; PROCEDURE Compare(op1, op2: Operations.Operand) : INTEGER; VAR m1, m2: Measure; BEGIN CheckCompatibility(op1, op2, m1, m2); ASSERT(m1.type = m2.type); CheckCompatibility(op1, op2, m1, m2); RETURN m1.scale.if.compare(m1, m2) END Compare; PROCEDURE InitInterface; BEGIN NEW(opif); opif.create := CreateOperand; opif.assign := Assign; opif.op := Op; opif.compare := Compare; opcaps := {Operations.add, Operations.sub, Operations.cmp}; END InitInterface; BEGIN InitInterface; PersistentObjects.RegisterType(measureType, "Scales.Measure", "Operations.Operand", NIL); END ulmScales.