MODULE XMLStyle; (** Stefan Walthert *) (** AUTHOR "swalthert"; PURPOSE ""; *) 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; (* compute number of rule sets in style sheet *) 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); (* store rule sets of style sheet in array *) 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; (* sort selRS by specifity of selectors (selRS[].selector.GetSpecifity(a, b, c)) and their original order *) HeapSort(selRS^); (* attach style of unimportant declarations *) 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; (* attach style of important declarations *) 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 (* not implemented *) RETURN FALSE ELSIF rel = CSS2.Dashmatch THEN (* not implemented *) 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.