Initial commit
[profile/ivi/openjade.git] / style / ProcessContext.cxx
1 // Copyright (c) 1996 James Clark
2 // See the file copying.txt for copying permission.
3
4 #include "stylelib.h"
5 #include "ProcessContext.h"
6 #include "Interpreter.h"
7 #include "InterpreterMessages.h"
8 #include "SosofoObj.h"
9 #include "macros.h"
10 #include "IListIter.h"
11
12 #ifdef DSSSL_NAMESPACE
13 namespace DSSSL_NAMESPACE {
14 #endif
15
16 ProcessContext::ProcessContext(Interpreter &interp, FOTBuilder &fotb)
17 : Collector::DynamicRoot(interp), vm_(interp), flowObjLevel_(0), havePageType_(0), connectableStackLevel_(0)
18 {
19   connectionStack_.insert(new Connection(&fotb));
20 }
21
22 void ProcessContext::process(const NodePtr &node)
23 {
24   Interpreter &interp = *vm_.interp;
25   StyleObj *style = interp.initialStyle();
26   if (style) {
27     currentStyleStack().push(style, vm(), currentFOTBuilder());
28     currentFOTBuilder().startSequence();
29   }
30   processNode(node, interp.initialProcessingMode());
31   if (style) {
32     currentFOTBuilder().endSequence();
33     currentStyleStack().pop();
34   }
35 }
36
37 void ProcessContext::processNodeSafe(const NodePtr &nodePtr,
38                                      const ProcessingMode *processingMode,
39                                      bool chunk)
40 {
41   unsigned long elementIndex;
42   if (nodePtr->elementIndex(elementIndex) == accessOK) {
43     unsigned groveIndex = nodePtr->groveIndex();
44     for (size_t i = 0; i < nodeStack_.size(); i++) {
45       const NodeStackEntry &nse = nodeStack_[i];
46       if (nse.elementIndex == elementIndex
47           && nse.groveIndex == groveIndex
48           && nse.processingMode == processingMode) {
49         vm_.interp->setNodeLocation(nodePtr);
50         vm_.interp->message(InterpreterMessages::processNodeLoop);
51         return;
52       }
53     }
54     nodeStack_.resize(nodeStack_.size() + 1);
55     NodeStackEntry &e = nodeStack_.back();
56     e.elementIndex = elementIndex;
57     e.groveIndex = groveIndex;
58     e.processingMode = processingMode;
59     processNode(nodePtr, processingMode, chunk);
60     nodeStack_.resize(nodeStack_.size() - 1);
61   }
62   else
63     processNode(nodePtr, processingMode, chunk);
64 }
65
66 void ProcessContext::processNode(const NodePtr &nodePtr,
67                                  const ProcessingMode *processingMode,
68                                  bool chunk)
69 {
70   ASSERT(processingMode != 0);
71   GroveString str;
72   if (nodePtr->charChunk(*vm_.interp, str) == accessOK) 
73     currentFOTBuilder().charactersFromNode(nodePtr, str.data(), chunk ? str.size() : 1);
74   else {
75     EvalContext::CurrentNodeSetter cns(nodePtr, processingMode, vm());
76     ProcessingMode::Specificity saveSpecificity(matchSpecificity_);
77     matchSpecificity_ = ProcessingMode::Specificity();
78     bool hadStyle = 0;
79     currentFOTBuilder().startNode(nodePtr, processingMode->name());
80     for (;;) {
81       const ProcessingMode::Rule *rule
82        = vm().processingMode->findMatch(nodePtr, *vm_.interp, *vm_.interp,
83                                         matchSpecificity_);
84       if (!rule) {
85         if (hadStyle) {
86           currentStyleStack().pushEnd(vm(), currentFOTBuilder());
87           currentFOTBuilder().startSequence();
88         }
89         processChildren(processingMode);
90         break;
91       }
92       if (!matchSpecificity_.isStyle()) {
93         SosofoObj *sosofoObj;
94         InsnPtr insn;
95         rule->action().get(insn, sosofoObj);
96         if (hadStyle) {
97           currentStyleStack().pushEnd(vm(), currentFOTBuilder());
98           currentFOTBuilder().startSequence();
99         }
100         if (sosofoObj)
101           sosofoObj->process(*this);
102         else {
103           ELObj *obj = vm().eval(insn.pointer());
104           if (vm_.interp->isError(obj)) {
105             if (processingMode->name().size() == 0)
106               processChildren(processingMode);
107           }
108           else {
109             ELObjDynamicRoot protect(*vm_.interp, obj);
110             ((SosofoObj *)obj)->process(*this);
111           }
112         }
113         break;
114       }
115       SosofoObj *sosofoObj;
116       InsnPtr insn;
117       rule->action().get(insn, sosofoObj);
118       ELObj *obj = vm().eval(insn.pointer());
119       if (!vm_.interp->isError(obj)) {
120         if (!hadStyle) {
121           currentStyleStack().pushStart();
122           hadStyle = 1;
123         }
124         currentStyleStack().pushContinue((StyleObj *)obj, rule, nodePtr, vm_.interp);
125       }
126     }
127     if (hadStyle) {
128       currentFOTBuilder().endSequence();
129       currentStyleStack().pop();
130     }
131     currentFOTBuilder().endNode();
132     matchSpecificity_ = saveSpecificity;
133   }
134 }
135
136 void ProcessContext::nextMatch(StyleObj *overridingStyle)
137 {
138   ProcessingMode::Specificity saveSpecificity(matchSpecificity_);
139   StyleObj *saveOverridingStyle = vm().overridingStyle;
140   if (overridingStyle)
141     vm().overridingStyle = overridingStyle;
142   const ProcessingMode::Rule *rule
143    = vm().processingMode->findMatch(vm().currentNode, *vm_.interp, *vm_.interp,
144                                     matchSpecificity_);
145   if (rule) {
146     ASSERT(!matchSpecificity_.isStyle());
147     SosofoObj *sosofoObj;
148     InsnPtr insn;
149     rule->action().get(insn, sosofoObj);
150     if (sosofoObj)
151       sosofoObj->process(*this);
152     else {
153       ELObj *obj = vm().eval(insn.pointer());
154       if (vm_.interp->isError(obj)) 
155         processChildren(vm().processingMode);
156       else {
157         ELObjDynamicRoot protect(*vm_.interp, obj);
158         ((SosofoObj *)obj)->process(*this);
159       }
160     }
161   }
162   else
163     processChildren(vm().processingMode);
164   vm().overridingStyle = saveOverridingStyle;
165   matchSpecificity_ = saveSpecificity;
166 }
167
168 void ProcessContext::processChildren(const ProcessingMode *processingMode)
169 {
170   if (vm().currentNode.assignFirstChild() == accessOK) {
171     do {
172       processNode(vm().currentNode, processingMode);
173     } while (vm().currentNode.assignNextChunkSibling() == accessOK);
174   }
175   else if (vm().currentNode->getDocumentElement(vm().currentNode) == accessOK)
176     processNode(vm().currentNode, processingMode);
177 }
178
179 inline
180 bool isWhiteSpace(Char c, Interpreter &interp)
181 {
182   return 
183     interp.charProperty(
184       interp.makeStringC("input-whitespace?"), c, Location(), 0)->isTrue(); 
185 }
186
187 static
188 bool onlyWhiteSpaceFollows(const NodePtr &node, Interpreter &interp)
189 {
190   NodePtr tem;
191   if (node->nextChunkSibling(tem) == accessOK) {
192     do {
193       GroveString str;
194       if (tem->charChunk(interp, str) == accessOK) {
195         for (size_t i = 0; i < str.size(); i++)
196           if (!isWhiteSpace(str[i], interp))
197             return 0;
198       }
199       else if (tem->getGi(str) == accessOK)
200         return 0;
201     } while (tem.assignNextChunkSibling() == accessOK);
202   }
203   return 1;
204 }
205
206 void ProcessContext::processChildrenTrim(const ProcessingMode *processingMode)
207 {
208   if (vm().currentNode.assignFirstChild() == accessOK) {
209     bool atStart = 1;
210     do {
211       NodePtr curNode = vm().currentNode;
212       GroveString str;
213       if (curNode->charChunk(*vm().interp, str) == accessOK) {
214         if (atStart) {
215           const Char *s = str.data();
216           size_t n = str.size();
217           for (; n > 0; n--, s++) {
218             if (!isWhiteSpace(*s, *vm().interp))
219               break;
220           }
221           if (n == 0)
222             str.assign(0, 0);
223           else {
224             atStart = 0;
225             if (s != str.data()) {
226               if (curNode->followSiblingRef(str.size() - (n + 1), curNode) != accessOK)
227                 CANNOT_HAPPEN();
228               str.assign(s, n);
229             }
230           }
231         }
232         if (str.size()) {
233           if (isWhiteSpace(str[str.size() - 1], *vm().interp)
234               && onlyWhiteSpaceFollows(curNode, *vm().interp)) {
235             for (size_t n = str.size() - 1; n > 0; n--) {
236               if (!isWhiteSpace(str[n - 1], *vm().interp)) {
237                 currentFOTBuilder().charactersFromNode(curNode, str.data(), n);
238                 return;
239               }
240             }
241             return;
242           }
243           currentFOTBuilder().charactersFromNode(curNode, str.data(), str.size());
244         }
245       }
246       else {
247         if (atStart && vm().currentNode->getGi(str) == accessOK)
248           atStart = 0;
249         processNode(vm().currentNode, processingMode);
250       }
251     } while (vm().currentNode.assignNextChunkSibling() == accessOK);
252   }
253   else if (vm().currentNode->getDocumentElement(vm().currentNode) == accessOK)
254     processNode(vm().currentNode, processingMode);
255 }
256
257 void ProcessContext::startConnection(SymbolObj *label, const Location &loc)
258 {
259   unsigned connLevel = connectableStackLevel_;
260   for (IListIter<Connectable> iter(connectableStack_); !iter.done(); iter.next(), --connLevel) {
261     Connectable *conn = iter.cur();
262     for (size_t i = 0; i < conn->ports.size(); i++) {
263       Port &port = conn->ports[i];
264       for (size_t j = 0; j < port.labels.size(); j++)
265         if (port.labels[j] == label) {
266           restoreConnection(connLevel, i);
267           return;
268         }
269     }
270     for (size_t i = 0; i < conn->principalPortLabels.size(); i++)
271       if (conn->principalPortLabels[i] == label) {
272         restoreConnection(connLevel, size_t(-1));
273         return;
274       }
275   }
276   vm().interp->setNextLocation(loc);
277   vm().interp->message(InterpreterMessages::badConnection,
278                        StringMessageArg(*label->name()));
279   connectionStack_.head()->nBadFollow++;
280 }
281
282
283 void ProcessContext::endConnection()
284 {
285   if (inTableRow() && tableStack_.head()->rowConnectableLevel == connectableStackLevel_)
286     endTableRow();
287   if (connectionStack_.head()->nBadFollow > 0)
288     connectionStack_.head()->nBadFollow--;
289   else {
290     currentFOTBuilder().endNode();
291     Port *port = connectionStack_.head()->port;
292     if (port && --(port->connected) == 0) {
293       while (!port->saveQueue.empty()) {
294         SaveFOTBuilder *saved = port->saveQueue.get();
295         saved->emit(*port->fotb);
296         delete saved;
297       }
298     }
299     delete connectionStack_.get();
300   }
301 }
302
303 void ProcessContext::restoreConnection(unsigned connectableLevel, size_t portIndex)
304 {
305   unsigned connLevel = connectableStackLevel_;
306   IListIter<Connectable> iter(connectableStack_);
307   for (; connLevel != connectableLevel; iter.next(), --connLevel)
308     ;
309   Connectable *conn = iter.cur();
310   if (portIndex != size_t(-1)) {
311     Port &port = conn->ports[portIndex];
312     Connection *c = new Connection(conn->styleStack, &port, connLevel);
313     if (port.connected) {
314       port.connected++;
315       SaveFOTBuilder *save = new SaveFOTBuilder(vm().currentNode,
316                                                 vm().processingMode->name());
317       c->fotb = save;
318       port.saveQueue.append(save);
319     }
320     else {
321       c->fotb = port.fotb;
322       port.connected = 1;
323     }
324     connectionStack_.insert(c);
325     currentFOTBuilder().startNode(vm().currentNode,
326                                   vm().processingMode->name());
327   }
328   else {
329     Connection *c = new Connection(conn->styleStack, 0, connLevel);
330     if (conn->flowObjLevel == flowObjLevel_) {
331       c->fotb = &currentFOTBuilder();
332     }
333     else {
334       SaveFOTBuilder *save = new SaveFOTBuilder(vm().currentNode,
335                                                 vm().processingMode->name());
336       c->fotb = save;
337       if (conn->flowObjLevel >= principalPortSaveQueues_.size())
338         principalPortSaveQueues_.resize(conn->flowObjLevel + 1);
339       principalPortSaveQueues_[conn->flowObjLevel].append(save);
340     }
341     connectionStack_.insert(c);
342     currentFOTBuilder().startNode(vm().currentNode,
343                                   vm().processingMode->name());
344   }
345 }
346
347 void ProcessContext::endFlowObj()
348 {
349   flowObjLevel_--;
350   if (flowObjLevel_ < principalPortSaveQueues_.size()) {
351     IQueue<SaveFOTBuilder> &saveQueue = principalPortSaveQueues_[flowObjLevel_];
352     while (!saveQueue.empty()) {
353       SaveFOTBuilder *saved = saveQueue.get();
354       saved->emit(currentFOTBuilder());
355       delete saved;
356     }
357   }
358 }
359
360
361 ProcessContext::Connection::Connection(const StyleStack &s, Port *p, unsigned connLevel)
362 : styleStack(s), port(p), nBadFollow(0), connectableLevel(connLevel)
363 {
364 }
365
366 ProcessContext::Connection::Connection(FOTBuilder *f)
367 : fotb(f), port(0), nBadFollow(0), connectableLevel(0)
368 {
369 }
370
371 void ProcessContext::pushPorts(bool,
372                                const Vector<SymbolObj *> &labels,
373                                const Vector<FOTBuilder *> &fotbs)
374 {
375   Connectable *c = new Connectable(labels.size(), currentStyleStack(), flowObjLevel_);
376   connectableStack_.insert(c);
377   for (size_t i = 0; i < labels.size(); i++) {
378     c->ports[i].labels.push_back(labels[i]);
379     c->ports[i].fotb = fotbs[i];
380   }
381   connectableStackLevel_++;
382   // FIXME deal with !hasPrincipalPort
383 }
384
385 void ProcessContext::popPorts()
386 {
387   connectableStackLevel_--;
388   delete connectableStack_.get();
389 }
390
391 void ProcessContext::pushPrincipalPort(FOTBuilder* principalPort)
392 {
393   connectionStack_.insert(new Connection(principalPort));
394 }
395
396 void ProcessContext::popPrincipalPort()
397 {
398   delete connectionStack_.get();
399 }
400
401 void ProcessContext::startDiscardLabeled(SymbolObj *label)
402 {
403   startFlowObj();
404   Connectable *c = new Connectable(1, currentStyleStack(), flowObjLevel_);
405   connectableStack_.insert(c);
406   c->ports[0].labels.push_back(label);
407   c->ports[0].fotb = &ignoreFotb_;
408 }
409
410 void ProcessContext::endDiscardLabeled()
411 {
412   delete connectableStack_.get();
413   endFlowObj();
414 }
415
416 void ProcessContext::startMapContent(ELObj *contentMap, const Location &loc)
417 {
418   bool badFlag = 0;
419   if (!connectableStack_.head() || connectableStack_.head()->flowObjLevel != flowObjLevel_)
420     connectableStack_.insert(new Connectable(0, currentStyleStack(), flowObjLevel_));
421   Connectable &conn = *connectableStack_.head();
422   Vector<SymbolObj *> portNames(conn.ports.size());
423   for (size_t i = 0; i < conn.ports.size(); i++) {
424     portNames[i] = conn.ports[i].labels[0];
425     conn.ports[i].labels.clear();
426   }
427   for (;;) {
428     if (contentMap->isNil())
429       break;
430     PairObj *tem = contentMap->asPair();
431     if (!tem) {
432       badContentMap(badFlag, loc);
433       break;
434     }
435     ELObj *entry = tem->car();
436     contentMap = tem->cdr();
437     tem = entry->asPair();
438     if (tem) {
439       SymbolObj *label = tem->car()->asSymbol();
440       if (label) {
441         // FIXME check if label already specified
442         tem = tem->cdr()->asPair();
443         if (tem) {
444           SymbolObj *port = tem->car()->asSymbol();
445           if (port) {
446             for (size_t i = 0; i < portNames.size(); i++)
447               if (portNames[i] == port) {
448                 conn.ports[i].labels.push_back(label);
449                 port = 0;
450                 break;
451               }
452             if (port) {
453               vm().interp->setNextLocation(loc);
454               vm().interp->message(InterpreterMessages::contentMapBadPort,
455                                    StringMessageArg(*port->name()));
456             }
457           }
458           else if (tem->car() == vm().interp->makeFalse())
459             conn.principalPortLabels.push_back(label);
460           else
461             badContentMap(badFlag, loc);
462           if (!tem->cdr()->isNil())
463             badContentMap(badFlag, loc);
464         }
465         else
466           badContentMap(badFlag, loc);
467       }
468       else
469         badContentMap(badFlag, loc);
470     }
471     else
472       badContentMap(badFlag, loc);
473   }
474 }
475
476 void ProcessContext::endMapContent()
477 {
478   if (connectableStack_.head()->ports.size() == 0)
479     delete connectableStack_.get();
480 }
481  
482 void ProcessContext::badContentMap(bool &badFlag, const Location &loc)
483 {
484   if (badFlag)
485     return;
486   badFlag = 1;
487   vm().interp->setNextLocation(loc);
488   vm().interp->message(InterpreterMessages::badContentMap);
489 }
490
491 ProcessContext::Connectable::Connectable(int nPorts, const StyleStack &ss, unsigned fol)
492 : ports(nPorts), styleStack(ss), flowObjLevel(fol)
493 {
494 }
495
496 ProcessContext::Port::Port()
497 : connected(0), fotb(0)
498 {
499 }
500
501 void ProcessContext::trace(Collector &c) const
502 {
503   for (IListIter<Connection> iter(connectionStack_); !iter.done(); iter.next())
504     iter.cur()->styleStack.trace(c);
505   for (IListIter<Connectable> iter(connectableStack_); !iter.done(); iter.next())
506     iter.cur()->styleStack.trace(c);
507   for (IListIter<Table> iter(tableStack_); !iter.done(); iter.next()) {
508     c.trace(iter.cur()->rowStyle);
509     Vector<Vector<StyleObj *> > &styles = iter.cur()->columnStyles;
510     for (size_t i = 0; i < styles.size(); i++)
511       for (size_t j = 0; j < styles[i].size(); j++)
512         c.trace(styles[i][j]);
513   }
514 }
515 SosofoObj *SosofoObj::asSosofo()
516 {
517   return this;
518 }
519
520 bool SosofoObj::tableBorderStyle(StyleObj *&)
521 {
522   return 0;
523 }
524
525 bool SosofoObj::isRule()
526 {
527   return 0;
528 }
529
530 bool SosofoObj::ruleStyle(ProcessContext &, StyleObj *&)
531 {
532   return 0;
533 }
534
535 bool SosofoObj::isCharacter()
536 {
537   return 0;
538 }
539
540 bool SosofoObj::characterStyle(ProcessContext &, StyleObj *&, FOTBuilder::CharacterNIC &)
541 {
542   return 0;
543 }
544
545 AppendSosofoObj *AppendSosofoObj::asAppendSosofo()
546 {
547   return this;
548 }
549
550 void AppendSosofoObj::process(ProcessContext &context)
551 {
552   for (size_t i = 0; i < v_.size(); i++)
553     v_[i]->process(context);
554 }
555
556 void AppendSosofoObj::traceSubObjects(Collector &c) const
557 {
558   for (size_t i = 0; i < v_.size(); i++)
559     c.trace(v_[i]);
560 }
561
562 void LiteralSosofoObj::process(ProcessContext &context)
563 {
564   const Char *s;
565   size_t n;
566   if (str_->stringData(s, n))
567     context.currentFOTBuilder().characters(s, n);
568 }
569
570 void LiteralSosofoObj::traceSubObjects(Collector &c) const
571 {
572   c.trace(str_);
573 }
574
575 void ProcessChildrenSosofoObj::process(ProcessContext &context)
576 {
577   NodePtr node(context.vm().currentNode);
578   context.processChildren(mode_);
579   context.vm().currentNode = node;
580 }
581
582 void ProcessChildrenTrimSosofoObj::process(ProcessContext &context)
583 {
584   NodePtr node(context.vm().currentNode);
585   context.processChildrenTrim(mode_);
586   context.vm().currentNode = node;
587 }
588
589 NextMatchSosofoObj::NextMatchSosofoObj(StyleObj *style)
590 : style_(style)
591 {
592 }
593
594 void NextMatchSosofoObj::process(ProcessContext &context)
595 {
596   context.nextMatch(style_);
597 }
598
599 void EmptySosofoObj::process(ProcessContext &)
600 {
601   // nothing needed
602 }
603
604 ProcessNodeListSosofoObj::ProcessNodeListSosofoObj(NodeListObj *nodeList,
605                                                    const ProcessingMode *mode)
606 : nodeList_(nodeList), mode_(mode)
607 {
608   hasSubObjects_ = 1;
609 }
610
611 void ProcessNodeListSosofoObj::process(ProcessContext &context)
612 {
613   NodeListObj *nl = nodeList_;
614   Interpreter &interp = *context.vm().interp;
615   ELObjDynamicRoot protect(interp, nl);
616   for (;;) {
617     NodePtr node = nl->nodeListFirst(context.vm(), interp);
618     if (!node)
619       break;
620     bool chunk;
621     nl = nl->nodeListChunkRest(context.vm(), interp, chunk);
622     protect = nl;
623     context.processNodeSafe(node, mode_, chunk);
624   }
625 }
626
627 void ProcessNodeListSosofoObj::traceSubObjects(Collector &c) const
628 {
629   c.trace(nodeList_);
630 }
631
632 ProcessNodeSosofoObj::ProcessNodeSosofoObj(const NodePtr &node, const ProcessingMode *mode)
633 : node_(node), mode_(mode)
634 {
635 }
636
637 void ProcessNodeSosofoObj::process(ProcessContext &context)
638 {
639   context.processNode(node_, mode_);
640 }
641
642 void CurrentNodePageNumberSosofoObj::process(ProcessContext &context)
643 {
644   context.currentFOTBuilder().currentNodePageNumber(node_);
645 }
646
647 void PageNumberSosofoObj::process(ProcessContext &context)
648 {
649   context.currentFOTBuilder().pageNumber();
650 }
651
652 SetNonInheritedCsSosofoObj
653 ::SetNonInheritedCsSosofoObj(FlowObj *flowObj, const InsnPtr &code, ELObj **display, const NodePtr &node)
654 : flowObj_(flowObj), code_(code), display_(display), node_(node)
655 {
656   hasSubObjects_ = 1;
657 }
658
659 SetNonInheritedCsSosofoObj::~SetNonInheritedCsSosofoObj()
660 {
661   delete [] display_;
662 }
663
664 ELObj *SetNonInheritedCsSosofoObj::resolve(ProcessContext &context)
665 {
666   VM &vm = context.vm();
667   EvalContext::CurrentNodeSetter cns(node_, 0, vm);
668   StyleStack *saveStyleStack = vm.styleStack;
669   vm.styleStack = &context.currentStyleStack();
670   unsigned saveSpecLevel = vm.specLevel;
671   vm.specLevel = vm.styleStack->level();
672   Vector<size_t> dep;
673   vm.actualDependencies = &dep;
674   ELObj *obj = vm.eval(code_.pointer(), display_, flowObj_->copy(*vm.interp));
675   vm.styleStack = saveStyleStack;
676   vm.specLevel = saveSpecLevel;
677   if (vm.interp->isError(obj))
678     return 0;
679   return obj;
680 }
681
682 void SetNonInheritedCsSosofoObj::process(ProcessContext &context)
683 {
684   context.startFlowObj();
685   unsigned flags = 0;
686   flowObj_->pushStyle(context, flags);
687   ELObj *obj = resolve(context);
688   if (obj) {
689     ELObjDynamicRoot protect(*context.vm().interp, obj);
690     ((FlowObj *)obj)->processInner(context);
691   }
692   flowObj_->popStyle(context, flags);
693   context.endFlowObj();
694 }
695
696 bool SetNonInheritedCsSosofoObj::isCharacter()
697 {
698   return flowObj_->isCharacter();
699 }
700
701 bool SetNonInheritedCsSosofoObj::isRule()
702 {
703   return flowObj_->isRule();
704 }
705
706 bool SetNonInheritedCsSosofoObj::characterStyle(ProcessContext &context, StyleObj *&style, FOTBuilder::CharacterNIC &nic)
707 {
708   ELObj *obj = resolve(context);
709   if (obj) {
710     ELObjDynamicRoot protect(*context.vm().interp, obj);
711     return ((SosofoObj *)obj)->characterStyle(context, style, nic);
712   }
713   return 0;
714 }
715
716 bool SetNonInheritedCsSosofoObj::ruleStyle(ProcessContext &context, StyleObj *&style)
717 {
718   ELObj *obj = resolve(context);
719   if (obj) {
720     ELObjDynamicRoot protect(*context.vm().interp, obj);
721     return ((SosofoObj *)obj)->ruleStyle(context, style);
722   }
723   return 0;
724 }
725
726 void SetNonInheritedCsSosofoObj::traceSubObjects(Collector &c) const
727 {
728   c.trace(flowObj_);
729   if (display_)
730     for (ELObj **p = display_; *p; p++)
731       c.trace(*p);
732 }
733
734 LabelSosofoObj::LabelSosofoObj(SymbolObj *label, const Location &loc, SosofoObj *content)
735 : label_(label), locp_(new Location(loc)), content_(content)
736 {
737   hasSubObjects_ = 1;
738 }
739
740 void LabelSosofoObj::process(ProcessContext &context)
741 {
742   context.startConnection(label_, *locp_);
743   content_->process(context);
744   context.endConnection();
745 }
746
747 void LabelSosofoObj::traceSubObjects(Collector &c) const
748 {
749   // Symbols are permanent and don't need tracing.
750   c.trace(content_);
751 }
752
753 ContentMapSosofoObj::ContentMapSosofoObj(ELObj *contentMap,
754                                          const Location *locp, SosofoObj *content)
755 : contentMap_(contentMap), locp_(locp), content_(content)
756 {
757   hasSubObjects_ = 1;
758 }
759
760 void ContentMapSosofoObj::process(ProcessContext &context)
761 {
762   context.startMapContent(contentMap_, *locp_);
763   content_->process(context);
764   context.endMapContent();
765 }
766
767 void ContentMapSosofoObj::traceSubObjects(Collector &c) const
768 {
769   c.trace(contentMap_);
770   c.trace(content_);
771 }
772
773 DiscardLabeledSosofoObj::DiscardLabeledSosofoObj(SymbolObj *label, SosofoObj *content)
774 : label_(label), content_(content)
775 {
776   hasSubObjects_ = 1;
777 }
778
779 void DiscardLabeledSosofoObj::process(ProcessContext &context)
780 {
781   context.startDiscardLabeled(label_);
782   content_->process(context);
783   context.endDiscardLabeled();
784 }
785
786 void DiscardLabeledSosofoObj::traceSubObjects(Collector &c) const
787 {
788   c.trace(content_);
789 }
790
791 PageTypeSosofoObj::PageTypeSosofoObj(unsigned pageTypeFlag, SosofoObj *match, SosofoObj *noMatch)
792 : pageTypeFlag_(pageTypeFlag), match_(match), noMatch_(noMatch)
793 {
794   hasSubObjects_ = 1;
795 }
796
797 void PageTypeSosofoObj::process(ProcessContext &context)
798 {
799   unsigned pageType;
800   if (context.getPageType(pageType)) {
801     if (pageType & pageTypeFlag_)
802       match_->process(context);
803     else
804       noMatch_->process(context);
805   }
806 }
807
808 void PageTypeSosofoObj::traceSubObjects(Collector &c) const
809 {
810   c.trace(match_);
811   c.trace(noMatch_);
812 }
813
814 #ifdef DSSSL_NAMESPACE
815 }
816 #endif