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: BOOL←FALSE];
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 [
BOOL←
FALSE] = {
--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.
ROPE←
NIL] = {
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 [
BOOL←
TRUE] =
-- 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:
BOOL←
FALSE]
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: BOOL ← FALSE;
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: BOOL ← FALSE;
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.ROPE←NIL;
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 [
BOOL←
TRUE] =
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:
BOOL←
FALSE] =
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.