CDTextExtractionImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
by Ch. Jacobi, November 20, 1985 7:59:40 pm PST
Last Edited by: Jacobi, March 28, 1986 5:12:39 pm PST
DIRECTORY
Ascii,
Atom,
CD,
CDBasics,
CDCells,
CDCommandOps,
CDDirectory,
CDErrors,
CDInstances,
CDMenus,
CDProperties,
CDTextExtraction,
CDSequencer,
CDSymbolicObjects,
CDTexts,
CDOps,
IO,
PropertyLists,
Rope,
TerminalIO;
CDTextExtractionImpl: CEDAR PROGRAM
IMPORTS Ascii, Atom, CDBasics, CDCells, CDCommandOps, CDDirectory, CDErrors, CDInstances, CDMenus, CDProperties, CDSequencer, CDSymbolicObjects, CDTexts, CDOps, IO, Rope, TerminalIO
EXPORTS CDTextExtraction
SHARES CDProperties =
BEGIN
lorPropKey: ATOM = $TextExtraction;
PropRec: TYPE = RECORD [key: ATOM←NIL, value: REF←NIL, onOb: BOOLFALSE];
PutLorProp: PROC [x: REF, text: Rope.ROPE] =
BEGIN
lor: LIST OF Rope.ROPE
WITH CDProperties.GetProp[x, lorPropKey] SELECT FROM
rl: LIST OF Rope.ROPE => rl,
ENDCASE => NIL;
lor ← CONS[text, lor];
CDProperties.PutProp[x, lorPropKey, lor];
END;
PutPropRec: PROC [inst: CD.Instance, prop: PropRec] =
--puts the property onto the instance or the object
BEGIN
IF prop.onOb THEN {
IF inst.ob.class.inDirectory THEN
CDProperties.PutProp[onto: inst.ob, prop: prop.key, val: prop.value]
}
ELSE CDProperties.PutProp[onto: inst, prop: prop.key, val: prop.value]
END;
CheckPropRec: PROC [inst: CD.Instance, prop: PropRec] RETURNS [match: BOOL] =
--checks whether the instance or object has the same prop property
BEGIN
Similar: PROC [x, y: REF] RETURNS [BOOLFALSE] = {
--returns x and y are similar (considered ROPE's)
IF x=y THEN RETURN [TRUE];
IF ISTYPE[x, Rope.ROPE] AND ISTYPE[y, Rope.ROPE] THEN {
RETURN [ Rope.Equal[ NARROW[x, Rope.ROPE], NARROW[y, Rope.ROPE] ] ]
};
};
match ← prop.key#NIL AND
Similar[prop.value,
CDProperties.GetProp[from: (IF prop.onOb THEN inst.ob ELSE inst), prop: prop.key]
]
END;
ScanPropRec: PROC [ob: CD.Object] RETURNS [PropRec] =
--find out property description from text object
BEGIN
RemoveBlanks: PROC [r: Rope.ROPE] RETURNS [Rope.ROPE] = {
-- returns rope with leading and trailing spaces removed
RemoveTrailingBlanks: PROC [r: Rope.ROPE] RETURNS [Rope.ROPE] = INLINE {
-- returns rope with trailing spaces removed
n: INT ← r.Length[];
WHILE n>0 AND r.Fetch[n-1]=' DO n ← n-1 ENDLOOP;
RETURN [r.Substr[start: 0, len: n]];
};
RemoveLeadingBlanks: PROC [r: Rope.ROPE] RETURNS [Rope.ROPE] = INLINE {
-- returns rope with leading spaces removed
RETURN [r.Substr[start: r.SkipOver[skip: " "]]]
};
RETURN [RemoveLeadingBlanks[RemoveTrailingBlanks[r]]]
};
Scan: PROC [text: Rope.ROPE] RETURNS [pRec: PropRec ← [NIL, NIL]] = INLINE {
n: INT ← text.SkipTo[skip: ":"];
IF n<Rope.Length[text] THEN {
keyPart: Rope.ROPE ← RemoveBlanks[Rope.Substr[base: text, start: 0, len: n]];
text ← RemoveBlanks[text.Substr[start: n+1]];
IF keyPart.Length[]#0 THEN {
IF Rope.IsEmpty[text] THEN text ← NIL;
pRec ← [Atom.MakeAtom[keyPart], text];
}
}
ELSE {
text ← RemoveBlanks[text];
IF ~Rope.IsEmpty[text] THEN pRec ← [$SignalName, text]
}
};
Text: PROC [ob: CD.Object] RETURNS [t: Rope.ROPENIL] = {
WITH ob.specificRef SELECT FROM
tp: CDTexts.TextPtr => RETURN [t ← tp.text]
ENDCASE => NULL;
};
--ScanPropRec
RETURN [Scan[Text[ob]]]
END;
Public: PROC [key: ATOM] RETURNS [BOOLTRUE] =
-- returns: property key is accessible for interactive actions
BEGIN
IF key=NIL THEN RETURN [FALSE]
ELSE {
pType: CDProperties.PropertyProcs = CDProperties.FetchProcs[key];
RETURN [ pType=NIL OR ~pType.exclusive ];
}
END;
Kind: TYPE = {stuff, anyProp, topProp, ignore};
--anything is stuff except certain objects used by this module
KindOf: PROC [ob: CD.Object] RETURNS [kind: Kind←stuff] =
--determine usage of an object as property indicator...
BEGIN
IF ob.class.wireTyped THEN {
IF ob.layer<=CD.selectionLayer THEN kind ← ignore
}
ELSE IF ob.layer=CD.commentLayer THEN
WITH ob.specificRef SELECT FROM
tp: CDTexts.TextPtr => {
fn: Rope.ROPE ~ tp.cdFont.supposedName;
IF Ascii.Upper[fn.Fetch[fn.Length[]-1]]='I THEN kind ← ignore
ELSE IF fn.Length[]>=27 AND Rope.Equal["Xerox/TiogaFonts/", fn.Substr[len: 17], FALSE] THEN {
IF Rope.Equal["Helvetica", fn.Substr[17, 9], FALSE] THEN kind ← anyProp
ELSE IF Rope.Equal["TimesRoman", fn.Substr[17, 10], FALSE] THEN kind ← topProp
}
};
ENDCASE => NULL;
END;
Separation: TYPE = ARRAY Kind OF CD.InstanceList ← ALL[NIL];
Split: PROC [list: CD.InstanceList] RETURNS [s: Separation] =
--Splits instancelist into different lists (reduces number of instances to be considered!)
--SideEffect: removes lorProp properties
BEGIN
FOR l: CD.InstanceList ← list, l.rest WHILE l#NIL DO
k: Kind ← KindOf[l.first.ob];
s[k] ← CONS[l.first, s[k]];
CDProperties.PutInstanceProp[l.first, lorPropKey, NIL];
ENDLOOP;
END;
UseCPtr: PROC[design: CD.Design, ob: CD.Object] RETURNS [cp: CD.CellPtr←NIL] =
--gets an appropriate CellPtr... (or NIL)
BEGIN
IF ob=NIL THEN cp ← design^.actual.first.specific
ELSE IF CDCells.IsCell[ob] THEN cp ← NARROW[ob.specificRef]
ELSE IF ob.class.inDirectory THEN {
new: CD.Object; tm, cm: CDDirectory.DMode;
[new, tm, cm] ← CDDirectory.Expand[ob];
IF new#NIL AND new#ob AND tm#immutable THEN {
IF tm=ready THEN [] ← CDDirectory.Include[design, new];
cp ← UseCPtr[design, new];
}
};
END;
ExtractProperties: PUBLIC PROC [design: CD.Design, cell: CD.Object←NIL, specialMode: BOOL FALSE, checkOnly: BOOLFALSE] RETURNS [matches: INT𡤀, errors: INT𡤀] =
BEGIN
Message: PROC [text: Rope.ROPE, r, r2: CD.Rect ← CDBasics.universe] = {
blow: CD.Number ~ (IF design#NIL THEN design.technology.lambda ELSE 4);
[] ← CDErrors.IncludeMessage[design: design, ob: cell,
rect: CDBasics.Intersection[CDBasics.Extend[r, blow], CDBasics.Extend[r2, blow]],
message: text,
owner: $PropertyChecker
];
errors ← errors+1;
};
MatchFirst: PROC [tr: CD.Rect, l: CD.InstanceList] RETURNS [BOOL] = INLINE {
RETURN [
CDBasics.Intersect[tr, CDInstances.InstRectO[l.first]] AND
CDBasics.Intersect[tr, CDInstances.InstRectI[l.first]]
]
};
ExtractLORText: PROC [stuff: CD.InstanceList, ti: CD.Instance] =
BEGIN
tp: CDTexts.TextPtr ~ NARROW[ti.ob.specificRef];
tr: CD.Rect ~ CDInstances.InstRectI[ti];
match: BOOLFALSE;
FOR l: CD.InstanceList ← stuff, l.rest WHILE l#NIL DO
IF MatchFirst[tr, l] THEN {
match ← TRUE;
PutLorProp[l.first, tp.text]
}
ENDLOOP;
IF match THEN matches ← matches+1
ELSE Message["property does not match anything", tr];
END;
ExtractANYText: PROC [stuff: CD.InstanceList, ti: CD.Instance] =
BEGIN
pRec: PropRec ~ ScanPropRec[ti.ob];
tr: CD.Rect ~ CDInstances.InstRectI[ti];
IF Public[pRec.key] THEN {
match: BOOL FALSE;
FOR l: CD.InstanceList ← stuff, l.rest WHILE l#NIL DO
IF MatchFirst[tr, l] THEN {
match ← TRUE;
PutPropRec[l.first, pRec];
}
ENDLOOP;
IF match THEN matches ← matches+1
}
END;
CheckANYText: PROC [stuff: CD.InstanceList, ti: CD.Instance] =
BEGIN
pRec: PropRec ~ ScanPropRec[ti.ob];
tr: CD.Rect ~ CDInstances.InstRectI[ti];
IF Public[pRec.key] THEN {
match: BOOLFALSE;
FOR l: CD.InstanceList ← stuff, l.rest WHILE l#NIL DO
IF MatchFirst[tr, l] THEN {
match ← TRUE;
IF ~CheckPropRec[l.first, pRec] THEN
Message["property missmatch", tr, CDInstances.InstRectI[l.first]];
};
ENDLOOP;
IF ~match THEN Message["property does not match anything", tr]
}
ELSE Message["bad or protected property", tr]
END;
--ExtractProperties
s: Separation;
cPtr: CD.CellPtr ~ UseCPtr[design, cell];
IF cPtr=NIL THEN RETURN [errors ← 1];
CDErrors.RemoveMessages[design, cell, $PropertyChecker];
s ← Split[cPtr.contents];
IF specialMode THEN {
--special properties on instances
FOR l: CD.InstanceList ← s[anyProp], l.rest WHILE l#NIL DO
ExtractLORText[s[stuff], l.first]
ENDLOOP;
}
ELSE {
--normal properties on instances
IF ~checkOnly THEN
--extract ordinary props
FOR l: CD.InstanceList ← s[anyProp], l.rest WHILE l#NIL DO
ExtractANYText[s[stuff], l.first];
ENDLOOP;
--check ordinary props
FOR l: CD.InstanceList ← s[anyProp], l.rest WHILE l#NIL DO
CheckANYText[s[stuff], l.first]
ENDLOOP;
};
--properties on cell itself
IF ~checkOnly AND cell#NIL THEN {
rl: LIST OF Rope.ROPENIL;
FOR l: CD.InstanceList ← s[topProp], l.rest WHILE l#NIL DO
tp: CDTexts.TextPtr ~ NARROW[l.first.ob.specificRef];
rl ← CONS[tp.text, rl]
ENDLOOP;
CDProperties.PutObjectProp[cell, lorPropKey, rl];
};
END;
ExtractTopComm: PROC [comm: CDSequencer.Command] =
BEGIN
matches, errors: INT;
TerminalIO.WriteRope["extract properties in top level\n"];
[matches, errors] ← ExtractProperties[comm.design];
TerminalIO.WriteF[" %g texts used in top level; %g problem(s) found\n",
IO.int[matches], IO.int[errors]];
END;
ExtractSelComm: PROC [comm: CDSequencer.Command] =
BEGIN
inst: CD.Instance ← CDCommandOps.TheInstance[comm, "extract properties selected"];
IF inst#NIL THEN {
matches, errors: INT;
[matches, errors] ← ExtractProperties[comm.design, inst.ob];
TerminalIO.WriteF[" %g texts used in `%g'; %g problem(s) found\n",
IO.int[matches], IO.rope[CDOps.ObjectInfo[inst.ob]], IO.int[errors]];
}
END;
----------------
OkToRemoveProperties: PROC [i: CD.Instance] RETURNS [BOOLTRUE] =
BEGIN
IF CDSymbolicObjects.IsSymbolicOb[i.ob] THEN RETURN [FALSE];
IF i.ob.class.wireTyped THEN RETURN [i.ob.layer#CD.errorLayer];
END;
RemInstProps: PROC [i: CD.Instance] =
--removes all public properties of an instance
BEGIN
DoIt: PROC = {
copy: CD.PropList ← NIL;
FOR l: PropertyLists.PropList ← i.properties, l.rest WHILE l#NIL DO
WITH l.first.key SELECT FROM
a: ATOM => IF ~Public[a] THEN copy ← CONS[l.first, copy];
ENDCASE => copy ← CONS[l.first, copy];
ENDLOOP;
i.properties ← copy;
};
IF OkToRemoveProperties[i] THEN CDProperties.DoWithinLock[DoIt]
END;
RemovePublicProperties: PUBLIC PROC [design: CD.Design, ob: CD.Object←NIL] =
--removes all public properties
BEGIN
cPtr: CD.CellPtr ~ UseCPtr[design, ob];
IF cPtr#NIL THEN
FOR l: CD.InstanceList ← cPtr.contents, l.rest WHILE l#NIL DO
RemInstProps[l.first];
ENDLOOP;
END;
RemTopComm: PROC [comm: CDSequencer.Command] =
BEGIN
TerminalIO.WriteRope["remove all properties in top level\n"];
RemovePublicProperties[comm.design];
END;
RemSelInstPropsComm: PROC [comm: CDSequencer.Command] =
BEGIN
inst: CD.Instance ← CDCommandOps.TheInstance[comm, "remove properties of selected instance\n"];
IF inst#NIL THEN {
IF OkToRemoveProperties[inst] THEN RemInstProps[inst]
ELSE TerminalIO.WriteRope["don't remove the properties of this object\n"];
}
END;
IsAnyText: PROC [ob: CD.Object] RETURNS [yes: BOOLFALSE] = INLINE
-- whether ob is a text object
BEGIN
yes ← CDTexts.IsText[ob]
END;
SelectFromPropComm: PROC [comm: CDSequencer.Command] =
BEGIN
inst: CD.Instance ← CDCommandOps.TheInstance[comm, "select using property"];
IF inst#NIL THEN {
design: CD.Design ← comm.design;
pRec: PropRec;
cnt: INT ← 0;
IF IsAnyText[inst.ob] THEN pRec ← ScanPropRec[inst.ob]
ELSE IF inst.ob.class.wireTyped OR CDSymbolicObjects.IsSymbolicOb[inst.ob] THEN
pRec ← [$SignalName, CDProperties.GetProp[inst, $SignalName], FALSE]
ELSE {
TerminalIO.WriteRope[" not done: selection is neither text (Helvetica) nor rectangle\n"];
RETURN;
};
IF pRec.key=NIL THEN {
TerminalIO.WriteRope[" not done: property key is NIL\n"];
RETURN;
};
TerminalIO.WriteF[" key: %g, value: %g\n",
IO.atom[pRec.key],
IO.rope[(IF pRec.value=NIL THEN "NIL" ELSE CDCommandOps.ToRope[pRec.value])]
];
FOR l: CD.InstanceList ← CDOps.InstList[design], l.rest WHILE l#NIL DO
IF CheckPropRec[l.first, pRec] THEN {
l.first.selected ← TRUE;
CDOps.RedrawInstance[design, l.first, FALSE];
cnt ← cnt+1
};
ENDLOOP;
IF cnt>0 AND CDTexts.IsText[inst.ob] THEN {
inst.selected ← FALSE;
CDOps.RedrawInstance[design, inst];
};
TerminalIO.WriteF[" %g instance(s) selected\n", IO.int[cnt]];
};
END;
PropagateSignalNameComm: PROC [comm: CDSequencer.Command] =
BEGIN
inst: CD.Instance ~ CDCommandOps.TheInstance[comm, "propagate signal name"];
IF inst#NIL THEN {
design: CD.Design ~ comm.design;
foundL: CD.InstanceList ← LIST[inst];
no: INT ← 1;
value: REF ← CDProperties.GetProp[from: inst, prop: $SignalName];
IF value=NIL THEN TerminalIO.WriteRope[" not done: no signal name\n"]
ELSE {
WHILE foundL#NIL DO
curr: CD.Instance ~ foundL.first;
currR: CD.Rect ~ CDInstances.InstRectI[curr];
foundL ← foundL.rest;
FOR l: CD.InstanceList ← CDOps.InstList[design], l.rest WHILE l#NIL DO
IF ~l.first.selected AND l.first.ob.class.wireTyped AND l.first.ob.layer=curr.ob.layer THEN {
IF CDBasics.Intersect[currR, CDInstances.InstRectI[l.first]] THEN {
foundL ← CONS[l.first, foundL];
l.first.selected ← TRUE;
no ← no+1;
CDProperties.PutProp[l.first, $SignalName, value];
CDOps.RedrawInstance[design, l.first, FALSE];
};
};
ENDLOOP;
ENDLOOP;
TerminalIO.WriteF[" %g instance(s) selected\n", IO.int[no]];
};
};
END;
FindSelectedProperties: PROC [design: CD.Design] RETURNS [doList: LIST OF PropRec←NIL] =
--and tells its result with TerminalIO
BEGIN
Occurs: PROC [list: LIST OF PropRec, key: ATOM] RETURNS [yes: BOOL FALSE] = INLINE {
FOR pl: LIST OF PropRec ← list, pl.rest WHILE pl#NIL DO
IF pl.first.key=key THEN RETURN [yes ← TRUE]
ENDLOOP;
};
FOR l: CD.InstanceList ← CDOps.InstList[design], l.rest WHILE l#NIL DO
IF l.first.selected AND IsAnyText[l.first.ob] THEN {
pr: PropRec = ScanPropRec[l.first.ob];
IF ~Public[pr.key] THEN {
TerminalIO.WriteRope["unknown or restricted property\n"];
[] ← CDErrors.IncludeMessage[design: design, ob: NIL,
rect: CDInstances.InstRectI[l.first],
message: "bad property",
owner: $PropertyChecker
];
}
ELSE IF Occurs[doList, pr.key] THEN {
TerminalIO.WriteF[" ** property: %g, value: %g twice or contradicting! ignored\n",
IO.atom[pr.key],
IO.rope[(IF pr.value=NIL THEN "NIL" ELSE CDCommandOps.ToRope[pr.value])]
];
}
ELSE {
TerminalIO.WriteF[" property: %g, value: %g\n",
IO.atom[pr.key],
IO.rope[(IF pr.value=NIL THEN "NIL" ELSE CDCommandOps.ToRope[pr.value])]
];
doList ← CONS[pr, doList];
}
}
ENDLOOP;
END;
SetPropWithSelectionComm: PROC [comm: CDSequencer.Command] =
BEGIN
design: CD.Design ← comm.design;
doList: LIST OF PropRec;
TerminalIO.WriteRope["set properties using the selection\n"];
doList ← FindSelectedProperties[design];
IF doList=NIL THEN TerminalIO.WriteRope["no texts selected"]
ELSE {
FOR il: CD.InstanceList ← CDOps.InstList[design], il.rest WHILE il#NIL DO
IF il.first.selected AND ~IsAnyText[il.first.ob] THEN {
FOR pl: LIST OF PropRec ← doList, pl.rest WHILE pl#NIL DO
PutPropRec[il.first, pl.first]
ENDLOOP;
}
ENDLOOP;
TerminalIO.WriteRope[" --\n"];
};
END;
SetPropToPushedInCell: PROC [comm: CDSequencer.Command] =
BEGIN
design: CD.Design ← comm.design;
doList: LIST OF PropRec;
object: REF;
IF CDCells.IsPushedIn[design] THEN {
TerminalIO.WriteRope["set selected properties to the pushed in cell\n"];
object ← design.actual.first.mightReplace.ob;
}
ELSE {
TerminalIO.WriteRope["set selected properties to the design\n"];
object ← design;
};
doList ← FindSelectedProperties[design];
IF doList=NIL THEN TerminalIO.WriteRope["no texts selected"]
ELSE {
FOR pl: LIST OF PropRec ← doList, pl.rest WHILE pl#NIL DO
CDProperties.PutProp[object, pl.first.key, pl.first.value]
ENDLOOP;
TerminalIO.WriteRope[" --\n"];
};
END;
Register: PROC [entry: Rope.ROPE, p: CDSequencer.CommandProc, key: ATOM] =
BEGIN
CDMenus.CreateEntry[menu: $TextPropertyMenu, entry: entry, key: key];
CDSequencer.ImplementCommand[key: key, proc: p];
END;
[] ← CDProperties.RegisterProperty[lorPropKey, $chj0];
CDProperties.InstallProcs[
prop: lorPropKey,
new: CDProperties.PropertyProcsRec[exclusive: TRUE, makeCopy: CDProperties.CopyVal]
];
Register["extract properties top level I", ExtractTopComm, $ExtractPropTop];
Register["extract properties selected ob I", ExtractSelComm, $ExtractPropSel];
Register["remove all properties top level I", RemTopComm, $RemPropTop];
Register["remove all properties of sel inst", RemSelInstPropsComm, $RemPropSel];
Register["selected properties to selection I", SetPropWithSelectionComm, $SetPropWithSelection];
Register["selected properties to pushed in O", SetPropToPushedInCell, $SetPropPushed];
Register["select using property I", SelectFromPropComm, $SelectFromProp];
Register["propagate SignalName I", PropagateSignalNameComm, $PropagateSignalName];
END.