1 // Copyright (c) 1997 James Clark
2 // See the file copying.txt for copying permission.
5 #include "TransformFOTBuilder.h"
6 #include "FOTBuilder.h"
7 #include "OutputCharStream.h"
8 #include "MessageArg.h"
9 #include "ErrnoMessageArg.h"
13 #ifdef DSSSL_NAMESPACE
14 namespace DSSSL_NAMESPACE {
19 class TransformFOTBuilder : public SerialFOTBuilder {
21 // SGML Transformations
22 struct DocumentTypeNIC {
31 Vector<StringC> attributes;
33 class TransformExtensionFlowObj : public FOTBuilder::ExtensionFlowObj {
35 virtual void atomic(TransformFOTBuilder &, const NodePtr &) const = 0;
37 class TransformCompoundExtensionFlowObj : public FOTBuilder::CompoundExtensionFlowObj {
39 virtual void start(TransformFOTBuilder &, const NodePtr &) const = 0;
40 virtual void end(TransformFOTBuilder &) const = 0;
42 class EntityRefFlowObj : public TransformExtensionFlowObj {
44 void atomic(TransformFOTBuilder &fotb, const NodePtr &) const {
45 fotb.entityRef(name_);
47 bool hasNIC(const StringC &name) const {
48 return name == "name";
50 void setNIC(const StringC &name, const Value &value) {
51 value.convertString(name_);
53 ExtensionFlowObj *copy() const { return new EntityRefFlowObj(*this); }
57 class ProcessingInstructionFlowObj : public TransformExtensionFlowObj {
59 void atomic(TransformFOTBuilder &fotb, const NodePtr &) const {
60 fotb.processingInstruction(data_);
62 bool hasNIC(const StringC &name) const {
63 return name.size() == 4 && name[0] == 'd' && name[1] == 'a' && name[2] == 't' && name[3] == 'a';
65 void setNIC(const StringC &name, const Value &value) {
66 value.convertString(data_);
68 ExtensionFlowObj *copy() const { return new ProcessingInstructionFlowObj(*this); }
72 class EmptyElementFlowObj : public TransformExtensionFlowObj {
73 void atomic(TransformFOTBuilder &fotb, const NodePtr &nd) const {
74 if (nic_.gi.size() > 0)
75 fotb.emptyElement(nic_);
78 if (nd && nd->getGi(str) == accessOK) {
80 tem.gi.assign(str.data(), str.size());
81 fotb.emptyElement(tem);
84 fotb.emptyElement(nic_);
87 bool hasNIC(const StringC &name) const {
88 return name == "gi" || name == "attributes";
90 void setNIC(const StringC &name, const Value &value) {
93 value.convertString(nic_.gi);
96 value.convertStringPairList(nic_.attributes);
100 ExtensionFlowObj *copy() const { return new EmptyElementFlowObj(*this); }
104 class ElementFlowObj : public TransformCompoundExtensionFlowObj {
105 void start(TransformFOTBuilder &fotb, const NodePtr &nd) const {
106 if (nic_.gi.size() > 0)
107 fotb.startElement(nic_);
110 if (nd && nd->getGi(str) == accessOK) {
111 ElementNIC tem(nic_);
112 tem.gi.assign(str.data(), str.size());
113 fotb.startElement(tem);
116 fotb.startElement(nic_);
119 void end(TransformFOTBuilder &fotb) const {
122 bool hasNIC(const StringC &name) const {
123 return name == "gi" || name == "attributes";
125 void setNIC(const StringC &name, const Value &value) {
128 value.convertString(nic_.gi);
131 value.convertStringPairList(nic_.attributes);
135 ExtensionFlowObj *copy() const { return new ElementFlowObj(*this); }
139 class EntityFlowObj : public TransformCompoundExtensionFlowObj {
140 void start(TransformFOTBuilder &fotb, const NodePtr &) const {
141 fotb.startEntity(systemId_);
143 void end(TransformFOTBuilder &fotb) const {
146 bool hasNIC(const StringC &name) const {
147 return name == "system-id";
149 void setNIC(const StringC &name, const Value &value) {
150 value.convertString(systemId_);
152 ExtensionFlowObj *copy() const { return new EntityFlowObj(*this); }
156 class DocumentTypeFlowObj : public TransformExtensionFlowObj {
157 void atomic(TransformFOTBuilder &fotb, const NodePtr &nd) const {
158 fotb.documentType(nic_);
160 bool hasNIC(const StringC &name) const {
161 return name == "system-id" || name == "public-id" || name == "name";
163 void setNIC(const StringC &name, const Value &value) {
166 value.convertString(nic_.systemId);
169 value.convertString(nic_.publicId);
172 value.convertString(nic_.name);
176 ExtensionFlowObj *copy() const { return new DocumentTypeFlowObj(*this); }
178 DocumentTypeNIC nic_;
180 TransformFOTBuilder(CmdLineApp *, bool xml, const Vector<StringC> &options);
181 ~TransformFOTBuilder();
182 void startElement(const ElementNIC &);
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 &);
193 void extension(const ExtensionFlowObj &fo, const NodePtr &);
194 void startExtensionSerial(const CompoundExtensionFlowObj &fo, const NodePtr &nd);
195 void endExtensionSerial(const CompoundExtensionFlowObj &fo);
198 void setPreserveSdata(bool);
200 TransformFOTBuilder(const TransformFOTBuilder &);
201 void operator=(const TransformFOTBuilder &);
203 OutputCharStream &os() { return *os_; }
204 void attributes(const Vector<StringC> &atts);
205 void flushPendingRe() {
206 if (state_ == statePendingRe) {
208 state_ = stateMiddle;
211 void flushPendingReCharRef() {
212 if (state_ == statePendingRe) {
214 state_ = stateMiddle;
219 OutputCharStream *os_;
220 Owner<OutputCharStream> topOs_;
221 Vector<StringC> openElements_;
223 struct OpenFile : Link {
225 OutputCharStream *saveOs;
226 // fb must be before os so it gets destroyed afterwards
227 FileOutputByteStream fb;
228 Owner<OutputCharStream> os;
231 IList<OpenFile> openFileStack_;
242 // Really Vector<bool>
243 StringC preserveSdataStack_;
246 FOTBuilder *makeTransformFOTBuilder(CmdLineApp *app,
248 const Vector<StringC> &options,
249 const FOTBuilder::Extension *&ext)
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[] = {
259 "UNREGISTERED::James Clark//Flow Object Class::processing-instruction",
267 "UNREGISTERED::James Clark//Flow Object Class::element",
275 "UNREGISTERED::James Clark//Flow Object Class::empty-element",
283 "UNREGISTERED::James Clark//Flow Object Class::entity",
291 "UNREGISTERED::James Clark//Flow Object Class::entity-ref",
299 "UNREGISTERED::James Clark//Flow Object Class::document-type",
307 "UNREGISTERED::James Clark//Characteristic::preserve-sdata?",
308 (void (FOTBuilder::*)(bool))&TransformFOTBuilder::setPreserveSdata,
317 return new TransformFOTBuilder(app, xml, options);
321 void outputNumericCharRef(OutputCharStream &os, Char c)
323 os << "&#" << (unsigned long)c << ';';
326 TransformFOTBuilder::TransformFOTBuilder(CmdLineApp *app, bool xml,
327 const Vector<StringC> &options)
330 topOs_(new RecordOutputCharStream(app->makeStdOut())),
334 undefGi_ = app_->systemCharset().execToDesc("#UNDEF");
335 topOs_->setEscaper(outputNumericCharRef);
336 os_ = topOs_.pointer();
337 preserveSdataStack_ += 0;
342 for (size_t i = 0; i < options.size(); i++) {
343 if (options[i] == app_->systemCharset().execToDesc("raw")) {
350 TransformFOTBuilder::~TransformFOTBuilder()
354 static bool contains(const StringC &str, Char c)
356 for (size_t i = 0; i < str.size(); i++)
362 void TransformFOTBuilder::documentType(const DocumentTypeNIC &nic)
365 if (nic.name.size()) {
366 os() << "<!DOCTYPE " << nic.name;
367 if (nic.publicId.size())
368 os() << " PUBLIC \"" << nic.publicId << '"';
371 if (nic.systemId.size()) {
372 if (nic.publicId.size()) {
375 char quote = contains(nic.systemId, '"') ? '\'' : '"';
376 os() << quote << nic.systemId << quote;
383 void TransformFOTBuilder::attributes(const Vector<StringC> &atts)
385 for (size_t i = 0; i < atts.size(); i += 2) {
386 os() << SP_ << atts[i] << '=';
387 const StringC &s = atts[i + 1];
390 if (!contains(s, '&'))
391 if (!contains(s, '"'))
393 else if (!contains(s, '\''))
397 os() << quoteChar << s << quoteChar;
400 for (size_t j = 0; j < s.size(); j++) {
405 outputNumericCharRef(os(), '"');
412 outputNumericCharRef(os(), '&');
422 void TransformFOTBuilder::startElement(const ElementNIC &nic)
426 const StringC &s = nic.gi.size() == 0 ? undefGi_ : nic.gi;
428 attributes(nic.attributes);
430 openElements_.push_back(s);
432 state_ = stateStartOfElement;
435 void TransformFOTBuilder::emptyElement(const ElementNIC &nic)
439 const StringC &s = nic.gi.size() == 0 ? undefGi_ : nic.gi;
441 attributes(nic.attributes);
447 state_ = stateMiddle;
450 void TransformFOTBuilder::endElement()
452 flushPendingReCharRef();
453 os() << "</" << openElements_.back();
455 openElements_.resize(openElements_.size() - 1);
457 state_ = stateMiddle;
460 void TransformFOTBuilder::processingInstruction(const StringC &s)
462 flushPendingReCharRef();
471 void TransformFOTBuilder::formattingInstruction(const StringC &s)
477 void TransformFOTBuilder::entityRef(const StringC &s)
480 os() << "&" << s << ";";
483 void TransformFOTBuilder::startEntity(const StringC &systemId)
486 OpenFile *ofp = new OpenFile;
487 openFileStack_.insert(ofp);
488 ofp->systemId = systemId;
490 String<CmdLineApp::AppChar> filename;
491 #ifdef SP_WIDE_SYSTEM
494 filename = app_->codingSystem()->convertOut(systemId);
496 if (filename.size()) {
498 if (!ofp->fb.open(filename.data())) {
499 app_->message(CmdLineApp::openFileErrorMessage(),
500 StringMessageArg(systemId),
501 ErrnoMessageArg(errno));
505 = new RecordOutputCharStream(
506 new EncodeOutputCharStream(&ofp->fb,
507 app_->outputCodingSystem()));
508 ofp->os->setEscaper(outputNumericCharRef);
509 os_ = ofp->os.pointer();
514 void TransformFOTBuilder::endEntity()
517 OpenFile &of = *openFileStack_.head();
522 app_->message(CmdLineApp::closeFileErrorMessage(),
523 StringMessageArg(of.systemId),
524 ErrnoMessageArg(errno));
527 delete openFileStack_.get();
531 OutputCharStream &operator<<(OutputCharStream &os, GroveString &str)
533 return os.write(str.data(), str.size());
536 void TransformFOTBuilder::charactersFromNode(const NodePtr &nd, const Char *s, size_t n)
539 if (preserveSdata_ && n == 1 && nd->getEntityName(name) == accessOK) {
541 os() << "&" << name << ';';
544 TransformFOTBuilder::characters(s, n);
547 void TransformFOTBuilder::characters(const Char *s, size_t n)
552 if (state_ == stateStartOfElement && *s == RE) {
557 state_ = stateMiddle;
561 if (s[n - 1] == RE) {
563 state_ = statePendingRe;
566 state_ = stateMiddle;
567 for (; n > 0; n--, s++) {
573 outputNumericCharRef(os(), *s);
579 outputNumericCharRef(os(), *s);
585 outputNumericCharRef(os(), *s);
594 void TransformFOTBuilder::extension(const ExtensionFlowObj &fo, const NodePtr &nd)
596 ((const TransformExtensionFlowObj &)fo).atomic(*this, nd);
599 void TransformFOTBuilder::startExtensionSerial(const CompoundExtensionFlowObj &fo, const NodePtr &nd)
601 ((const TransformCompoundExtensionFlowObj &)fo).start(*this, nd);
604 void TransformFOTBuilder::endExtensionSerial(const CompoundExtensionFlowObj &fo)
606 ((const TransformCompoundExtensionFlowObj &)fo).end(*this);
609 void TransformFOTBuilder::setPreserveSdata(bool b)
614 void TransformFOTBuilder::start()
616 preserveSdataStack_ += Char(preserveSdata_);
619 void TransformFOTBuilder::end()
621 preserveSdataStack_.resize(preserveSdataStack_.size() - 1);
622 preserveSdata_ = bool(preserveSdataStack_[preserveSdataStack_.size() - 1]);
625 TransformFOTBuilder::OpenFile::~OpenFile()
629 TransformFOTBuilder::DocumentTypeNIC::~DocumentTypeNIC()
633 TransformFOTBuilder::ElementNIC::~ElementNIC()
637 #ifdef DSSSL_NAMESPACE
641 #include "TransformFOTBuilder_inst.cxx"