Initial commit
[profile/ivi/openjade.git] / jade / TransformFOTBuilder.cxx
1 // Copyright (c) 1997 James Clark
2 // See the file copying.txt for copying permission.
3
4 #include "config.h"
5 #include "TransformFOTBuilder.h"
6 #include "FOTBuilder.h"
7 #include "OutputCharStream.h"
8 #include "MessageArg.h"
9 #include "ErrnoMessageArg.h"
10
11 #include <errno.h>
12
13 #ifdef DSSSL_NAMESPACE
14 namespace DSSSL_NAMESPACE {
15 #endif
16
17 const char RE = '\r';
18
19 class TransformFOTBuilder : public SerialFOTBuilder {
20 public:
21   // SGML Transformations
22   struct DocumentTypeNIC {
23     ~DocumentTypeNIC();
24     StringC name;
25     StringC publicId;
26     StringC systemId;
27   };
28   struct ElementNIC {
29     ~ElementNIC();
30     StringC gi;
31     Vector<StringC> attributes;
32   };
33   class TransformExtensionFlowObj : public FOTBuilder::ExtensionFlowObj {
34   public:
35     virtual void atomic(TransformFOTBuilder &, const NodePtr &) const = 0;
36   };
37   class TransformCompoundExtensionFlowObj : public FOTBuilder::CompoundExtensionFlowObj {
38   public:
39     virtual void start(TransformFOTBuilder &, const NodePtr &) const = 0;
40     virtual void end(TransformFOTBuilder &) const = 0;
41   };
42   class EntityRefFlowObj : public TransformExtensionFlowObj {
43   public:
44     void atomic(TransformFOTBuilder &fotb, const NodePtr &) const {
45       fotb.entityRef(name_);
46     }
47     bool hasNIC(const StringC &name) const {
48       return name == "name";
49     }
50     void setNIC(const StringC &name, const Value &value) {
51       value.convertString(name_);
52     }
53     ExtensionFlowObj *copy() const { return new EntityRefFlowObj(*this); }
54   private:
55     StringC name_;
56   };
57   class ProcessingInstructionFlowObj : public TransformExtensionFlowObj {
58   public:
59     void atomic(TransformFOTBuilder &fotb, const NodePtr &) const {
60       fotb.processingInstruction(data_);
61     }
62     bool hasNIC(const StringC &name) const {
63       return name.size() == 4 && name[0] == 'd' && name[1] == 'a' && name[2] == 't' && name[3] == 'a';
64     }
65     void setNIC(const StringC &name, const Value &value) {
66       value.convertString(data_);
67     }
68     ExtensionFlowObj *copy() const { return new ProcessingInstructionFlowObj(*this); }
69   private:
70     StringC data_;
71   };
72   class EmptyElementFlowObj : public TransformExtensionFlowObj {
73     void atomic(TransformFOTBuilder &fotb, const NodePtr &nd) const {
74       if (nic_.gi.size() > 0)
75         fotb.emptyElement(nic_);
76       else {
77         GroveString str;
78         if (nd && nd->getGi(str) == accessOK) {
79           ElementNIC tem(nic_);
80           tem.gi.assign(str.data(), str.size());
81           fotb.emptyElement(tem);
82         }
83         else
84           fotb.emptyElement(nic_);
85       }
86     }
87     bool hasNIC(const StringC &name) const {
88       return name == "gi" || name == "attributes";
89     }
90     void setNIC(const StringC &name, const Value &value) {
91       switch (name[0]) {
92       case 'g':
93         value.convertString(nic_.gi);
94         break;
95       case 'a':
96         value.convertStringPairList(nic_.attributes);
97         break;
98       }
99     }
100     ExtensionFlowObj *copy() const { return new EmptyElementFlowObj(*this); }
101   private:
102     ElementNIC nic_;
103   };
104   class ElementFlowObj : public TransformCompoundExtensionFlowObj {
105     void start(TransformFOTBuilder &fotb, const NodePtr &nd) const {
106       if (nic_.gi.size() > 0)
107         fotb.startElement(nic_);
108       else {
109         GroveString str;
110         if (nd && nd->getGi(str) == accessOK) {
111           ElementNIC tem(nic_);
112           tem.gi.assign(str.data(), str.size());
113           fotb.startElement(tem);
114         }
115         else
116           fotb.startElement(nic_);
117       }
118     }
119     void end(TransformFOTBuilder &fotb) const {
120       fotb.endElement();
121     }
122     bool hasNIC(const StringC &name) const {
123       return name == "gi" || name == "attributes";
124     }
125     void setNIC(const StringC &name, const Value &value) {
126       switch (name[0]) {
127       case 'g':
128         value.convertString(nic_.gi);
129         break;
130       case 'a':
131         value.convertStringPairList(nic_.attributes);
132         break;
133       }
134     }
135     ExtensionFlowObj *copy() const { return new ElementFlowObj(*this); }
136   private:
137     ElementNIC nic_;
138   };
139   class EntityFlowObj : public TransformCompoundExtensionFlowObj {
140     void start(TransformFOTBuilder &fotb, const NodePtr &) const {
141       fotb.startEntity(systemId_);
142     }
143     void end(TransformFOTBuilder &fotb) const {
144       fotb.endEntity();
145     }
146     bool hasNIC(const StringC &name) const {
147       return name == "system-id";
148     }
149     void setNIC(const StringC &name, const Value &value) {
150       value.convertString(systemId_);
151     }
152     ExtensionFlowObj *copy() const { return new EntityFlowObj(*this); }
153   private:
154     StringC systemId_;
155   };
156   class DocumentTypeFlowObj : public TransformExtensionFlowObj {
157     void atomic(TransformFOTBuilder &fotb, const NodePtr &nd) const {
158       fotb.documentType(nic_);
159     }
160     bool hasNIC(const StringC &name) const {
161       return name == "system-id" || name == "public-id" || name == "name";
162     }
163     void setNIC(const StringC &name, const Value &value) {
164       switch (name[0]) {
165       case 's':
166         value.convertString(nic_.systemId);
167         break;
168       case 'p':
169         value.convertString(nic_.publicId);
170         break;
171       case 'n':
172         value.convertString(nic_.name);
173         break;
174       }
175     }
176     ExtensionFlowObj *copy() const { return new DocumentTypeFlowObj(*this); }
177   private:
178     DocumentTypeNIC nic_;
179   };
180   TransformFOTBuilder(CmdLineApp *, bool xml, const Vector<StringC> &options);
181   ~TransformFOTBuilder();
182   void startElement(const ElementNIC &);
183   void endElement();
184   void emptyElement(const ElementNIC &);
185   void characters(const Char *s, size_t n);
186   void charactersFromNode(const NodePtr &, const Char *, size_t);
187   void processingInstruction(const StringC &);
188   void documentType(const DocumentTypeNIC &);
189   void formattingInstruction(const StringC &);
190   void entityRef(const StringC &);
191   void startEntity(const StringC &);
192   void endEntity();
193   void extension(const ExtensionFlowObj &fo, const NodePtr &);
194   void startExtensionSerial(const CompoundExtensionFlowObj &fo, const NodePtr &nd);
195   void endExtensionSerial(const CompoundExtensionFlowObj &fo);
196   void start();
197   void end();
198   void setPreserveSdata(bool);
199 private:
200   TransformFOTBuilder(const TransformFOTBuilder &);
201   void operator=(const TransformFOTBuilder &);
202
203   OutputCharStream &os() { return *os_; }
204   void attributes(const Vector<StringC> &atts);
205   void flushPendingRe() {
206     if (state_ == statePendingRe) {
207       os() << RE;
208       state_ = stateMiddle;
209     }
210   }
211   void flushPendingReCharRef() {
212     if (state_ == statePendingRe) {
213       os() << "&#13;";
214       state_ = stateMiddle;
215     }
216   }
217
218   CmdLineApp *app_;
219   OutputCharStream *os_;
220   Owner<OutputCharStream> topOs_;
221   Vector<StringC> openElements_;
222   StringC undefGi_;
223   struct OpenFile : Link {
224     ~OpenFile();
225     OutputCharStream *saveOs;
226     // fb must be before os so it gets destroyed afterwards
227     FileOutputByteStream fb;
228     Owner<OutputCharStream> os;
229     StringC systemId;
230   };
231   IList<OpenFile> openFileStack_;
232   bool xml_;
233   enum ReState {
234     stateMiddle,
235     stateStartOfElement,
236     statePendingRe
237   };
238   ReState state_;
239   bool preserveSdata_;
240   char RE_[2];
241   char SP_[2];
242   // Really Vector<bool>
243   StringC preserveSdataStack_;
244 };
245
246 FOTBuilder *makeTransformFOTBuilder(CmdLineApp *app,
247                                     bool xml,
248                                     const Vector<StringC> &options,
249                                     const FOTBuilder::Extension *&ext)
250 {
251   static const TransformFOTBuilder::ProcessingInstructionFlowObj pi;
252   static const TransformFOTBuilder::ElementFlowObj element;
253   static const TransformFOTBuilder::EmptyElementFlowObj emptyElement;
254   static const TransformFOTBuilder::EntityFlowObj entity;
255   static const TransformFOTBuilder::EntityRefFlowObj entityRef;
256   static const TransformFOTBuilder::DocumentTypeFlowObj documentType;
257   static const FOTBuilder::Extension extensions[] = {
258     {
259       "UNREGISTERED::James Clark//Flow Object Class::processing-instruction",
260       0,
261       0,
262       0,
263       0,
264       &pi
265     },
266     {
267       "UNREGISTERED::James Clark//Flow Object Class::element",
268       0,
269       0,
270       0,
271       0,
272       &element
273     },
274     {
275       "UNREGISTERED::James Clark//Flow Object Class::empty-element",
276       0,
277       0,
278       0,
279       0,
280       &emptyElement
281     },
282     {
283       "UNREGISTERED::James Clark//Flow Object Class::entity",
284       0,
285       0,
286       0,
287       0,
288       &entity
289     },
290     {
291       "UNREGISTERED::James Clark//Flow Object Class::entity-ref",
292       0,
293       0,
294       0,
295       0,
296       &entityRef
297     },
298     {
299       "UNREGISTERED::James Clark//Flow Object Class::document-type",
300       0,
301       0,
302       0,
303       0,
304       &documentType
305     },
306     {
307       "UNREGISTERED::James Clark//Characteristic::preserve-sdata?",
308       (void (FOTBuilder::*)(bool))&TransformFOTBuilder::setPreserveSdata,
309       0,
310       0,
311       0,
312       0
313     },
314     { 0 }
315   };
316   ext = extensions;
317   return new TransformFOTBuilder(app, xml, options);
318 }
319
320 static
321 void outputNumericCharRef(OutputCharStream &os, Char c)
322 {
323   os << "&#" << (unsigned long)c << ';';
324 }
325
326 TransformFOTBuilder::TransformFOTBuilder(CmdLineApp *app, bool xml,
327                                          const Vector<StringC> &options)
328 : app_(app),
329   xml_(xml),
330   topOs_(new RecordOutputCharStream(app->makeStdOut())),
331   state_(stateMiddle),
332   preserveSdata_(0)
333 {
334   undefGi_ = app_->systemCharset().execToDesc("#UNDEF");
335   topOs_->setEscaper(outputNumericCharRef);
336   os_ = topOs_.pointer();
337   preserveSdataStack_ += 0;
338   RE_[0] = RE;
339   RE_[1] = 0;
340   SP_[0] = RE;
341   SP_[1] = 0;
342   for (size_t i = 0; i < options.size(); i++) {
343     if (options[i] == app_->systemCharset().execToDesc("raw")) {
344       RE_[0] = 0;
345       SP_[0] = ' ';
346     }
347   }
348 }
349
350 TransformFOTBuilder::~TransformFOTBuilder()
351 {
352 }
353
354 static bool contains(const StringC &str, Char c)
355 {
356   for (size_t i = 0; i < str.size(); i++)
357     if (str[i] == c)
358        return 1;
359   return 0;
360 }
361
362 void TransformFOTBuilder::documentType(const DocumentTypeNIC &nic)
363 {
364   flushPendingRe();
365   if (nic.name.size()) {
366     os() << "<!DOCTYPE " << nic.name;
367     if (nic.publicId.size())
368       os() << " PUBLIC \"" << nic.publicId << '"';
369     else 
370       os() << " SYSTEM";
371     if (nic.systemId.size()) {
372       if (nic.publicId.size()) {
373         os() << ' ';
374       }
375       char quote = contains(nic.systemId, '"') ? '\'' : '"';
376       os() << quote << nic.systemId << quote;
377     }
378     os() << '>' << RE;
379   }
380   atomic();
381 }
382
383 void TransformFOTBuilder::attributes(const Vector<StringC> &atts)
384 {
385   for (size_t i = 0; i < atts.size(); i += 2) {
386     os() << SP_ << atts[i] << '=';
387     const StringC &s = atts[i + 1];
388     char quoteChar = 0;
389
390     if (!contains(s, '&'))
391       if (!contains(s, '"'))
392         quoteChar = '"';
393       else if (!contains(s, '\''))
394         quoteChar = '\'';
395
396     if (quoteChar)
397       os() << quoteChar << s << quoteChar;
398     else {
399       os() << '"';
400       for (size_t j = 0; j < s.size(); j++) {
401         if (s[j] == '"') {
402           if (xml_)
403             os() << "&quot;";
404           else
405             outputNumericCharRef(os(), '"');
406         }
407         else
408         if (s[j] == '&' ) {
409           if (xml_)
410             os() << "&amp;";
411           else
412             outputNumericCharRef(os(), '&');
413         }
414         else
415           os().put(s[j]);
416       }
417       os() << '"';
418     }
419   }
420 }
421
422 void TransformFOTBuilder::startElement(const ElementNIC &nic)
423 {
424   flushPendingRe();
425   os() << "<";
426   const StringC &s = nic.gi.size() == 0 ? undefGi_ : nic.gi;
427   os() << s;
428   attributes(nic.attributes);
429   os() << RE_ << '>';
430   openElements_.push_back(s);
431   start();
432   state_ = stateStartOfElement;
433 }
434
435 void TransformFOTBuilder::emptyElement(const ElementNIC &nic)
436 {
437   flushPendingRe();
438   os() << "<";
439   const StringC &s = nic.gi.size() == 0 ? undefGi_ : nic.gi;
440   os() << s;
441   attributes(nic.attributes);
442   if (xml_)
443     os() << "/>";
444   else
445     os() << '>';
446   atomic();
447   state_ = stateMiddle;
448 }
449  
450 void TransformFOTBuilder::endElement()
451 {
452   flushPendingReCharRef();
453   os() << "</" << openElements_.back();
454   os() << RE_ << '>';
455   openElements_.resize(openElements_.size() - 1);
456   end();
457   state_ = stateMiddle;
458 }
459
460 void TransformFOTBuilder::processingInstruction(const StringC &s)
461 {
462   flushPendingReCharRef();
463   os() << "<?" << s;
464   if (xml_)
465     os() << "?>";
466   else
467     os() << '>';
468   atomic();
469 }
470
471 void TransformFOTBuilder::formattingInstruction(const StringC &s)
472 {
473   flushPendingRe();
474   os() << s;
475 }
476
477 void TransformFOTBuilder::entityRef(const StringC &s)
478 {
479   flushPendingRe();
480   os() << "&" << s << ";";
481 }
482
483 void TransformFOTBuilder::startEntity(const StringC &systemId)
484 {
485   flushPendingRe();
486   OpenFile *ofp = new OpenFile;
487   openFileStack_.insert(ofp);
488   ofp->systemId = systemId;
489   ofp->saveOs = os_;
490   String<CmdLineApp::AppChar> filename;
491 #ifdef SP_WIDE_SYSTEM
492   filename = systemId;
493 #else
494   filename = app_->codingSystem()->convertOut(systemId);
495 #endif
496   if (filename.size()) {
497     filename += 0;
498     if (!ofp->fb.open(filename.data())) {
499       app_->message(CmdLineApp::openFileErrorMessage(),
500                     StringMessageArg(systemId),
501                     ErrnoMessageArg(errno));
502     }
503     else {
504       ofp->os
505         = new RecordOutputCharStream(
506               new EncodeOutputCharStream(&ofp->fb,
507                                          app_->outputCodingSystem()));
508       ofp->os->setEscaper(outputNumericCharRef);
509       os_ = ofp->os.pointer();
510     }
511   }
512 }
513
514 void TransformFOTBuilder::endEntity()
515 {
516   flushPendingRe();
517   OpenFile &of = *openFileStack_.head();
518   if (of.os) {
519     errno = 0;
520     of.os->flush();
521     if (!of.fb.close())
522       app_->message(CmdLineApp::closeFileErrorMessage(),
523                     StringMessageArg(of.systemId),
524                     ErrnoMessageArg(errno));
525   }
526   os_ = of.saveOs;
527   delete openFileStack_.get();
528 }
529
530 inline
531 OutputCharStream &operator<<(OutputCharStream &os, GroveString &str)
532 {
533   return os.write(str.data(), str.size());
534 }
535
536 void TransformFOTBuilder::charactersFromNode(const NodePtr &nd, const Char *s, size_t n)
537 {
538   GroveString name;
539   if (preserveSdata_ && n == 1 && nd->getEntityName(name) == accessOK) {
540     flushPendingRe();
541     os() << "&" << name << ';';
542   }
543   else
544     TransformFOTBuilder::characters(s, n);
545 }
546
547 void TransformFOTBuilder::characters(const Char *s, size_t n)
548 {
549   if (n == 0)
550     return;
551   flushPendingRe();
552   if (state_ == stateStartOfElement && *s == RE) {
553     s++;
554     n--;
555     os() << "&#13;";
556     if (n == 0) {
557       state_ = stateMiddle;
558       return;
559     }
560   }
561   if (s[n - 1] == RE) {
562     n--;
563     state_ = statePendingRe;
564   }
565   else
566     state_ = stateMiddle;
567   for (; n > 0; n--, s++) {
568     switch (*s) {
569     case '&':
570       if (xml_)
571         os() << "&amp;";
572       else
573         outputNumericCharRef(os(), *s);
574       break;
575     case '<':
576       if (xml_)
577         os() << "&lt;";
578       else
579         outputNumericCharRef(os(), *s);
580       break;
581     case '>':
582       if (xml_)
583         os() << "&gt;";
584       else
585         outputNumericCharRef(os(), *s);
586       break;
587     default:
588       os().put(*s);
589       break;
590     }
591   }
592 }
593
594 void TransformFOTBuilder::extension(const ExtensionFlowObj &fo, const NodePtr &nd)
595 {
596   ((const TransformExtensionFlowObj &)fo).atomic(*this, nd);
597 }
598
599 void TransformFOTBuilder::startExtensionSerial(const CompoundExtensionFlowObj &fo, const NodePtr &nd)
600 {
601   ((const TransformCompoundExtensionFlowObj &)fo).start(*this, nd);
602 }
603
604 void TransformFOTBuilder::endExtensionSerial(const CompoundExtensionFlowObj &fo)
605 {
606   ((const TransformCompoundExtensionFlowObj &)fo).end(*this);
607 }
608
609 void TransformFOTBuilder::setPreserveSdata(bool b)
610 {
611   preserveSdata_ = b;
612 }
613
614 void TransformFOTBuilder::start()
615 {
616   preserveSdataStack_ += Char(preserveSdata_);
617 }
618
619 void TransformFOTBuilder::end()
620 {
621   preserveSdataStack_.resize(preserveSdataStack_.size() - 1);
622   preserveSdata_ = bool(preserveSdataStack_[preserveSdataStack_.size() - 1]);
623 }
624
625 TransformFOTBuilder::OpenFile::~OpenFile()
626 {
627 }
628
629 TransformFOTBuilder::DocumentTypeNIC::~DocumentTypeNIC()
630 {
631 }
632
633 TransformFOTBuilder::ElementNIC::~ElementNIC()
634 {
635 }
636
637 #ifdef DSSSL_NAMESPACE
638 }
639 #endif
640
641 #include "TransformFOTBuilder_inst.cxx"