/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "base/polygraph.h" #include #include "xstd/h/sstream.h" #include "xstd/Checksum.h" #include "xstd/StrIdentifier.h" #include "xstd/gadgets.h" #include "base/AnyToString.h" #include "base/BStream.h" #include "runtime/IOBuf.h" #include "csm/XmlTagParser.h" #include "csm/oid2Url.h" #include "csm/InjectIter.h" #include "csm/EmbedContMdl.h" #include "csm/ContentDbase.h" #include "csm/cdbEntries.h" /* CdbEntryPrnOpt */ CdbEntryPrnOpt::CdbEntryPrnOpt(): buf(0), injector(0), sizeMax(-1), entryOff(-1), entryData(0) { } /* CdbEntry */ bool CdbEntry::Pour(const Area &image, bool divisible, CdbEntryPrnOpt &opt, bool &needMore) { if (image.size() <= 0) { // empty needMore = false; opt.entryOff = 0; return true; } if (!Should(opt.entryOff < image.size())) // internal error return false; Size remainingSize = Size(image.size()) - opt.entryOff; if (remainingSize > opt.sizeMax) { // body ends sooner if (!divisible) return false; // and we cannot divide remainingSize = opt.sizeMax; } const Size space = opt.buf->spaceSize(); if (remainingSize > space) { needMore = true; if (space > 0) { opt.buf->append(image.data() + opt.entryOff, space); opt.entryOff += space; opt.sizeMax -= space; } } else { needMore = false; opt.buf->append(image.data() + opt.entryOff, remainingSize); opt.entryOff = 0; opt.sizeMax -= remainingSize; } return true; } /* CdbeBlob */ void CdbeBlob::image(const String &anImage) { theImage = anImage; } OBStream &CdbeBlob::store(OBStream &ol) const { return ol << theImage; } IBStream &CdbeBlob::load(IBStream &il) { return il >> theImage; } ostream &CdbeBlob::print(ostream &os) const { return os << theImage; } bool CdbeBlob::pour(CdbEntryPrnOpt &opt, bool &needMore) const { return Pour(theImage.area(0), false, opt, needMore); } /* CdbeText */ bool CdbeText::pour(CdbEntryPrnOpt &opt, bool &needMore) const { const Size mark = opt.buf->contSize(); if (!Pour(theImage.area(0), true, opt, needMore)) return false; if (opt.injector) opt.injector->inject(*opt.buf, 0, opt.buf->contSize() - mark); return true; } /* CdbeLink */ Size CdbeLink::size(CdbEntryPrnOpt &opt) const { return generateImage(opt.embed).size(); } Size CdbeLink::meanSize() const { return Size(45); // roughly } OBStream &CdbeLink::store(OBStream &ol) const { return ol << contentCategory << origImage; } IBStream &CdbeLink::load(IBStream &il) { return il >> contentCategory >> origImage; } ostream &CdbeLink::print(ostream &os) const { return os << "[link:" << contentCategory << '@' << origImage << ']'; } bool CdbeLink::pour(CdbEntryPrnOpt &opt, bool &needMore) const { CdbEntryPrnOpt::Embed &e = opt.embed; if (!Should(e.model && e.rng)) return false; const Area image = generateImage(e); Should(image.size() > 0) && Should(*image.data() == '/'); if (!Pour(image, false, opt, needMore)) return false; return true; } Area CdbeLink::generateImage(CdbEntryPrnOpt::Embed &e) const { Assert(e.model); const ObjId eid = e.model->embedCatOid(e.container, e.count, contentCategory); static char buf[8*1024]; ofixedstream os(buf, sizeof(buf)); Oid2UrlPath(eid, os); os.flush(); Should(os); return Area::Create(buf, 0, Size(os.tellp())); } /* CdbePage */ CdbePage::CdbePage(): theDb(new ContentDbase) { } CdbePage::~CdbePage() { delete theDb; } Size CdbePage::size(CdbEntryPrnOpt &globOpt) const { CdbEntryPrnOpt opt = globOpt; Size sum = 0; int &pos = opt.embed.count; for (pos = 0; pos < theDb->count(); ++pos) { const CdbEntry *e = theDb->entry(pos); sum += e->size(opt); } return sum; } Size CdbePage::meanSize() const { return int(theDb->entrySizeMean() * theDb->count()); } void CdbePage::add(CdbEntry *e) { theDb->add(e); } OBStream &CdbePage::store(OBStream &ol) const { theDb->store(ol); return ol; } IBStream &CdbePage::load(IBStream &il) { theDb->load(il); return il; } ostream &CdbePage::print(ostream &os) const { return theDb->print(os); } // based on CdbBodyIter::pourBody bool CdbePage::pour(CdbEntryPrnOpt &globOpt, bool &needMore) const { CdbEntryPrnOpt &opt = globOpt; for (int &pos = opt.entryData; pos < theDb->count() && opt.sizeMax > 0; ++pos) { const CdbEntry *e = theDb->entry(pos); opt.embed.count = pos; if (!e->pour(opt, needMore)) return false; // current entry will never fit if (needMore) return true; } needMore = false; return true; }