/* 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 "xml/XmlDoc.h" #include "xml/XmlTag.h" #include "xml/XmlAttr.h" #include "loganalyzers/ReportBlob.h" #include "loganalyzers/BlobDb.h" #include "loganalyzers/Sample.h" #include "loganalyzers/RepToHtmlFile.h" Map RepToHtmlFile::TheLocations; void RepToHtmlFile::Location(BlobDb &db, const ReportBlob &blob, const String &fname) { TheLocations.add(blob.key(), new String(fname)); CollectLocations(db, blob, fname); } void RepToHtmlFile::CollectLocations(BlobDb &db, const XmlNode &node, const String &fname) { if (node.attrs()) { if (node.name() == "report_blob" && node.attrs()->has("key")) { const String &key = node.attrs()->value("key"); if (!Location(key)) TheLocations.add(key, new String(fname + "#_" + key)); } else if (node.name() == "include" && node.attrs()->has("src") && node.attrs()->has("auth")) { const String &key = node.attrs()->value("src"); CollectLocations(db, db.get(key), fname); } } if (node.kids()) { for (int i = 0; i < node.kids()->count(); ++i) CollectLocations(db, *node.kids()->item(i), fname); } } String RepToHtmlFile::Location(const String &key) { String *name = 0; if (TheLocations.find(key, name)) return *name; else return 0; } RepToHtmlFile::RepToHtmlFile(BlobDb &db, ostream *aStream, const String &aLocation): theDb(db), theStream(aStream), theLocation(aLocation), theQuoteLevel(0) { } RepToHtmlFile::~RepToHtmlFile() { } void RepToHtmlFile::render(const XmlDoc &doc) { if (doc.root()) { doc.root()->render(*this); } } void RepToHtmlFile::renderReportBlob(const ReportBlob &blob) { *theStream << "" << endl; renderBlob(blob); *theStream << "" << endl; } static void RepToHtmlFile_EscapeChar(char c, ostream &os) { if (c == '"') os << """; else if (c == '<') os << "<"; else if (c == '>') os << ">"; else if (c == '&') os << "&"; else os << c; } void RepToHtmlFile::renderText(const char *buf, Size sz) { for (int i = 0; i < sz; ++i, ++buf) RepToHtmlFile_EscapeChar(*buf, *theStream); } void RepToHtmlFile::renderTag(const XmlTag &tag) { if (tag.name() == "document") renderDocument(tag); else if (tag.name() == "section") renderSection(tag); else if (tag.name() == "chapter") renderChapter(tag); else if (tag.name() == "report_blob") renderBlob(tag); else if (tag.name() == "include") renderBlobInclude(tag); else if (tag.name() == "blob_ptr") renderBlobPtr(tag); else if (tag.name() == "measurement") renderMeasurement(tag); else if (tag.name() == "internal_error") *theStream << " err "; // XXX red, and add an ptr to an explanation else if (tag.name() == "codesample") { *theStream << "
";
		foreach(tag.kids());
		*theStream << "
"; } else if (tag.name() == "title") { // handled inside and
} else if (tag.name() == "description") { // handled inside } else if (tag.name() == "ul") { renderUl(tag); } else if (tag.name() == "th" || tag.name() == "td") { renderTableCell(tag); } else if (tag.name() == "img") { renderImage(tag); } else if (tag.name() == "br") { tag.printOpen(*theStream, ""); *theStream << ">"; } else { tag.printOpen(*theStream, ""); *theStream << ">"; foreach(tag.kids()); *theStream << ""; } } void RepToHtmlFile::renderDocument(const XmlTag &tag) { XmlSearchRes res; if (tag.kids()->selByTagName("title", res)) { *theStream << "

"; foreach(res.last()->kids()); *theStream << "

"; } foreach(tag.kids()); } void RepToHtmlFile::renderChapter(const XmlTag &tag) { theSectionState.reset(); if (tag.attrs()->has("name")) *theStream << "value("name") << "\">"; // title XmlSearchRes res; if (Should(tag.kids()->selByTagName("title", res))) { *theStream << "

"; foreach(res.last()->kids()); *theStream << "

" << endl; } foreach(tag.kids()); *theStream << endl << endl; } void RepToHtmlFile::renderSection(const XmlTag &tag) { const int sectLvl = theSectionState.level(); String trueNum; const String usrNum = theSectionState.begSection(tag, trueNum); *theStream << ""; // title const int hLvl = sectLvl + 1; XmlSearchRes res; if (Should(tag.kids()->selByTagName("title", res))) { *theStream << "' << usrNum << ' '; foreach(res.last()->kids()); *theStream << "' << endl; } if (sectLvl == 1) *theStream << "
"; foreach(tag.kids()); if (sectLvl == 1) *theStream << "
"; theSectionState.endSection(); *theStream << "
" << endl; } void RepToHtmlFile::renderBlobInclude(const XmlTag &tag) { const String &key = tag.attrs()->value("src"); const bool auth = tag.attrs()->has("auth"); theParents.append(&tag); if (!auth) ++theQuoteLevel; renderNode(theDb.get(key)); if (!auth) --theQuoteLevel; theParents.pop(); } void RepToHtmlFile::renderBlobPtr(const XmlTag &tag) { const String &key = tag.attrs()->value("key"); if (const String loc = location(key)) { *theStream << ""; foreach(tag.kids()); *theStream << ""; return; } *theStream << ""; if (!tag.attrs()->has("maybe_null")) { // XXX: we should output a link to the no-link error explanation if (theDb.has(key)) cerr << "internal_error: no location for blob '" << key << "'" << endl; else cerr << "internal_error: reference to an undefined blob '" << key << "'" << endl; } foreach(tag.kids()); *theStream << ""; } void RepToHtmlFile::renderBlob(const XmlTag &tag) { const String &key = tag.attrs()->value("key"); *theStream << ""; const bool div = !tag.attrs()->has("dtype", "span"); if (div) renderSampleStart(tag, "div", CompositeSample::TheId); XmlSearchRes res; tag.kids()->selByTagName("description", res); if (res.count()) { //*theStream << "
"; foreach(tag.kids()); //*theStream << "
"; *theStream << "
" << endl; //*theStream << "
"; for (int i = 0; i < res.count(); ++i) { foreach(res[i]->kids()); *theStream << "
" << endl; } //*theStream << "
"; *theStream << "
" << endl; } else { foreach(tag.kids()); } if (div) *theStream << ""; } void RepToHtmlFile::renderMeasurementVal(const XmlTag &tag, const String &val, bool renderUnit, const String &unit) { XmlSearchRes images; if (renderUnit && tag.kids()->selByTagName("image", images)) { foreach(images.last()->kids()); } else { String typeId = unit.len() > 0 ? NumberSample::TheId : TextSample::TheId; if (const XmlAttr *a = tag.attrs()->has("typeId")) typeId = a->value(); renderSampleStart(*tag.parent(), "span", typeId); *theStream << val; *theStream << ""; if (renderUnit) *theStream << unit; } } void RepToHtmlFile::renderMeasurement(const XmlTag &tag) { if (tag.attrs()->has("value")) { String val = tag.attrs()->value("value"); String unit = tag.attrs()->value("unit"); if (unit == "xact" || unit == "conn") val = val(0, val.chr('.') - val.cstr()); else if (unit == "string") unit = ""; bool renderUnit = true; const XmlNode *p = tag.parent(); int parentDepth = 0; while (renderUnit) { if (!p) { if (parentDepth < theParents.count()) p = theParents.last(parentDepth++); } if (!p) break; if (p->name() == "table") { renderUnit = p->attrs()->has("border", "0"); break; } renderUnit = !p->attrs() || !p->attrs()->has("align", "right"); p = p->parent(); } if (!renderUnit) *theStream << ""; renderMeasurementVal(tag, val, renderUnit, unit); if (!renderUnit) *theStream << ""; } else { foreach(tag.kids()); } } void RepToHtmlFile::renderUl(const XmlTag &tag) { tag.printOpen(*theStream, ""); *theStream << ">" << endl; for (int i = 0; i < tag.kids()->count(); ++i) { *theStream << "\t
  • "; renderNode(*tag.kids()->item(i)); *theStream << "
  • " << endl; } *theStream << ""; } void RepToHtmlFile::renderTableCell(const XmlTag &tag) { tag.printOpen(*theStream, ""); if (tag.attrs()->has("emphasized")) *theStream << " bgcolor='#FFFFFF'"; *theStream << ">"; if (tag.kids()->count()) { if (tag.attrs()->has("disabled")) *theStream << ""; foreach(tag.kids()); if (tag.attrs()->has("disabled")) *theStream << ""; } else { *theStream << " "; } *theStream << "" << endl; } void RepToHtmlFile::renderImage(const XmlTag &tag) { *theStream << "count(); ++a) { const String &name = tag.attrs()->item(a)->name(); String value = tag.attrs()->item(a)->value(); if (name == "src") value = relativeUrl(theLocation, value); *theStream << ' ' << name << "='" << value << "'"; } *theStream << ">"; } void RepToHtmlFile::renderSampleStart(const XmlNode &n, const String &element, const String &typeId) { *theStream << "<" << element; if (const XmlAttrs *attrs = n.attrs()) { *theStream << " class=\"" << typeId << "\""; if (theQuoteLevel == 0) // non-authoritative includes are not IDed *theStream << " id=\"" << attrs->value("key") << "\""; const XmlAttr *title = attrs->has("title"); if (title && title->value().len() > 0 && title->value() != ReportBlob::NilTitle) title->print(*theStream << " ", String()); } *theStream << ">"; } String RepToHtmlFile::relativeUrl(const String &from, const String &to) const { String cur = from; // cut ancor off if (const char *ancor = cur.rchr('#')) cur = cur(0, ancor - cur.cstr()); // cut file name off if (const char *fname = cur.rchr('/')) cur = cur(0, fname+1 - cur.cstr()); else cur = ""; // get to the common root by replacing last dir with '..' String back = ""; while (cur && !cur.casePrefixOf(to.cstr(), to.len())) { const char *rdir = cur.rchr('/'); while (rdir > cur.cstr() && rdir[-1] == '/') --rdir; back += "../"; if (cur.cstr() < rdir) cur = cur(0, rdir-cur.cstr()); else cur = ""; } const String forth = to(cur.len(), to.len()); return back + forth; } String RepToHtmlFile::location(const String &key) const { if (const String loc = Location(key)) return relativeUrl(theLocation, loc); else return 0; }