MODULE XMLStyle;
IMPORT
XMLObjects, CSS2, XML, XMLComponents;
TYPE
String= CSS2.String;
SelectorRuleSet = RECORD
selector: CSS2.Selector;
ruleSet: CSS2.RuleSet;
order: LONGINT
END;
PROCEDURE AttachStyle*(root: XML.Element; css: CSS2.StyleSheet);
VAR selRS: POINTER TO ARRAY OF SelectorRuleSet; noSel: LONGINT;
ruleSets, selectors, simpleSelectors: XMLObjects.Enumerator; ruleSet, selector, simpleSelector: ANY;
propChanger: XMLComponents.PropertyChanger; hasDynamic: BOOLEAN;
BEGIN
IF (root = NIL) OR (css = NIL) THEN RETURN END;
noSel := 0;
ruleSets := css.GetRuleSets();
WHILE ruleSets.HasMoreElements() DO
ruleSet := ruleSets.GetNext();
selectors := ruleSet(CSS2.RuleSet).GetSelectors();
WHILE selectors.HasMoreElements() DO
selector := selectors.GetNext();
INC(noSel)
END
END;
NEW(selRS, noSel);
noSel := 0;
ruleSets := css.GetRuleSets();
WHILE ruleSets.HasMoreElements() DO
ruleSet := ruleSets.GetNext();
selectors := ruleSet(CSS2.RuleSet).GetSelectors();
WHILE selectors.HasMoreElements() DO
selector := selectors.GetNext();
selRS[noSel].selector := selector(CSS2.Selector);
selRS[noSel].ruleSet := ruleSet(CSS2.RuleSet);
selRS[noSel].order := noSel;
INC(noSel)
END
END;
HeapSort(selRS^);
FOR noSel := 0 TO LEN(selRS) - 1 DO
IF selRS[noSel].ruleSet.HasNotImportantDeclarations() THEN
simpleSelectors := selRS[noSel].selector.GetSimpleSelectors();
IF simpleSelectors.HasMoreElements() THEN
simpleSelector := simpleSelectors.GetNext(); NEW(propChanger); hasDynamic := FALSE;
FindMatch(root, simpleSelector(CSS2.SimpleSelector), selRS[noSel].ruleSet,
propChanger, hasDynamic, FALSE)
END
END
END;
FOR noSel := 0 TO LEN(selRS) - 1 DO
IF selRS[noSel].ruleSet.HasImportantDeclarations() THEN
simpleSelectors := selRS[noSel].selector.GetSimpleSelectors();
IF simpleSelectors.HasMoreElements() THEN
simpleSelector := simpleSelectors.GetNext(); NEW(propChanger); hasDynamic := FALSE;
FindMatch(root, simpleSelector(CSS2.SimpleSelector), selRS[noSel].ruleSet,
propChanger, hasDynamic, TRUE)
END
END
END
END AttachStyle;
PROCEDURE HeapSort(VAR selRS: ARRAY OF SelectorRuleSet);
VAR left, right: LONGINT; elem: SelectorRuleSet;
PROCEDURE Sift(left, right: LONGINT);
VAR i, j: LONGINT; elem: SelectorRuleSet;
PROCEDURE Less(VAR elem1, elem2: SelectorRuleSet): BOOLEAN;
VAR a1, a2, b1, b2, c1, c2: LONGINT;
BEGIN
elem1.selector.GetSpecifity(a1, b1, c1); elem2.selector.GetSpecifity(a2, b2, c2);
RETURN (a1 < a2) OR ((a1 = a2) & (b1 < b2)) OR ((a1 = a2) & (b1 = b2) & (c1 < c2))
OR ((a1 = a2) & (b1 = b2) & (c1 = c2) & (elem1.order < elem2.order))
END Less;
BEGIN
i := left; j := 2 * left; elem := selRS[left];
IF (j < right) & Less(selRS[j], selRS[j + 1]) THEN INC(j) END;
WHILE (j <= right) & Less(elem, selRS[j]) DO
selRS[i] := selRS[j]; i := j; j := 2 * j;
IF (j < right) & Less(selRS[j], selRS[j + 1]) THEN INC(j) END;
END;
selRS[i] := elem
END Sift;
BEGIN
left := LEN(selRS) DIV 2 + 1; right := LEN(selRS) - 1;
WHILE left > 0 DO DEC(left); Sift(left, right) END;
WHILE right > 0 DO
elem := selRS[0]; selRS[0] := selRS[right]; selRS[right] := elem;
DEC(right); Sift(left, right)
END
END HeapSort;
PROCEDURE FindMatch(elem: XML.Element; simpleSelector: CSS2.SimpleSelector; ruleSet: CSS2.RuleSet;
propChanger: XMLComponents.PropertyChanger; VAR hasDynamic: BOOLEAN; important: BOOLEAN);
VAR children: XMLObjects.Enumerator; child: ANY; nextSimpleSelector: CSS2.SimpleSelector; sibling: XML.Element;
match: BOOLEAN;
BEGIN
nextSimpleSelector := simpleSelector.GetNext();
match := MatchSimpleSelector(elem, simpleSelector, propChanger, hasDynamic);
IF (nextSimpleSelector = NIL) & match & (elem IS XMLComponents.CSS2Component) THEN
IF hasDynamic THEN propChanger.SetChangingComponent(elem(XMLComponents.CSS2Component), ruleSet)
ELSE AttachStyleToComponent(elem(XMLComponents.CSS2Component), ruleSet, important)
END
END;
IF (nextSimpleSelector # NIL) & match THEN
CASE nextSimpleSelector.GetCombinator() OF
| CSS2.Sibling:
sibling := elem.GetNextSibling();
IF sibling # NIL THEN
FindMatch(sibling, nextSimpleSelector, ruleSet, propChanger.Copy(), hasDynamic, important)
END
| CSS2.Child, CSS2.Descendant:
children := elem.GetContents();
IF SelectFirstChild(nextSimpleSelector) THEN
child := children.GetNext();
IF (child # NIL) & (child IS XML.Element) THEN
FindMatch(child(XML.Element), nextSimpleSelector, ruleSet, propChanger.Copy(), hasDynamic, important)
END
ELSE
WHILE children.HasMoreElements() DO
child := children.GetNext();
IF child IS XML.Element THEN
FindMatch(child(XML.Element), nextSimpleSelector, ruleSet, propChanger.Copy(), hasDynamic, important)
END
END
END
ELSE
END
END;
IF simpleSelector.GetCombinator() = CSS2.Descendant THEN
children := elem.GetContents();
WHILE children.HasMoreElements() DO
child := children.GetNext();
IF child IS XML.Element THEN
FindMatch(child(XML.Element), simpleSelector, ruleSet, propChanger.Copy(), hasDynamic, important)
END
END
END
END FindMatch;
PROCEDURE MatchSimpleSelector(elem: XML.Element; simpleSelector: CSS2.SimpleSelector;
propChanger: XMLComponents.PropertyChanger; VAR hasDynamic: BOOLEAN): BOOLEAN;
VAR s1, s2: String; enum: XMLObjects.Enumerator; c: ANY; match: BOOLEAN;
BEGIN
s1 := elem.GetName();
s2 := simpleSelector.GetElementName();
IF (s2 = NIL) OR (s2^ = "*") OR (s1^ = s2^) THEN
enum := simpleSelector.GetSubSelectors();
match := TRUE;
WHILE enum.HasMoreElements() & match DO
c := enum.GetNext();
match := MatchSubSelector(elem, c(CSS2.SubSelector), propChanger, hasDynamic)
END;
RETURN match
ELSE RETURN FALSE
END
END MatchSimpleSelector;
PROCEDURE MatchSubSelector(elem: XML.Element; subSelector: CSS2.SubSelector;
propChanger: XMLComponents.PropertyChanger; VAR hasDynamic: BOOLEAN): BOOLEAN;
VAR s1, s2: String; rel: SHORTINT; attribute: XML.Attribute;
BEGIN
IF subSelector IS CSS2.Id THEN
s1 := elem.GetId(); s2 := subSelector(CSS2.Id).GetValue();
RETURN (s1 # NIL) & (s2 # NIL) & (s1^ = s2^)
ELSIF subSelector IS CSS2.Class THEN
WITH subSelector: CSS2.Class DO
attribute := elem.GetAttribute("class");
IF attribute # NIL THEN
s1 := attribute.GetValue();
s2 := subSelector.GetValue();
RETURN s1^ = s2^
ELSE
RETURN FALSE
END
END
ELSIF subSelector IS CSS2.Attribute THEN
WITH subSelector: CSS2.Attribute DO
s1 := subSelector.GetName();
rel := subSelector.GetRelation();
attribute := elem.GetAttribute(s1^);
IF attribute # NIL THEN
IF rel = CSS2.Undefined THEN
RETURN TRUE
ELSE
s1 := attribute.GetValue();
s2 := subSelector.GetValue();
IF rel = CSS2.Equal THEN
RETURN s1^ = s2^
ELSIF rel = CSS2.Includes THEN
RETURN FALSE
ELSIF rel = CSS2.Dashmatch THEN
RETURN FALSE
END
END
ELSE
RETURN FALSE
END
END
ELSIF subSelector IS CSS2.Pseudo THEN
s1 := subSelector(CSS2.Pseudo).GetType();
IF s1 = NIL THEN
RETURN FALSE
ELSIF s1^ = "first-child" THEN
RETURN TRUE
ELSE
IF elem IS XMLComponents.VisualComponent THEN
hasDynamic := TRUE;
propChanger.AddListenedComponent(elem(XMLComponents.CSS2Component), s1^);
RETURN TRUE
ELSE
RETURN FALSE
END
END
ELSE
END
END MatchSubSelector;
PROCEDURE AttachStyleToComponent(comp: XMLComponents.CSS2Component; ruleSet: CSS2.RuleSet; important: BOOLEAN);
VAR declarations: XMLObjects.Enumerator; declaration: ANY;
BEGIN
declarations := ruleSet.GetDeclarations();
WHILE declarations.HasMoreElements() DO
declaration := declarations.GetNext();
IF declaration(CSS2.Declaration).IsImportant() = important THEN
comp.properties.SetValue(declaration(CSS2.Declaration))
END
END
END AttachStyleToComponent;
PROCEDURE SelectFirstChild(simpleSelector: CSS2.SimpleSelector): BOOLEAN;
VAR subSelectors: XMLObjects.Enumerator; c: ANY; s: String;
BEGIN
subSelectors := simpleSelector.GetSubSelectors();
WHILE subSelectors.HasMoreElements() DO
c := subSelectors.GetNext();
IF (c IS CSS2.Pseudo) THEN
s := c(CSS2.Pseudo).GetType();
IF (s # NIL) & (s^ = "first-child") THEN RETURN TRUE END
END
END;
RETURN FALSE
END SelectFirstChild;
END XMLStyle.