// // File: hdsGraph.cpp // // Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free // Software Foundation, either version 3 of the License, or any later version. // // The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
#include <iostream> #include <fstream>
#include <unistd.h> #include <stdarg.h> #include <dirent.h> #include <netdb.h> #include <signal.h>
#include "hzErrcode.h" //#include "hzFsTbl.h" #include "hzDissemino.h"
using namespace std ;
/* ** Graphics Entity Constructors/Destructors */
#define FCG_NULL 0x00 // Graphic not assigned to a command #define FCG_START 0x01 // Graphic is a START stadium #define FCG_STEP 0x02 // Graphic is a START stadium #define FCG_IF 0x03 // Graphic is an IF hexagon #define FCG_ELSEIF 0x04 // Graphic is an ELSE-IF hexagon #define FCG_SWITCH 0x05 // Graphic is an DOWHILE loop hexagon #define FCG_CASE 0x06 // Graphic is an DOWHILE loop hexagon #define FCG_DEFAULT 0x07 // Graphic is an DOWHILE loop hexagon #define FCG_FOR 0x08 // Graphic is an FOR loop hexagon #define FCG_WHILE 0x09 // Graphic is an WHILE loop hexagon #define FCG_DOWHILE 0x0A // Graphic is an DOWHILE loop hexagon #define FCG_GOTO 0x0B // Graphic is a GOTO stadium #define FCG_BREAK 0x0C // Graphic is a GOTO stadium #define FCG_CONTINUE 0x0D // Graphic is a GOTO stadium #define FCG_RETURN 0x0E // Graphic is a GOTO stadium #define FCG_EXIT 0x0F // Graphic is a GOTO stadium
global hzString _hzGlobal_str_TRUE = "True" ; global hzString _hzGlobal_str_FALSE = "False" ; global hzString _hzGlobal_str_START = "START" ;
hdsGraphic::hdsGraphic (void) { m_eShape = HDSGRAPH_NULL ; m_ColorFill = 0x00ffffff ; m_ColorLine = 0 ; m_Thick = 1 ; m_Width = m_Height = 0 ; m_Lft = m_Rht = m_Top = m_Bot = 0 ; m_Rad = 0 ; m_Id = 0 ; }
hdsDiagram::hdsDiagram (hdsApp* pApp) { InitVE(pApp) ; m_pApp = pApp ; m_ColorFill = 0x00ffffff ; m_ColorLine = 0 ; m_Width = m_Height = 0 ; }
hdsFlowchart::hdsFlowchart (hdsApp* pApp) { InitVE(pApp) ; m_pApp = pApp ;
m_pShapes = 0 ; // Final list of graphic in flowchart m_ColorTerm = 0x0000ffff ; // Cyan m_ColorTest = 0x00ffffe0 ; // Light yellow m_ColorProc = 0x0066ff00 ; // Light green m_ColorLine = 0x00000000 ; // Black m_Height = m_Width = 0 ; // Caclulated from incident graphic objects m_nShapes = m_nConnects = 0 ; // No of graphic objects and connectors m_nBoundary = 0 ; // Width of boundary. Default 0 m_nWidthConn = 1 ; // Connector line width }
/* ** hdsText - please note, not a VE */
hdsText::hdsText (void) { Clear() ; }
void hdsText::Clear (void) { m_Text = (char*) 0 ; m_Font = (char*) 0 ; m_Color = 0 ; m_posV = m_posH = 0 ; }
// hdsLine hdsLine::operator= (const hdsLine& op) // { // m_Text = op.m_Text ; // m_Font = op.m_Font ; // m_Color = op.m_Color ; // m_posY = op.m_posY ; // m_posV = op.m_posV ; // }
hdsText hdsText::operator= (const hdsText& op) { m_Text = op.m_Text ; m_Font = op.m_Font ; m_Color = op.m_Color ; m_posH = op.m_posH ; m_posV = op.m_posV ; return *this ; }
void hdsText::Init (const hzString& text, const hzString& font, uint32_t color, int16_t posV, int16_t posH, int32_t align) { m_Text = text ; m_Font = font ; m_Color = color & 0x00ffffff ; m_posH = posH ; m_posV = posV ; m_Color |= align == 2 ? 0x02000000 : align == 1 ? 0x01000000 : 0 ; }
/* ** hdsChartPie */
hdsChartPie::hdsChartPie (hdsApp* pApp) { InitVE(pApp) ; m_Total = 0.0 ; m_Height = m_Width = 0 ; m_ccX = m_ccY = m_Rad = m_IdxX = m_IdxY = m_nSlices = 0 ; }
hdsChartPie::~hdsChartPie (void) { hzList<_pie*>::Iter pi ; // Pie chart component iterator _pie* ptr ; // Pie chart component
for (pi = m_Parts ; pi.Valid() ; pi++) { ptr = pi.Element() ; delete ptr ; } }
/* ** hdsChartBar */
hdsChartBar::hdsChartBar (hdsApp* pApp) { InitVE(pApp) ; m_nMinY = m_nMaxY = m_nMinX = m_nMaxX = 0 ; m_origX = m_origY = 0 ; }
hdsChartBar::~hdsChartBar (void) { hzList<_rset*>::Iter ri ; // Line chart dataset iterator _rset* ptr ; // Line chart dataset
for (ri = m_Sets ; ri.Valid() ; ri++) { ptr = ri.Element() ; delete ptr ; } }
/* ** hdsChartStd */
hdsChartStd::hdsChartStd (hdsApp* pApp) { InitVE(pApp) ; m_nMinY = m_nMaxY = m_nMinX = m_nMaxX = 0 ; m_origX = m_origY = 0 ; }
hdsChartStd::~hdsChartStd (void) { hzList<_rset*>::Iter ri ; // Line chart dataset iterator _rset* ptr ; // Line chart dataset
for (ri = m_Sets ; ri.Valid() ; ri++) { ptr = ri.Element() ; delete ptr ; } }
/* ** Graphics Config Functions */
hzEcode hdsApp::_readText (hdsText& tx, hzXmlNode* pN) { // Read a HTML canvas text entity // // Arguments: 1) tx The current text entity // 2) pN Current XML node // // Returns: E_SYNTAX If there is a syntax error // E_OK If the text entity was set OK
_hzfunc("hdsApp::_readText") ;
hzAttrset ai ; // Attribute iterator hzString text ; // Text content hzString font ; // Font uint32_t color ; // Color int16_t posH ; // H position int16_t posV ; // Y position int32_t align ; // Align - Left, center or right hzEcode rc = E_OK ; // Return code
if (!pN) Fatal("No node supplied\n") ; if (!pN->NameEQ("text")) Fatal("File %s Line %d: Incorrect node (%s) supplied. Must be <text>\n", *pN->Filename(), pN->Line(), pN->txtName()) ;
tx.Clear() ; for (ai = pN ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("str")) text = ai.Value() ; else if (ai.NameEQ("font")) font = ai.Value() ; else if (ai.NameEQ("ypos")) posH = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("xpos")) posV = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("color")) IsHexnum(color, ai.Value()) ; else if (ai.NameEQ("align")) { if (ai.ValEQ("left")) align = 0 ; else if (ai.ValEQ("center")) align = 1 ; else if (ai.ValEQ("right")) align = 2 ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <text> Bad alignment (%s)\n", *pN->Filename(), pN->Line(), ai.Value()) ; break ; } } else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <text> Bad param (%s=%s)\n", *pN->Filename(), pN->Line(), ai.Name(), ai.Value()) ; break ; } }
if (posH == -1) { rc=E_SYNTAX ; m_pLog->Log("File %s Line %d: <text> No ypos supplied\n", *pN->Filename(), pN->Line()) ; } if (posV == -1) { rc=E_SYNTAX ; m_pLog->Log("File %s Line %d: <text> No xpos supplied\n", *pN->Filename(), pN->Line()) ; } if (!text) { rc=E_SYNTAX ; m_pLog->Log("File %s Line %d: <text> No string supplied\n", *pN->Filename(), pN->Line()) ; }
if (rc == E_OK) tx.Init(text, font, color, posH, posV, align) ;
return rc ; }
hdsVE* hdsApp::_readChartPie (hzXmlNode* pN) { // Read in configs for a pie chart. The current XML node is assumed to be at an <xchartPie> tag // // Argument: pN The current XML node // // Returns: Pointer to the new pie chart as a visible entity // NULL If the any configuration errors occur
_hzfunc("hdsApp::_readChartPie") ;
hzArray<hdsChartPie::_pie> ar ; // Temp array of slices
hdsChartPie::_pie slice ; // Current slice hdsChartPie* pPie ; // The chart
hdsText TX ; // Text item hzAttrset ai ; // Attribute iterator hdsVE* thisVE ; // Visible entity (pie chart) hzXmlNode* pN1 ; // Subtag probe hzString cfg ; // Config source file hzString color ; // Color (hex string) hzString typ ; // Basetype uint32_t ln ; // Current line uint32_t bErr = 0 ; // Error hzEcode rc ; // Return code
if (!pN) Fatal("No node supplied\n") ; if (!pN->NameEQ("xchartPie")) Fatal("File %s Line %d: Incorrect node (%s) supplied. Must be <xchartPie>\n", *pN->Filename(), pN->Line(), pN->txtName()) ;
thisVE = pPie = new hdsChartPie(this) ; cfg = pN->Filename() ; thisVE->m_Line = pN->Line() ; thisVE->m_Indent = pN->Level() ;
// All <xchartPie> tags have attrs of id, type, header and footer and bgcolor for (ai = pN ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("id")) pPie->m_Id = ai.Value() ; else if (ai.NameEQ("font")) pPie->m_Font = ai.Value() ; else if (ai.NameEQ("dtype")) typ = ai.Value() ; else if (ai.NameEQ("header")) pPie->m_Header = ai.Value() ; else if (ai.NameEQ("footer")) pPie->m_Footer = ai.Value() ; else if (ai.NameEQ("bgcolor")) IsHexnum(pPie->m_BgColor, ai.Value()) ; else if (ai.NameEQ("linecolor")) IsHexnum(pPie->m_ColorLine, ai.Value()) ; else if (ai.NameEQ("fillcolor")) IsHexnum(pPie->m_ColorFill, ai.Value()) ; else if (ai.NameEQ("rad")) pPie->m_Rad = ai.Value() ? atoi(ai.Value()) : -1 ; else { bErr=1 ; m_pLog->Log("File %s Line %d: <xchartPie> Bad param (%s=%s)\n", *cfg, pN->Line(), ai.Name(), ai.Value()) ; } }
// Check values if (!pPie->m_Id) { bErr=1 ; m_pLog->Log("File %s Line %d: <xchart> Chart id not supplied\n", *cfg, pN->Line()) ; } if (!pPie->m_Font) { bErr=1; m_pLog->Log("File %s Line %d: <params> No font\n", *cfg, ln) ; } if (!pPie->m_Rad) { bErr=1; m_pLog->Log("File %s Line %d: <params> No radius\n", *cfg, ln) ; }
// Get other params. All types have headers and footer but different graphic and other configs for (pN1 = pN->GetFirstChild() ; !bErr && pN1 ; pN1 = pN1->Sibling()) { ln = pN1->Line() ;
if (pN1->NameEQ("text")) { rc = _readText(TX, pN1) ; pPie->m_Texts.Add(TX) ; } else if (pN1->NameEQ("part")) { slice.header.Clear() ; slice.color = 0 ; slice.m_nValue = 0.0 ;
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("name")) slice.header = ai.Value() ; else if (ai.NameEQ("color")) IsHexnum(slice.color, ai.Value()) ; else if (ai.NameEQ("value")) slice.m_nValue = ai.Value() ? atof(ai.Value()) : 0 ; else { bErr=1 ; m_pLog->Log("File %s Line %d: <part> Bad param (%s=%s)\n", *cfg, pN1->Line(), ai.Name(), ai.Value()) ; } }
if (!slice.header) { bErr=1; m_pLog->Log("File %s Line %d: <part> No name\n", *cfg, ln) ; } if (!slice.color) { bErr=1; m_pLog->Log("File %s Line %d: <part> Bad color\n", *cfg, ln) ; } if (!slice.m_nValue) { bErr=1; m_pLog->Log("File %s Line %d: <part> Bad value\n", *cfg, ln) ; }
pPie->m_Total += slice.m_nValue ; ar.Add(slice) ; } else { bErr=1 ; m_pLog->Log("File %s Line %d: <xchartPie> Bad subtag <%s>\n", *cfg, ln, pN1->txtName()) ; } }
if (bErr) { delete pPie ; return 0 ; }
// Add the slices to the pie pPie->m_pSlices = new hdsChartPie::_pie [ar.Count()] ;
for (pPie->m_nSlices = 0 ; pPie->m_nSlices < ar.Count() ; pPie->m_nSlices++) { pPie->m_pSlices[pPie->m_nSlices] = ar[pPie->m_nSlices] ; }
// Calculate dimensions. The step size in pixels and numbers of steps specified for each axis, gives the height and width of the graph itself. To this the following entities // are added:- // // - The graph header and footer (if any), will each add one line to the height // - The v-axis heading adds 15 pixels to the height // - The v-axis markers add to the width, but this depends on their maximum text size // - The h-axis markers add 15 pixels to the height // - The chart index (multiple data sets only), will either add one text line to the height or the width of the longest data set title
ln = pPie->m_Header ? 3 : 2 ; pPie->m_ccY = (ln * 20) + pPie->m_Rad ; pPie->m_ccX = 80 + pPie->m_Rad ; ln += pPie->m_Footer ? 1 : 0 ; pPie->m_Height = (ln * 20) + (pPie->m_Rad * 2) ; pPie->m_Height += 100 ; pPie->m_Width = pPie->m_ccX + pPie->m_Rad + 200 ;
pPie->m_IdxX = pPie->m_ccX - pPie->m_Rad ; pPie->m_IdxY = pPie->m_ccY + pPie->m_Rad + 40 ;
m_pLog->Log("Declared xchartPie parmas\n") ; return thisVE ; }
hdsVE* hdsApp::_readChartBar (hzXmlNode* pN) { // Category: HtmlGeneration // // Read in parameters for displaying a standard (bar or line) chart (graph) // // Argument: pN The current XML node // // Returns: Pointer to the new standard chart as a visible entity // NULL If the any configuration errors occur
_hzfunc("hdsApp::_readChartBar") ;
static hzString dfltFont = "10px Arial" ; // Default font settings
hdsChartBar* pChart ; // This chart
hdsChartBar::_rset* pSet ; // Dataset pointer
hdsText TX ; // Text item chIter zi ; // For reading comma separated values hzChain tmpChain ; // For processing explicit datasets hzChain W ; // For processing dataset values hzAttrset ai ; // Attribute iterator //hzNumPair np ; // For graph values hzXmlNode* pN1 ; // Subtag probe hdsVE* thisVE ; // Visible entity/active entity double stepSize ; // Axis step size double val ; // For processing dataset values hzString strStart ; // Axis start value hzString strIndex ; // Index (either horizontal, vertical or not specified) hzString tmp ; // Temp string (for comma separated values) hzString cfg ; // Config source file hzString strDatatype ; // V/H axis datatype hzString min ; // V/H axis min value hzString max ; // V/H axis max value hzString mark ; // V/H axis line marker hzString count ; // V/H axis value marker uint32_t ln ; // Line in file hdbBasetype eType ; // Basetype hzEcode rc = E_OK ; // Return code
if (!pN) Fatal("No node supplied\n") ; if (!pN->NameEQ("xchartBar")) Fatal("File %s Line %d: Incorrect node (%s) supplied. Must be <xchartBar>\n", *pN->Filename(), pN->Line(), pN->txtName()) ;
pSet = 0 ; thisVE = pChart = new hdsChartBar(this) ; cfg = pN->Filename() ; thisVE->m_Line = ln = pN->Line() ; thisVE->m_Indent = pN->Level() ;
// All <xchartStd> tags have attrs of id, type for (ai = pN ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("id")) pChart->m_Id = ai.Value() ; else if (ai.NameEQ("header")) pChart->m_Header = ai.Value() ; else if (ai.NameEQ("footer")) pChart->m_Footer = ai.Value() ; else if (ai.NameEQ("index")) strIndex = ai.Value() ; else if (ai.NameEQ("bgcolor")) IsHexnum(pChart->m_BgColor, ai.Value()) ; else if (ai.NameEQ("fgcolor")) IsHexnum(pChart->m_FgColor, ai.Value()) ; else if (ai.NameEQ("linecolor")) IsHexnum(pChart->m_ColorLine, ai.Value()) ; else if (ai.NameEQ("fillcolor")) IsHexnum(pChart->m_ColorFill, ai.Value()) ; else if (ai.NameEQ("height")) pChart->m_Height = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("width")) pChart->m_Width = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("font")) pChart->m_Font = ai.Value() ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartBar> Bad param (%s=%s)\n", *cfg, pN->Line(), ai.Name(), ai.Value()) ; } }
if (!pChart->m_Id) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartBar> No canvas id supplied\n", *cfg, ln) ; } if (!pChart->m_Height) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartBar> No canvas height\n", *cfg, ln) ; } if (!pChart->m_Width) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartBar> No canvas width\n", *cfg, ln) ; }
if (!pChart->m_Font) pChart->m_Font = dfltFont ;
m_pLog->Log("Declaring xchartBar (%s)\n", *pChart->m_Id) ;
// Get other params. All types have headers and footer but different graphic and other configs for (pN1 = pN->GetFirstChild() ; rc == E_OK && pN1 ; pN1 = pN1->Sibling()) { ln = pN1->Line() ;
if (pN1->NameEQ("axisY")) { strDatatype.Clear() ; min.Clear() ; max.Clear() ;
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("header")) pChart->m_HdrY = ai.Value() ; else if (ai.NameEQ("datatype")) strDatatype = ai.Value() ; else if (ai.NameEQ("start")) strStart = ai.Value() ; else if (ai.NameEQ("step")) stepSize = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("noSteps")) pChart->m_nSlotsY = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("slotPx")) pChart->m_nPxSlotY = ai.Value() ? atoi(ai.Value()) : 0 ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> Bad param (%s=%s)\n", *cfg, ln, ai.Name(), ai.Value()) ; } }
if (!pChart->m_HdrY) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No x-axis header\n", *cfg, ln) ; } if (!strDatatype) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No datatype set\n", *cfg, ln) ; } if (!strStart) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No min value\n", *cfg, ln) ; } if (!stepSize) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No max value\n", *cfg, ln) ; } if (!pChart->m_nSlotsY) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No number of slots\n", *cfg, ln) ; } if (!pChart->m_nPxSlotY) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No pixels per slot\n", *cfg, ln) ; }
if (rc != E_OK) break ;
eType = Str2Basetype(strDatatype) ; if (eType == BASETYPE_UNDEF) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> Illegal datatype (%s)\n", *cfg, ln, *strDatatype) ; break ; }
pChart->m_nMinY = atof(*strStart) ; pChart->m_nMaxY = pChart->m_nMinY + (pChart->m_nSlotsY * stepSize) ;
m_pLog->Log("Declared xchartStd x-axis range of (%f - %f) over %u pixels with steps of %u\n", *_fn, pChart->m_nMinY, pChart->m_nMaxY, pChart->m_nPxSlotY * pChart->m_nSlotsY, pChart->m_nPxSlotY) ; } else if (pN1->NameEQ("axisX")) { m_pLog->Log("Doing xchartStd y-axis\n") ; strDatatype.Clear() ; min.Clear() ; max.Clear() ;
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("header")) pChart->m_HdrX = ai.Value() ; else if (ai.NameEQ("datatype")) strDatatype = ai.Value() ; else if (ai.NameEQ("start")) strStart = ai.Value() ; else if (ai.NameEQ("step")) stepSize = ai.Value() ? atof(ai.Value()) : 0 ; else if (ai.NameEQ("noSteps")) pChart->m_nSlotsX = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("slotPx")) pChart->m_nPxSlotX = ai.Value() ? atoi(ai.Value()) : 0 ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> Bad param (%s=%s)\n", *cfg, ln, ai.Name(), ai.Value()) ; } }
if (!pChart->m_HdrX) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No x-axis header\n", *cfg, ln) ; } if (!strDatatype) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No datatype set\n", *cfg, ln) ; } if (!strStart) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No min value\n", *cfg, ln) ; } if (!stepSize) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No max value\n", *cfg, ln) ; } if (!pChart->m_nSlotsX) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No number of slots\n", *cfg, ln) ; } if (!pChart->m_nPxSlotX) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No pixels per slot\n", *cfg, ln) ; }
if (rc != E_OK) break ;
eType = Str2Basetype(strDatatype) ; if (eType == BASETYPE_UNDEF) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> Illegal datatype (%s)\n", *cfg, ln, *strDatatype) ; break ; }
pChart->m_nMinX = atof(*strStart) ; pChart->m_nMaxX = pChart->m_nMinX + (pChart->m_nSlotsX * stepSize) ;
m_pLog->Log("Declared xchartStd axisX range of (%f - %f) over %u pixels with steps of %u\n", *_fn, pChart->m_nMinX, pChart->m_nMaxX, pChart->m_nPxSlotX * pChart->m_nSlotsX, pChart->m_nPxSlotX) ;
for (val = pChart->m_nMinX ; val <= pChart->m_nMaxX ; val += stepSize) pChart->m_hVals.Add(val) ; } else if (pN1->NameEQ("dataset")) { // The <dataset> tag names the dataset and sets a color for the trace. The values are provided as a simple list of x-values. There need not be any // correlation between the number of y,x value pairs and the number of y-axis markers. There must be at least two y,x value pairs. The value pairs // will be of the form {y,x}
m_pLog->Log("Doing <dataset>\n") ; pSet = new hdsChartBar::_rset() ; pChart->m_Sets.Add(pSet) ;
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("color")) IsHexnum(pSet->color, ai.Value()) ; else if (ai.NameEQ("name")) pSet->header = ai.Value() ; else if (ai.NameEQ("data")) tmpChain = ai.Value() ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <dataset> Bad param (%s=%s)\n", *cfg, pN1->Line(), ai.Name(), ai.Value()) ; } }
if (!tmpChain.Size()) tmpChain = pN1->m_fixContent ;
for (zi = tmpChain ; !zi.eof() ; zi++) { for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
for (; !zi.eof() && (IsDigit(*zi) || *zi == '.') ; zi++) W.AddByte(*zi) ; tmp = W ; W.Clear() ; val = atof(*tmp) ; pSet->m_vVals.Add(val) ;
if (zi.eof()) break ;
if (*zi == CHAR_COMMA) continue ; rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <dataset> Expected a comma (got %c)\n", *cfg, ln, *zi) ; break ; } m_pLog->Log("Done <dataset> have now %d values\n", pSet->m_vVals.Count()) ; } else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartStd> Bad subtag %s\n", *cfg, pN1->Line(), pN1->txtName()) ; } }
// Calculate dimensions. The step size in pixels and numbers of steps specified for each axis, gives the height and width of the graph itself. To this the following entities // are added:- // // - The graph header and footer (if any), will each add one line to the height // - The v-axis heading adds 15 pixels to the height // - The v-axis markers add to the width, but this depends on their maximum text size // - The h-axis markers add 15 pixels to the height // - The chart index (multiple data sets only), will either add one text line to the height or the width of the longest data set title
if (rc != E_OK) { delete pChart ; return 0 ; }
ln = pChart->m_Header ? 3 : 2 ; pChart->m_origY = (ln * 20) + ((pChart->m_nSlotsY+1) * pChart->m_nPxSlotY) ; pChart->m_origX = 80 ; ln += pChart->m_Footer ? 1 : 0 ; pChart->m_Height = (ln * 20) + (pChart->m_nSlotsY * pChart->m_nPxSlotY) ; pChart->m_Height += 100 ; pChart->m_Width = pChart->m_nSlotsX * pChart->m_nPxSlotX ; pChart->m_Width += 120 ;
m_pLog->Log("Declared xchart parmas\n") ; return thisVE ; }
hdsVE* hdsApp::_readChartStd (hzXmlNode* pN) { // Category: HtmlGeneration // // Read in parameters for displaying a standard (bar or line) chart (graph) // // Argument: pN The current XML node // // Returns: Pointer to the new standard chart as a visible entity // NULL If the any configuration errors occur
_hzfunc("hdsApp::_readChartStd") ;
static hzString dfltFont = "10px Arial" ; // Default font settings
hdsChartStd* pChart ; // This chart
hdsChartStd::_rset* pSet ; // Dataset pointer
hdsText TX ; // Text item chIter zi ; // For reading comma separated values hzChain tmpChain ; // For processing explicit datasets hzChain W ; // For processing dataset values hzAttrset ai ; // Attribute iterator hzXmlNode* pN1 ; // Subtag probe hdsVE* thisVE ; // Visible entity/active entity double stepSize ; // Axis step size double val ; // Working value hzString strStart ; // Axis start value hzString strIndex ; // Index (either horizontal, vertical or not specified) hzString tmp ; // Temp string (for comma separated values) hzString cfg ; // Config source file hzString strDatatype ; // V/H axis datatype hzString min ; // V/H axis min value hzString max ; // V/H axis max value hzString mark ; // V/H axis line marker hzString count ; // V/H axis value marker uint32_t ln ; // Line in file hdbBasetype eType ; // Basetype hzEcode rc = E_OK ; // Return code
if (!pN) Fatal("No node supplied\n") ; if (!pN->NameEQ("xchartStd")) Fatal("File %s Line %d: Incorrect node (%s) supplied. Must be <xchartStd>\n", *pN->Filename(), pN->Line(), pN->txtName()) ;
pSet = 0 ; thisVE = pChart = new hdsChartStd(this) ; cfg = pN->Filename() ; thisVE->m_Line = ln = pN->Line() ; thisVE->m_Indent = pN->Level() ;
// All <xchartStd> tags have attrs of id, type for (ai = pN ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("id")) pChart->m_Id = ai.Value() ; else if (ai.NameEQ("header")) pChart->m_Header = ai.Value() ; else if (ai.NameEQ("footer")) pChart->m_Footer = ai.Value() ; else if (ai.NameEQ("index")) strIndex = ai.Value() ; else if (ai.NameEQ("bgcolor")) IsHexnum(pChart->m_BgColor, ai.Value()) ; else if (ai.NameEQ("fgcolor")) IsHexnum(pChart->m_FgColor, ai.Value()) ; else if (ai.NameEQ("linecolor")) IsHexnum(pChart->m_ColorLine, ai.Value()) ; else if (ai.NameEQ("fillcolor")) IsHexnum(pChart->m_ColorFill, ai.Value()) ; else if (ai.NameEQ("height")) pChart->m_Height = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("width")) pChart->m_Width = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("font")) pChart->m_Font = ai.Value() ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartStd> Bad param (%s=%s)\n", *cfg, pN->Line(), ai.Name(), ai.Value()) ; } }
if (!pChart->m_Id) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartStd> No canvas id supplied\n", *cfg, ln) ; } if (!pChart->m_Height) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartStd> No canvas height\n", *cfg, ln) ; } if (!pChart->m_Width) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartStd> No canvas width\n", *cfg, ln) ; }
if (!pChart->m_Font) pChart->m_Font = dfltFont ;
m_pLog->Log("Declaring xchartStd (%s)\n", *pChart->m_Id) ;
// Get other params. All types have headers and footer but different graphic and other configs for (pN1 = pN->GetFirstChild() ; rc == E_OK && pN1 ; pN1 = pN1->Sibling()) { ln = pN1->Line() ;
if (pN1->NameEQ("axisY")) { // Vertical axis parameters strDatatype.Clear() ; min.Clear() ; max.Clear() ;
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("header")) pChart->m_HdrY = ai.Value() ; else if (ai.NameEQ("datatype")) strDatatype = ai.Value() ; else if (ai.NameEQ("start")) strStart = ai.Value() ; else if (ai.NameEQ("step")) stepSize = ai.Value() ? atof(ai.Value()) : 0 ; else if (ai.NameEQ("noSteps")) pChart->m_nSlotsY = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("slotPx")) pChart->m_nPxSlotY = ai.Value() ? atoi(ai.Value()) : 0 ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> Bad param (%s=%s)\n", *cfg, ln, ai.Name(), ai.Value()) ; } }
if (!pChart->m_HdrY) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No header\n", *cfg, ln) ; } if (!strDatatype) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No datatype set\n", *cfg, ln) ; } if (!strStart) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No origin value\n", *cfg, ln) ; } if (!stepSize) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No step size\n", *cfg, ln) ; } if (!pChart->m_nSlotsY) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No number of slots\n", *cfg, ln) ; } if (!pChart->m_nPxSlotY) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> No pixels per slot\n", *cfg, ln) ; }
if (rc != E_OK) break ;
eType = Str2Basetype(strDatatype) ; if (eType == BASETYPE_UNDEF) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisY> Illegal datatype (%s)\n", *cfg, ln, *strDatatype) ; break ; }
pChart->m_nMinY = atof(*strStart) ; pChart->m_nMaxY = pChart->m_nMinY + (pChart->m_nSlotsY * stepSize) ;
m_pLog->Log("Declared xchartStd x-axis range of (%f - %f) over %u pixels with steps of %u\n", *_fn, pChart->m_nMinY, pChart->m_nMaxY, pChart->m_nPxSlotY * pChart->m_nSlotsY, pChart->m_nPxSlotY) ; } else if (pN1->NameEQ("axisX")) { // Horizontal axis parameters strDatatype.Clear() ; min.Clear() ; max.Clear() ;
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("header")) pChart->m_HdrX = ai.Value() ; else if (ai.NameEQ("datatype")) strDatatype = ai.Value() ; else if (ai.NameEQ("start")) strStart = ai.Value() ; else if (ai.NameEQ("step")) stepSize = ai.Value() ? atof(ai.Value()) : 0 ; else if (ai.NameEQ("noSteps")) pChart->m_nSlotsX = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("slotPx")) pChart->m_nPxSlotX = ai.Value() ? atoi(ai.Value()) : 0 ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> Bad param (%s=%s)\n", *cfg, ln, ai.Name(), ai.Value()) ; } }
if (!pChart->m_HdrX) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No header\n", *cfg, ln) ; } if (!strDatatype) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No datatype set\n", *cfg, ln) ; } if (!strStart) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No origin value\n", *cfg, ln) ; } if (!stepSize) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No step size\n", *cfg, ln) ; } if (!pChart->m_nSlotsX) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No number of slots\n", *cfg, ln) ; } if (!pChart->m_nPxSlotX) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> No pixels per slot\n", *cfg, ln) ; }
if (rc != E_OK) break ;
eType = Str2Basetype(strDatatype) ; if (eType == BASETYPE_UNDEF) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <axisX> Illegal datatype (%s)\n", *cfg, ln, *strDatatype) ; break ; }
pChart->m_nMinX = atof(*strStart) ; pChart->m_nMaxX = pChart->m_nMinX + (pChart->m_nSlotsX * stepSize) ;
m_pLog->Log("Declared xchartStd axisX range of (%f - %f) over %u pixels with steps of %u\n", *_fn, pChart->m_nMinX, pChart->m_nMaxX, pChart->m_nPxSlotX * pChart->m_nSlotsX, pChart->m_nPxSlotX) ;
for (val = pChart->m_nMinX ; val <= pChart->m_nMaxX ; val += stepSize) pChart->m_hVals.Add(val) ; } else if (pN1->NameEQ("dataset")) { // The <dataset> tag names the dataset and sets a color for the trace. The values are provided as a simple list of x-values. There need not be any // correlation between the number of y,x value pairs and the number of y-axis markers. There must be at least two y,x value pairs. The value pairs // will be of the form {y,x}
m_pLog->Log("Doing <dataset>\n") ; pSet = new hdsChartStd::_rset() ; pChart->m_Sets.Add(pSet) ;
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("color")) IsHexnum(pSet->color, ai.Value()) ; else if (ai.NameEQ("name")) pSet->header = ai.Value() ; else if (ai.NameEQ("data")) tmpChain = ai.Value() ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <dataset> Bad param (%s=%s)\n", *cfg, pN1->Line(), ai.Name(), ai.Value()) ; } }
if (!tmpChain.Size()) tmpChain = pN1->m_fixContent ;
for (zi = tmpChain ; !zi.eof() ; zi++) { for (; !zi.eof() && *zi <= CHAR_SPACE ; zi++) ;
for (; !zi.eof() && (IsDigit(*zi) || *zi == '.') ; zi++) W.AddByte(*zi) ; tmp = W ; W.Clear() ; val = atof(*tmp) ; pSet->m_vVals.Add(val) ;
if (zi.eof()) break ;
if (*zi == CHAR_COMMA) continue ; rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <dataset> Expected a comma (got %c)\n", *cfg, ln, *zi) ; break ; } m_pLog->Log("Done <dataset> have now %d values\n", pSet->m_vVals.Count()) ; } else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xchartStd> Bad subtag %s\n", *cfg, pN1->Line(), pN1->txtName()) ; } }
if (rc != E_OK) { delete pChart ; return 0 ; }
// Calculate dimensions: The header adds 1 line, as does the footer, the Y-axis heading and the X-axis markers. If an index applies and is horizonal, yet another line will be // needed.
ln = pChart->m_Header ? 3 : 2 ; pChart->m_origY = (ln * 20) + ((pChart->m_nSlotsY+1) * pChart->m_nPxSlotY) ; pChart->m_origX = 80 ; ln += pChart->m_Footer ? 1 : 0 ; pChart->m_Height = (ln * 20) + (pChart->m_nSlotsY * pChart->m_nPxSlotY) ; pChart->m_Height += 100 ; pChart->m_Width = pChart->m_nPxSlotX * (pChart->m_nSlotsX + 1) ; pChart->m_Width += 120 ;
m_pLog->Log("Declared xchart parmas\n") ; return thisVE ; }
#if 0 hzEcode hdsApp::_readShapes (hzXmlNode* pN, hdsDiagram* pDiag) { // Read in a single diagram component // // Arguments: 1) pN The current XML node expected to be a <shapes> tag // 2) pDiag The current diagram // // Returns: E_SYNTAX If there is a syntax error // E_OK If no errors occured
_hzfunc("hdsApp::_readShape") ;
hzMapS<hzString,hdsGraphic*> tmp ; // Map of graphics (for connectors)
hdsGraphic* pShape = 0 ; // Diagram component (new) hdsGraphic* pShape_x = 0 ; // Diagram component (previously existing)
hzAttrset ai ; // Attribute iterator hzXmlNode* pN1 ; // Subtag probe hzString cfg ; // Config source file hzString name ; // Node name hzString from ; // For connector from (shape id) hzString to ; // For connector to (shape id) uint32_t dfltFillColor = -1 ; // Default fill color uint32_t ln ; // Line number uint32_t flag ; // Flags to control which attributes apply to which shape types uint32_t maxH = 0 ; // Highest y-coord uint32_t maxV = 0 ; // Highest x-coord //uint32_t bErr = 0 ; // Error if set hzEcode rc = E_OK ; // Return code
if (!pN) Fatal("No node supplied\n") ; if (!pN->NameEQ("shapes")) Fatal("Incorrect node (%s) supplied. Must be <shapes>\n", pN->txtName()) ; if (!pDiag) Fatal("No diagram supplied\n") ;
cfg = pN->Filename() ;
// Process params here for (ai = pN ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("fillcolor")) IsHexnum(dfltFillColor, ai.Value()) ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> Bad param (%s=%s)\n", *cfg, ln, pN->txtName(), ai.Name(), ai.Value()) ; } }
// Process child nodes (shapes) for (pN1 = pN->GetFirstChild() ; rc == E_OK && pN1 ; pN1 = pN1->Sibling()) { ln = pN1->Line() ; name = pN1->txtName() ;
pShape = new hdsGraphic() ; pDiag->m_Shapes.Add(pShape) ;
if (name == "line") { flag = 0x04FF ; pShape->m_eShape = HDSGRAPH_LINE ; } else if (name == "diamond") { flag = 0x04FF ; pShape->m_eShape = HDSGRAPH_DIAMOND ; } else if (name == "arrow") { flag = 0x0CFF ; pShape->m_eShape = HDSGRAPH_ARROW ; } else if (name == "rect") { flag = 0x04FF ; pShape->m_eShape = HDSGRAPH_RECT ; } else if (name == "rrect") { flag = 0x04FF ; pShape->m_eShape = HDSGRAPH_RRECT ; } else if (name == "stadium") { flag = 0x04FF ; pShape->m_eShape = HDSGRAPH_STADIUM ; } else if (name == "circle") { flag = 0x01FF ; pShape->m_eShape = HDSGRAPH_CIRCLE ; } else if (name == "gateAND") { flag = 0x04F1 ; pShape->m_eShape = HDSGRAPH_LGATE_AND ; } else if (name == "gateOR") { flag = 0x04F1 ; pShape->m_eShape = HDSGRAPH_LGATE_OR ; } else if (name == "gateNOT") { flag = 0x04F1 ; pShape->m_eShape = HDSGRAPH_LGATE_NOT ; } else if (name == "gateNAND") { flag = 0x04F1 ; pShape->m_eShape = HDSGRAPH_LGATE_NAND ; } else if (name == "gateNOR") { flag = 0x04FD ; pShape->m_eShape = HDSGRAPH_LGATE_NOR ; } else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xdiagram> Bad subtag <%s>\n", *cfg, ln, *name) ; }
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (flag & 0x0001 && ai.NameEQ("id")) pShape->m_Uid = ai.Value() ; else if (flag & 0x0002 && ai.NameEQ("text")) pShape->m_Text = ai.Value() ; //else if (flag & 0x0004 && ai.NameEQ("txH")) pShape->text_y = ai.Value() ? atoi(ai.Value()) : -1 ; //else if (flag & 0x0008 && ai.NameEQ("txV")) pShape->text_x = ai.Value() ? atoi(ai.Value()) : -1 ; else if (flag & 0x0010 && ai.NameEQ("top")) pShape->m_Rect.m_Top = ai.Value() ? atoi(ai.Value()) : -1 ; else if (flag & 0x0020 && ai.NameEQ("bot")) pShape->m_Rect.m_Bot = ai.Value() ? atoi(ai.Value()) : -1 ; else if (flag & 0x0040 && ai.NameEQ("lft")) pShape->m_Rect.m_Lft = ai.Value() ? atoi(ai.Value()) : -1 ; else if (flag & 0x0080 && ai.NameEQ("rht")) pShape->m_Rect.m_Rht = ai.Value() ? atoi(ai.Value()) : -1 ; else if (flag & 0x0100 && ai.NameEQ("rad")) pShape->m_Rad = ai.Value() ? atoi(ai.Value()) : -1 ; else if (flag & 0x0200 && ai.NameEQ("width")) pShape->m_Width = ai.Value() ? atoi(ai.Value()) : -1 ; else if (flag & 0x0400 && ai.NameEQ("thick")) pShape->m_Thick = ai.Value() ? atoi(ai.Value()) : -1 ; //else if (flag & 0x0800 && ai.NameEQ("head")) pShape->head = ai.Value() ? atoi(ai.Value()) : -1 ; else if (flag & 0x1000 && ai.NameEQ("from")) from = ai.Value() ; else if (flag & 0x2000 && ai.NameEQ("to")) to = ai.Value() ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> Bad param (%s=%s)\n", *cfg, ln, *name, ai.Name(), ai.Value()) ; } }
if (pShape->m_eShape != HDSGRAPH_LINE && !pShape->m_Uid) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> No shape id supplied\n", *cfg, ln, *name) ; } else { if (tmp.Exists(pShape->m_Uid)) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> Duplicate shape id (%s)\n", *cfg, ln, *name, *pShape->m_Uid) ; } else tmp.Insert(pShape->m_Uid, pShape) ; }
/* if (pShape->type == HDSGRAPH_CONNECT) { if (!from) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%s> No from shape id supplied\n", *cfg, ln, *name) ; } if (!to) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%s> No from shape id supplied\n", *cfg, ln, *name) ; }
pShape_x = tmp[pShape->uid] ; if (!pShape_x) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> Cannot locate shape %s\n", *cfg, ln, *name, *pShape->uid) ; } else pShape->from = pShape_x->nid ;
pShape_x = tmp[pShape->uid] ; if (!pShape_x) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> Cannot locate shape %s\n", *cfg, ln, *name, *pShape->uid) ; } else pShape->to = pShape_x->nid ;
from = (char*) 0 ; to = (char*) 0 ; continue ; } */
if (pShape->m_Rect.m_Lft == -1) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%s> No Lft coord supplied\n", *cfg, ln, *name, *pShape->m_Uid) ; } if (pShape->m_Rect.m_Top == -1) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%s> No Top coord supplied\n", *cfg, ln, *name, *pShape->m_Uid) ; } if (pShape->m_Rect.m_Rht == -1) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%s> No Rht coord supplied\n", *cfg, ln, *name, *pShape->m_Uid) ; } if (pShape->m_Rect.m_Bot == -1) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%s> No Bot coord supplied\n", *cfg, ln, *name, *pShape->m_Uid) ; }
if (pShape->m_Rect.m_Top > pShape->m_Rect.m_Bot) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%s> Top/Bot inversion\n", *cfg, ln, *name, *pShape->m_Uid) ; } if (pShape->m_Rect.m_Lft > pShape->m_Rect.m_Rht) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%s> Lft/Rht reversal\n", *cfg, ln, *name, *pShape->m_Uid) ; }
if (rc != E_OK) break ;
// Set canvas limits if (pShape->m_Rect.m_Bot > maxV) maxV = pShape->m_Rect.m_Bot ; if (pShape->m_Rect.m_Rht > maxH) maxH = pShape->m_Rect.m_Rht ;
// Set width and height pShape->m_Width = pShape->m_Rect.m_Rht - pShape->m_Rect.m_Lft ; pShape->m_Height = pShape->m_Rect.m_Bot - pShape->m_Rect.m_Top ;
// Set connection points //pShape->northH = pShape->coords.m_Lft + (pShape->width/2) ; //pShape->northV = pShape->coords.m_Top ;
//pShape->eastH = pShape->coords.m_Rht ; //pShape->eastV = pShape->coords.m_Top + (pShape->height/2) ;
//pShape->southH = pShape->coords.m_Lft + (pShape->width/2) ; //pShape->southV = pShape->coords.m_Bot ;
//pShape->westH = pShape->coords.m_Lft ; //pShape->westV = pShape->coords.m_Top + (pShape->height/2) ;
if (pShape->m_eShape == HDSGRAPH_LINE) { // By convention, lines are drawn from y1/x1 (top left) to y2/x2 (bottom right) }
if (pShape->m_eShape == HDSGRAPH_STADIUM) { pShape->m_Rad = (pShape->m_Rect.m_Bot - pShape->m_Rect.m_Top)/2 ; //pShape->text_y = pShape->coords.m_Lft + pShape->rad ; //pShape->text_x = pShape->coords.m_Top + pShape->rad ; }
if (pShape->m_ColorFill == 0) { if (dfltFillColor == 0) pShape->m_ColorFill = 0 ; else pShape->m_ColorFill = dfltFillColor ; } }
return rc ; } #endif
hdsVE* hdsApp::_readDiagram (hzXmlNode* pN) { // Read in diagram components. // // <xdiagram id="mydiagram" header="flowchart"> // <stadium top="20" bot="70" lft="50" rht="150" text="Start"/> // <arrow from="70,100" to"120,100" width="2" head="10" color="0"/> // <diamond top="120" bot="220" lft="50" rht="150" text="Are we ready?"/> // <arrow from="170,150" to"70,100" width="2" head="10" color="0" text="N"/> // <arrow from="220,100" to"270,100" width="2" head="10" color="0" text="Y"/> // <stadium top="270" bot="320" lft="50" rht="150" text="Go!"/> // </xdiagram> // // Argument: pN The current XML node expected to be a <xdiagram> tag // // Returns: Pointer to diagram visual entity
_hzfunc("hdsApp::_readDiagram") ;
hzSet<uint16_t> tmp ; // Set of graphic object ids already found
hdsText TX ; // Diagram Text component hdsLine LN ; // Diagram Line component hdsGraphic gObj ; // Current grapgic object //hdsConnector con ; // Current connector hzAttrset ai ; // Attribute iterator hzXmlNode* pN1 ; // Subtag probe hdsDiagram* pDiag ; // This diagram hdsVE* thisVE ; // Visible entity/active entity hzString cfg ; // Config source file hzString strXid ; // Text representation of graphic object id uint32_t ln ; // Line number uint32_t maxH = 0 ; // Highest y-coord uint32_t maxV = 0 ; // Highest x-coord hzEcode rc = E_OK ; // Return code
if (!pN) Fatal("No node supplied\n") ; if (!pN->NameEQ("xdiagram")) Fatal("File %s Line %d: Incorrect node (%s) supplied. Must be <xdiagram>\n", *pN->Filename(), pN->Line(), pN->txtName()) ;
thisVE = pDiag = new hdsDiagram(this) ; cfg = pN->Filename() ; thisVE->m_Line = ln = pN->Line() ; thisVE->m_Indent = pN->Level() ;
// Get diagram params for (ai = pN ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("id")) pDiag->m_Id = ai.Value() ; else if (ai.NameEQ("bgcolor")) IsHexnum(pDiag->m_BgColor, ai.Value()) ; else if (ai.NameEQ("fgcolor")) IsHexnum(pDiag->m_FgColor, ai.Value()) ; else if (ai.NameEQ("linecolor")) IsHexnum(pDiag->m_ColorLine, ai.Value()) ; else if (ai.NameEQ("fillcolor")) IsHexnum(pDiag->m_ColorFill, ai.Value()) ; else if (ai.NameEQ("height")) pDiag->m_Height = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("width")) pDiag->m_Width = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("font")) pDiag->m_Font = ai.Value() ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xdiagram> Bad param (%s=%s)\n", *cfg, ln, ai.Name(), ai.Value()) ; } }
// Check values if (!pDiag->m_Id) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xdiagram> No canvas id\n", *cfg, ln) ; } if (!pDiag->m_Height) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xdiagram> No canvas height\n", *cfg, ln) ; } if (!pDiag->m_Width) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xdiagram> No canvas width\n", *cfg, ln) ; }
if (rc != E_OK) { delete pDiag ; return 0 ; }
// Get components for (pN1 = pN->GetFirstChild() ; rc == E_OK && pN1 ; pN1 = pN1->Sibling()) { ln = pN1->Line() ;
if (pN1->NameEQ("line")) { LN.Clear() ;
for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("color")) IsHexnum(LN.m_Color, ai.Value()) ; else if (ai.NameEQ("y1")) LN.m_startH = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("x1")) LN.m_startV = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("y2")) LN.m_finalH = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("x2")) LN.m_finalV = ai.Value() ? atoi(ai.Value()) : -1 ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <line> Bad param (%s=%s)\n", *cfg, ln, ai.Name(), ai.Value()) ; } }
pDiag->m_Lines.Add(LN) ; continue ; }
/* if (pN1->NameEQ("connector")) { rc = _readConnector(con, pN1) ; if (rc == E_OK) pDiag->m_Connectors.Add(con) ; continue ; } */
if (pN1->NameEQ("text")) { rc = _readText(TX, pN1) ; if (rc == E_OK) pDiag->m_Texts.Add(TX) ; continue ; }
if (pN1->NameEQ("diamond")) gObj.m_eShape = HDSGRAPH_DIAMOND ; else if (pN1->NameEQ("arrow")) gObj.m_eShape = HDSGRAPH_ARROW ; else if (pN1->NameEQ("rect")) gObj.m_eShape = HDSGRAPH_RECT ; else if (pN1->NameEQ("rrect")) gObj.m_eShape = HDSGRAPH_RRECT ; else if (pN1->NameEQ("stadium")) gObj.m_eShape = HDSGRAPH_STADIUM ; else if (pN1->NameEQ("circle")) gObj.m_eShape = HDSGRAPH_CIRCLE ; else if (pN1->NameEQ("gateAND")) gObj.m_eShape = HDSGRAPH_LGATE_AND ; else if (pN1->NameEQ("gateOR")) gObj.m_eShape = HDSGRAPH_LGATE_OR ; else if (pN1->NameEQ("gateNOT")) gObj.m_eShape = HDSGRAPH_LGATE_NOT ; else if (pN1->NameEQ("gateNAND")) gObj.m_eShape = HDSGRAPH_LGATE_NAND ; else if (pN1->NameEQ("gateNOR")) gObj.m_eShape = HDSGRAPH_LGATE_NOR ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xdiagram> Bad subtag <%s>\n", *pN1->Filename(), pN1->Line(), pN1->txtName()) ; }
strXid.Clear() ; for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("id")) strXid = ai.Value() ; else if (ai.NameEQ("text")) gObj.m_Text = ai.Value() ; else if (ai.NameEQ("font")) gObj.m_Font = ai.Value() ; else if (ai.NameEQ("top")) gObj.m_Top = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("bot")) gObj.m_Bot = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("lft")) gObj.m_Lft = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("rht")) gObj.m_Rht = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("rad")) gObj.m_Rad = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("width")) gObj.m_Width = ai.Value() ? atoi(ai.Value()) : -1 ; else if (ai.NameEQ("thick")) gObj.m_Thick = ai.Value() ? atoi(ai.Value()) : -1 ; //else if (ai.NameEQ("from")) from = ai.Value() ; //else if (ai.NameEQ("to")) to = ai.Value() ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> Bad param (%s=%s)\n", *pN1->Filename(), pN1->Line(), pN1->txtName(), ai.Name(), ai.Value()) ; } }
// Check graphic object id is set and is not a duplicate if (!strXid) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> No shape id supplied\n", *pN1->Filename(), pN1->Line(), pN1->txtName()) ; } else { gObj.m_Id = atoi(*strXid) ; if (tmp.Exists(gObj.m_Id)) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <%s> Duplicate shape id (%u)\n", *cfg, ln, pN1->txtName(), gObj.m_Id) ; } else tmp.Insert(gObj.m_Id) ; }
// Check other parameters if (gObj.m_Lft == -1) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%u> No Lft coord supplied\n", *cfg, ln, pN1->txtName(), gObj.m_Id) ; } if (gObj.m_Top == -1) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%u> No Top coord supplied\n", *cfg, ln, pN1->txtName(), gObj.m_Id) ; } if (gObj.m_Rht == -1) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%u> No Rht coord supplied\n", *cfg, ln, pN1->txtName(), gObj.m_Id) ; } if (gObj.m_Bot == -1) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%u> No Bot coord supplied\n", *cfg, ln, pN1->txtName(), gObj.m_Id) ; }
if (gObj.m_Top > gObj.m_Bot) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%u> Top/Bot inversion\n", *cfg, ln, pN1->txtName(), gObj.m_Id) ; } if (gObj.m_Lft > gObj.m_Rht) { rc = E_SYNTAX; m_pLog->Log("File %s Line %d: <%u> Lft/Rht reversal\n", *cfg, ln, pN1->txtName(), gObj.m_Id) ; }
if (rc != E_OK) break ;
// Set canvas limits if (gObj.m_Bot > maxV) maxV = gObj.m_Bot ; if (gObj.m_Rht > maxH) maxH = gObj.m_Rht ;
// Set width and height gObj.m_Width = gObj.m_Rht - gObj.m_Lft ; gObj.m_Height = gObj.m_Bot - gObj.m_Top ;
// Add text if (gObj.m_Text) { //TX.Init(Text, Font, color, posH, posV, align) ; TX.Init(gObj.m_Text, gObj.m_Font, 0, gObj.m_Lft, gObj.m_Top, 1) ; pDiag->m_Texts.Add(TX) ; }
// Set connection points
if (gObj.m_eShape == HDSGRAPH_STADIUM) { gObj.m_Rad = (gObj.m_Bot - gObj.m_Top)/2 ; }
if (gObj.m_ColorFill == 0) gObj.m_ColorFill = pDiag->m_ColorFill ;
pDiag->m_Shapes.Add(gObj) ; //if (pN1->NameEQ("shapes")) rc = _readShapes(pN1, pDiag) ; }
// Calculate dimensions if (rc != E_OK) { delete pDiag ; return 0 ; }
m_pLog->Log("Declared xchart parmas\n") ; return thisVE ; }
/* ** Graphic Spatial Calculations */
class hzPauli { // Represents a diagram component which in all cases, is deemed to occupy a rectangle with vertical and horizontal sides, within the diagram space. // // The basic idea is that diagram components (graphics and lines), should never intersect. In order to determine if a proposed rectangle would intersect with an existing one, // the rectangles (hzPauli instances) are held in a map. This requires hzPauli to support the less than and greater than operators. For this purpose the horizontal X-axis is // considered 'superior' to the vertical Y-axis.
public: uint32_t m_X ; uint32_t m_Y ;
hzPauli (void) { m_X = m_Y = 0 ; }
bool operator< (const hzPauli& op) { if (m_X > op.m_X) return false ; if (m_X < op.m_X) return true ;
return (m_Y < op.m_Y) ; }
bool operator> (const hzPauli& op) { if (m_X < op.m_X) return false ; if (m_X > op.m_X) return true ;
return (m_Y > op.m_Y) ; } } ;
class hzSpace2D { hzSet<hzPauli> m_Shapes ; // Diagram components } ;
/* ** Flowcharts */
enum _fc_type { // Statement type
STMT_NULL, // No command
// Applicable at the file level STMT_USING, // Tells lookup functions to add a namespace to the search STMT_NAMESPACE, // Declares a namespace and makes it current (all things from now belong to it) STMT_TYPEDEF, // Typedef (typedef existing_type new_type) STMT_CLASS_DCL, // Class/Forward declaration of a class or struct STMT_CLASS_DEF, // Class definition STMT_CTMPL_DEF, // Class template definition STMT_UNION_DCL, // Forward declaration of a union STMT_UNION_DEF, // Union definition STMT_ENUM_DCL, // Forward declaration of an enum STMT_ENUM_DEF, // Enum definition STMT_FUNC_DCL, // Function prototype STMT_FUNC_DEF, // Function definition STMT_FTMPL_DEF, // Function template definition
// Variable declarations STMT_VARDCL_FNPTR, // Function ptr declaration: Of the form [keywords] typlex (*var_name)(type_args) ; STMT_VARDCL_FNASS, // Function ptr declation and assignement: Of the form [keywords] typlex (*var_name)(type_args) = function_addr ; STMT_VARDCL_STD, // Variable declarations: Of the form [keywords] typlex var_name ; STMT_VARDCL_ASSIG, // Variable declaration and assignment: Of the form [keywords] typlex var_name = exp ; STMT_VARDCL_ARRAY, // Variable array declarations: Of the form [keywords] typlex var_name[noElements] ; STMT_VARDCL_ARASS, // Variable array declaration and assignment: Of the form [keywords] typlex var_name[noElements] = { values } ; STMT_VARDCL_CONS, // Variable declarations: Of the form [keywords] class/typlex var_name(value_args) ; // Branches STMT_BRANCH_IF, // If: Of the form if (condition) then statement/statement block STMT_BRANCH_ELSE, // Else: An 'else' then statement/statement block. The 'else' must follow an if or an else STMT_BRANCH_ELSEIF, // Else: An 'else' then statement/statement block. The 'else' must follow an if or an else STMT_BRANCH_FOR, // For: Of the form for (statements ; condition ; statements) followed by statement/statement block STMT_BRANCH_DOWHILE, // Do while: Of the form do statement block while (condition) STMT_BRANCH_WHILE, // While: Of the form while (condition) then statement/statement block STMT_BRANCH_SWITCH, // Switches: Of the form switch (value) then block of switch cases STMT_BRANCH_CASE, // Switch cases: Of the form case value: then series of statements STMT_BRANCH_DEFAULT, // Switch defaul case: Of the form case value: then series of statements
// Operations STMT_BLOC, // Start of a block of non-branching operations STMT_STEP, // A single non-branching operation STMT_VAR_INCA, // Variable increments: Of the form var++ STMT_VAR_INCB, // Variable increments: Of the form ++var STMT_VAR_DECA, // Variable decrements: Of the form var-- STMT_VAR_DECB, // Variable decrements: Of the form --var STMT_VAR_ASSIGN, // Variable assignments: Of the form var = evalutable expression STMT_VAR_MATH, // Variable operations: Of the form var +=, -=, *= or /= etc followed by an evalutable expresion STMT_FUNC_CALL, // Function calls: Of the form funcname(args) STMT_DELETE, // Delete variable: Of the form delete varname ;
// Jumps and exits STMT_CONTINUE, // To top of for/while loop STMT_BREAK, // Break out of loop or case block STMT_LABEL, // Goto STMT_GOTO, // Label STMT_RETURN, // Return from function STMT_EXIT // Terminate execution } ;
class _fc_item { // The _fc_item struct is a temporary support class for the _readFlowItem function. The purpose is to give hdsGraphic extra members during the parsing of flowchart XML, needed // to assign links between the flowchart items (graphics). // // Flowchart items are either:- // // START Stadia No incoming connector; One outgoing connector from the South point. The START item is singular. // RETURN Stadia One incoming connector, No outgoing connectors. Must be at least 1 RETURN, can be more. // LABEL Stadia One or more incoming connectors (all to the same, Northmost point) // TEST Hexagon One or more incoming connectors (all to the same, Northmost point), Two outgoing connectors: True; False. // ACTION Rectangle One or more incoming connectors (all to the same, Northmost point); One outgoing connector, either from the South or East point. // // Notes:- // Hexagons are used as they are more space efficient than diamonds, actions or labels. // Actions can be a single step or a contiguous set of steps. // Connectors are not specified in the <xFlowchart> tag, but are inferred from the list of flowchart items and generated automatically.
public: hzString m_Text ; // Compiled text content hdsShape m_eShape ; // Either a stadium, rectangle or hexagon _fc_type m_eSType ; // Type of instruction uint16_t m_nLevel ; // Code level uint16_t m_Row ; // Incremented on next step uint16_t m_Col ; // Incremented on branch uint16_t m_nLines ; // Number of lines in text uint16_t m_actH ; // Actual height based on shape and text uint16_t m_actW ; // Actual width based on shape and text uint16_t m_effH ; // Effective height based all shapes in a linked row uint16_t m_Top ; // Actual top of image space uint16_t m_Bot ; // Actual bottom of image space uint16_t m_Lft ; // Actual left of image space uint16_t m_Rht ; // Actual right of image space uint16_t m_effW ; // Effective width based all shapes in a linked column uint16_t m_Xid ; // Statement id uint16_t m_LinkCtrl ; // Start of current control loop if applicable //uint16_t m_LinkSwitch ; // Start of current switch if applicable uint16_t m_LinkDown ; // Next item in line of execution uint16_t m_LinkTrue ; // First item of execution branch (on conditiion=true) uint16_t m_Resv ; // Reserved
_fc_item (void) { //m_LinkCtrl = m_LinkSwitch = m_LinkDown = m_LinkTrue = 0 ; m_LinkCtrl = m_LinkDown = m_LinkTrue = 0 ; m_eShape = HDSGRAPH_NULL ; m_eSType = STMT_NULL ; m_Xid = m_Row = m_Col = m_nLevel = m_actH = m_actW = m_effH = m_effW = m_Resv = m_Top = m_Bot = m_Lft = m_Rht = 0 ; } } ;
struct _fc_arg { // Container for graphic array, connector array and log channel
hzMapS <uint32_t,_fc_item*> m_mapItems ; // Map of flowchart items ordered by the IDs of the XML nodes that create them. hzSet <hzString> m_setLabels ; // Set of labels found hzArray <_fc_item*> m_arrItems ; // All flowchart statements (by order of incidence)
hzLogger* pLog ; // Logger const char* file ; // Filename bool m_bErr ; // Error state
_fc_arg () { pLog = 0 ; file = 0 ; m_bErr = false ; }
~_fc_arg () { uint32_t n ;
threadLog("Clearing base\n") ; for (n = 0 ; n < m_arrItems.Count() ; n++) { delete m_arrItems[n] ; } threadLog("based cleared\n") ; } } ;
static hzEcode _createFCItems (_fc_arg& a, hzXmlNode* pN) { // Start with the <xflowchart> tag and move through all subnodes. There are two passes, in the first pass the objectives are:- // // 1) Check that tags are legal, but not if they are in the correct context. For example, elseif is legal, but only as part of an if/elseif/else series. // 2) Create an item for every subnode that necessitate a flowchart shape. // // In the second pass the objectives are:- // // 1) Check that labels exist for goto commands // 2) Check that any elseif or else statements are part of an if/elseif/else series // 3) Check that case statements are part of a switch statement and that no case statements follow a default // 4) ... // // Arguments: 1) a Temporary flowchart container // 2) pN Initial XML node // // Returns: E_FORMAT Error in syntax // E_OK Operation successful
_hzfunc(__func__) ;
const hzDocXml* pHostDoc ; // Host document hzXmlNode* pN1 ; // Current XML node hzXmlNode* pN2 ; // Secondary XML node hzAttrset ai ; // Attribute iterator _fc_item* pItem ; // Temporary flowchart item hzChain T ; // For text aggregation hzString st ; // Statement object hzString obj ; // Statement object hzString typ ; // Statement type uint32_t nId ; // Start node id uint16_t nLevel ; // Level of <xflowchart> tag in document. All subnodes of <xflowchart> must be above this level hzEcode rc = E_OK ; // Return code
if (!pN) Fatal("No node supplied\n") ; if (!pN->NameEQ("xflowchart")) Fatal("File %s Line %d: Incorrect node (%s) supplied. Must be <xflowchart>\n", *pN->Filename(), pN->Line(), pN->txtName()) ;
pHostDoc = pN->GetHostDoc() ; nId = pN->GetUid() ; nLevel = pN->Level() ;
for (nId++, pN1 = pHostDoc->GetNode(nId) ; rc == E_OK && pN1 ; nId++, pN1 = pHostDoc->GetNode(nId)) { if (pN1->Level() <= nLevel) break ;
// <break>, <continue>, <else> and <default> don't produce items if (pN1->NameEQ("break")) continue ; if (pN1->NameEQ("continue")) continue ; if (pN1->NameEQ("else")) continue ; if (pN1->NameEQ("default")) continue ;
// All the below tags do produce an item if (!a.m_mapItems.Exists(pN1->GetUid())) { pItem = new _fc_item() ; pItem->m_nLevel = pN1->Level() - nLevel ; a.m_mapItems.Insert(pN1->GetUid(), pItem) ; a.pLog->Log("Inserted item: level %d uid %u line %d <%s>\n", pItem->m_nLevel, pN1->GetUid(), pN1->Line(), pN1->txtName()) ; }
// Get the tag attributes (if any) st.Clear() ; typ.Clear() ; obj.Clear() ; for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("stmt")) st = ai.Value() ; else if (ai.NameEQ("type")) typ = ai.Value() ; else if (ai.NameEQ("obj")) obj = ai.Value() ; else { rc = E_FORMAT ; a.pLog->Log("File %s Line %d: <%s> Bad param (%s=%s)\n", a.file, pN1->Line(), pN1->txtName(), ai.Name(), ai.Value()) ; } }
if (rc != E_OK) break ;
if (pN1->NameEQ("label")) { pItem->m_eSType = STMT_LABEL ; pItem->m_eShape = HDSGRAPH_STADIUM ; pItem->m_Text = obj ; a.m_setLabels.Insert(obj) ; continue ; } if (pN1->NameEQ("goto")) { pItem->m_eSType = STMT_GOTO ; pItem->m_eShape = HDSGRAPH_STADIUM ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("return")) { pItem->m_eSType = STMT_RETURN ; pItem->m_eShape = HDSGRAPH_STADIUM ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("exit")) { pItem->m_eSType = STMT_EXIT ; pItem->m_eShape = HDSGRAPH_STADIUM ; pItem->m_Text = obj ; continue ; }
if (pN1->NameEQ("bloc")) { // Read in an action block - a set of one or more statements that are merged into a single action box rectangle. <bloc> can only have subtags of <step>. pN2 = pN1->GetFirstChild() ; if (!pN2) { // Dead block so ignore and continue continue ; }
pItem->m_eSType = STMT_BLOC ; pItem->m_eShape = HDSGRAPH_RECT ;
// Recurse to handle CASE statements T.Clear() ; for (; !a.m_bErr && pN2 ; pN2 = pN2->Sibling()) { if (!pN2->NameEQ("step")) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <bloc> Illegal subtag <%s>, only <step> allowed\n", a.file, pN2->Line(), pN2->txtName()) ; break ; }
obj.Clear() ; for (ai = pN2 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("stmt")) st = ai.Value() ; else if (ai.NameEQ("type")) typ = ai.Value() ; else if (ai.NameEQ("obj")) obj = ai.Value() ; else { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <step> Bad param (%s=%s)\n", a.file, pN2->Line(), ai.Name(), ai.Value()) ; } }
if (a.m_bErr) break ;
if (!obj) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <step> No object\n", a.file, pN2->Line()) ; break ; }
if (T.Size()) T.AddByte(CHAR_NL) ; T << obj ; } continue ; }
if (pN1->NameEQ("step")) { pItem->m_eSType = STMT_STEP ; pItem->m_eShape = HDSGRAPH_RECT ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("switch")) { pItem->m_eSType = STMT_BRANCH_SWITCH ; pItem->m_eShape = HDSGRAPH_RECT ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("case")) { pItem->m_eSType = STMT_BRANCH_CASE ; pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("if")) { pItem->m_eSType = STMT_BRANCH_IF ; pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("elseif")) { pItem->m_eSType = STMT_BRANCH_ELSEIF ; pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("for")) { pItem->m_eSType = STMT_BRANCH_FOR ; pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("while")) { pItem->m_eSType = STMT_BRANCH_WHILE ; pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ; continue ; } if (pN1->NameEQ("dowhile")) { pItem->m_eSType = STMT_BRANCH_DOWHILE ; pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ; continue ; }
else { // Illegal tag or tag position rc = E_FORMAT ; a.pLog->Log("Illegal tag\n") ; break ; }
// Place item(s) in m_arrItems }
return rc ; }
static _fc_item* _readFCItem (_fc_arg& a, hzXmlNode* pN, uint32_t lnkControl, uint32_t lnkBeyond, uint32_t nLevel) { // Recursively read the flowchart XML as an intial pass. This builds an array of flowchart items, in the same order and at the same level as the progenitor XML tags. The items // are assigned ids and shapes, and some connectors. The branching follows the ... // // Arguments: 1) a Container of the statements array and logger // 2) pN The current XML node // 3) lnkControl Loop control item (if applicable) // 4) lnkBeyond Item beyond the current loop, switch or if/elseif/else series // 5) nLevel Code level // // Returns: Pointer to first item created by this call // // Notes:
_hzfunc("_readFCItem") ;
hzXmlNode* pN1 ; // Subtag probe hzXmlNode* pN2 ; // Subtag probe _fc_item* pFirst ; // First flowchart item issued by this call _fc_item* pLast ; // Flowchart item _fc_item* pItem ; // Flowchart item _fc_item* pCase ; // Case statement _fc_item* pBranch ; // Case statement hzAttrset ai ; // Attribute iterator hzChain T ; // For text aggregation hzString st ; // Statement object hzString obj ; // Statement object hzString typ ; // Statement type bool bDefault ; // Set when a <default> tag is encountered (ensures it is singular)
pLast = a.m_arrItems[a.m_arrItems.Count() - 1] ; pFirst = pItem = 0 ;
// Process tags for (pN1 = pN->GetFirstChild() ; !a.m_bErr && pN1 ; pN1 = pN1->Sibling()) { a.pLog->Log("File %s Line %d: Processing <%s> tag at level %d ctrl %u beyond %u\n", a.file, pN1->Line(), pN1->txtName(), nLevel, lnkControl, lnkBeyond) ;
if (pItem) pLast = pItem ;
if (pN1->NameEQ("break")) { if (!lnkControl) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <step> break outside control loop\n", a.file, pN1->Line()) ; break ; }
// No item for break. Link to the item beyond the current control loop, switch or if/elseif/else series if (pLast) pLast->m_LinkDown = lnkBeyond ; continue ; }
if (pN1->NameEQ("continue")) { if (!lnkControl) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <step> continue outside control loop\n", a.file, pN1->Line()) ; break ; }
// No item for continue. The link down is back to the loop control item if (pLast) pLast->m_LinkDown = lnkControl ; continue ; }
// The following tags produce an item pItem = a.m_mapItems[pN1->GetUid()] ; if (!pItem) { a.pLog->Log("ERROR: No item found for tag %s file %s line %d\n", pN1->txtName(), a.file, pN1->Line()) ; a.m_bErr = 1 ; break ; }
if (!pFirst) pFirst = pItem ; pItem->m_Xid = a.m_arrItems.Count() ; pItem->m_Col = pItem->m_nLevel = nLevel ;
if (pLast) { pLast->m_LinkDown = pItem->m_Xid ; threadLog("Set link past %d down %d\n", pLast->m_Xid, pLast->m_LinkDown) ; }
if (lnkBeyond) { //pItem->m_LinkDown = pControl->m_LinkDown ; //threadLog("Set link item %d control %d down %d\n", pItem->m_Xid, pControl->m_Xid, pControl->m_LinkDown) ; threadLog("Set link item %u control %u down %u\n", pItem->m_Xid, lnkControl, lnkBeyond) ; }
a.m_arrItems.Add(pItem) ;
// The following tags may have an object attrubute obj.Clear() ; for (ai = pN1 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("stmt")) st = ai.Value() ; else if (ai.NameEQ("type")) typ = ai.Value() ; else if (ai.NameEQ("obj")) obj = ai.Value() ; else { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <%s> Bad param (%s=%s)\n", a.file, pN1->Line(), pN1->txtName(), ai.Name(), ai.Value()) ; } }
if (a.m_bErr) break ;
if (pN1->NameEQ("label")) { // This will generate a stadium but in the line of execution, and with a link in and a link out. The label will generally appear after any goto that refers to it, thus // at the point of the goto, the item id of the label is unknown. if (!obj) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <%s> No object\n", a.file, pN1->Line()) ; break ; }
pItem->m_eShape = HDSGRAPH_STADIUM ; pItem->m_eSType = STMT_LABEL ; pItem->m_Text = obj ; continue ; }
if (pN1->NameEQ("goto")) { // Link from current shape to the label. It may be the label is not yet so this is recorded as a note if (!obj) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <%s> No object\n", a.file, pN1->Line()) ; break ; }
pItem->m_eShape = HDSGRAPH_STADIUM ; pItem->m_eSType = STMT_GOTO ; pItem->m_Text = obj ; continue ; }
if (pN1->NameEQ("return") || pN1->NameEQ("exit")) { if (pN1->NameEQ("return")) pItem->m_eSType = STMT_RETURN ; else pItem->m_eSType = STMT_EXIT ;
pItem->m_eShape = HDSGRAPH_STADIUM ; pItem->m_Text = obj ; continue ; }
// Action steps or blocks of steps if (pN1->NameEQ("bloc")) { // Read in an action block - a set of one or more statements that are merged into a single action box rectangle. <bloc> can only have subtags of <step>. pN2 = pN1->GetFirstChild() ; if (!pN2) { // Dead block so ignore and continue continue ; }
pItem->m_eSType = STMT_BLOC ; pItem->m_eShape = HDSGRAPH_RECT ;
// Recurse to handle CASE statements T.Clear() ; for (; !a.m_bErr && pN2 ; pN2 = pN2->Sibling()) { if (!pN2->NameEQ("step")) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <bloc> Illegal subtag <%s>, only <step> allowed\n", a.file, pN2->Line(), pN2->txtName()) ; break ; }
obj.Clear() ; for (ai = pN2 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("stmt")) st = ai.Value() ; else if (ai.NameEQ("type")) typ = ai.Value() ; else if (ai.NameEQ("obj")) obj = ai.Value() ; else { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <step> Bad param (%s=%s)\n", a.file, pN2->Line(), ai.Name(), ai.Value()) ; } }
if (a.m_bErr) break ;
if (!obj) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <step> No object\n", a.file, pN2->Line()) ; break ; }
if (T.Size()) T.AddByte(CHAR_NL) ; T << obj ; } pItem->m_Text = T ; continue ; }
if (pN1->NameEQ("step")) { pItem->m_eSType = STMT_STEP ; pItem->m_eShape = HDSGRAPH_RECT ; pItem->m_Text = obj ; continue ; }
/* ** Switch handler */
if (pN1->NameEQ("switch")) { // Expect CASE and DEFAULT to be at next level pN2 = pN1->GetFirstChild() ; if (!pN2) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <switch> has no subtags\n", a.file, pN2->Line()) ; break ; }
// Check that at next level, all subnodes are <case> except for the last which may be <default> for (bDefault = false ; !a.m_bErr && pN2 ; pN2 = pN2->Sibling()) { if (pN2->NameEQ("case")) continue ;
if (pN2->NameEQ("default")) { if (bDefault) { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <default> Duplicate default\n", a.file, pN2->Line()) ; break ; } bDefault = true ; continue ; }
a.m_bErr = true ; a.pLog->Log("File %s Line %d: <%s> Illegal <switch> subtag\n", a.file, pN2->Line(), pN2->txtName()) ; break ; }
// Commit the default pItem->m_eSType = STMT_BRANCH_SWITCH ; pItem->m_eShape = HDSGRAPH_RECT ; pItem->m_Text = obj ;
//currSwitch = pItem ;
// Handle CASE and DEFAULT for (pN2 = pN1->GetFirstChild() ; !a.m_bErr && pN2 ; pN2 = pN2->Sibling()) { obj.Clear() ; for (ai = pN2 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("stmt")) st = ai.Value() ; else if (ai.NameEQ("type")) typ = ai.Value() ; else if (ai.NameEQ("obj")) obj = ai.Value() ; else { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <%s> Bad param (%s=%s)\n", a.file, pN2->Line(), pN2->txtName(), ai.Name(), ai.Value()) ; } }
if (a.m_bErr) break ;
pCase = a.m_mapItems[pN2->GetUid()] ; if (!pCase) { a.pLog->Log("ERROR: No item found for tag %s file %s line %d\n", pN1->txtName(), a.file, pN1->Line()) ; a.m_bErr = 1 ; break ; } //pCase = new _fc_item() ; a.m_arrItems.Add(pCase) ; pCase->m_Xid = a.m_arrItems.Count() ; pCase->m_Col = pCase->m_nLevel = nLevel ; //pCase->m_LinkCtrl = pControl ? pControl->m_Xid : 0 ; pCase->m_eShape = HDSGRAPH_HEXAGON ;
if (pN2->NameEQ("case")) pCase->m_eSType = STMT_BRANCH_CASE ; else pCase->m_eSType = STMT_BRANCH_DEFAULT ;
pCase->m_Text = obj ; _readFCItem(a, pN2, lnkControl, lnkBeyond, nLevel+1) ; //pCase->m_LinkSwitch = a.m_arrItems.Count() ; } continue ; }
/* ** Handle IF, ELSEIF and ELSE */
if (pN1->NameEQ("if")) { // Note that an IF can optionally be followed by one or more ELSE-IF and/or an ELSE. For the benefit of actions, it is necessary to know where the series ends since a // connector will be needed from the action to the step beyond the IF series. This will not necessarily be within the set of steps being processed in this call.
pItem->m_eSType = STMT_BRANCH_IF ; pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ;
// Recurse to handle TRUE action pBranch = _readFCItem(a, pN1, lnkControl, lnkBeyond, nLevel+1) ; if (pBranch) pItem->m_LinkTrue = pBranch->m_Xid ; pItem->m_LinkDown = a.m_arrItems.Count() ; threadLog("Set IF link src %d true %d down %d\n", pItem->m_Xid, pItem->m_LinkTrue, pItem->m_LinkDown) ;
// Check for ELSE-IF and ELSE pN2 = pN1->Sibling() ; if (!pN2) continue ;
for (; !a.m_bErr && pN2 && pN2->NameEQ("elseif") ; pN2 = pN2->Sibling()) { pLast = pItem ;
pItem = a.m_mapItems[pN2->GetUid()] ; if (!pItem) { a.pLog->Log("ERROR: No item found for tag %s file %s line %d\n", pN1->txtName(), a.file, pN1->Line()) ; a.m_bErr = 1 ; break ; } //pItem = new _fc_item() ; a.m_arrItems.Add(pItem) ; pItem->m_Xid = a.m_arrItems.Count() ; pItem->m_Col = pItem->m_nLevel = nLevel ; //pItem->m_LinkCtrl = pControl ? pControl->m_Xid : 0 ; pItem->m_eSType = STMT_BRANCH_ELSEIF ; pItem->m_eShape = HDSGRAPH_HEXAGON ;
obj.Clear() ; for (ai = pN2 ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("stmt")) st = ai.Value() ; else if (ai.NameEQ("type")) typ = ai.Value() ; else if (ai.NameEQ("obj")) obj = ai.Value() ; else { a.m_bErr = true ; a.pLog->Log("File %s Line %d: <%s> Bad param (%s=%s)\n", a.file, pN2->Line(), pN2->txtName(), ai.Name(), ai.Value()) ; } }
if (a.m_bErr) break ;
pBranch = _readFCItem(a, pN2, lnkControl, lnkBeyond, nLevel+1) ; if (pBranch) pItem->m_LinkTrue = pBranch->m_Xid ; pItem->m_LinkDown = a.m_arrItems.Count() ; threadLog("Set ELSEIF link src %d true %d down %d\n", pItem->m_Xid, pItem->m_LinkTrue, pItem->m_LinkDown) ; pN1 = pN2 ; }
if (!a.m_bErr && pN2 && pN2->NameEQ("else")) { // Recurse to handle CASE statements pBranch = _readFCItem(a, pN2, lnkControl, lnkBeyond, nLevel) ; if (pBranch) pItem->m_LinkDown = pBranch->m_Xid ; pItem->m_LinkDown = a.m_arrItems.Count() ; threadLog("Set ELSE link src %d true %d down %d\n", pItem->m_Xid, pItem->m_LinkTrue, pItem->m_LinkDown) ; pN1 = pN2 ; } continue ; }
/* ** Handle FOR, WHILE and DOWHILE loops */
if (pN1->NameEQ("for") || pN1->NameEQ("while")) { // Put the condition test hexagon before the loop body (branch) if (pN1->NameEQ("for")) pItem->m_eSType = STMT_BRANCH_FOR ; else pItem->m_eSType = STMT_BRANCH_WHILE ;
pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ; pN2 = pN1->Sibling() ; if (pN2) { }
_readFCItem(a, pN1, pItem->m_Xid, lnkBeyond, nLevel+1) ; continue ; }
if (pN1->NameEQ("dowhile")) { // Place loop body before the condition test hexagon. This means the loop body is not at a higher level, i.e. it is a continuation of the execution line. The item ids // are intended to be in order of appearence in the flowchart diagram. So if the dowhile tag is the Nth tag in the flowchart, the first item in the do-while loop will // have an id of N and the condition test hexagon will have an id of N+X where X is the number of items in the do-while loop. The true link of the condition test item // is then set to N. pItem->m_eSType = STMT_BRANCH_DOWHILE ; pItem->m_eShape = HDSGRAPH_HEXAGON ; pItem->m_Text = obj ;
_readFCItem(a, pN1, pItem->m_Xid, lnkBeyond, nLevel) ; continue ; } }
return pFirst ; }
static void _calcFCDims (_fc_arg& a, uint32_t& maxX, uint32_t& maxY) { // Support function for _readFlowchart. Align midpoints of flowchart items and calculate total height and width. // // The actual width and height of a flowchart item is determined by the item text content. The actual rectangle the item will occupy will be larger as there must be separating // space, and of a margin sufficient to accomodate objective connectors. The horizontal and vertical midpoints of the items are aligned so that as many connectors as possible // are single vertical or horizontal lines. It is accepted that some connectors will comprise multiple lines, such as those resuming the main line of execution after a branch, // and those returning to the top of a loop. // // First the actual width and height of every item is determined, then the effective height and width of the items is calculated, then finally the actual top, bottom, left and // right coords are assigned. The effective width of items is that of the widest item in the applicable vertically descending series (line of execution), plus a space margin. // The effective height of items is that of the tallest item in a horizontal series (an execution branch). // // Arguments: a The flowchart paramenter container // maxX The total width of the flowchart (set by this function) // maxY The total height of the flowchart (set by this function) // // Returns: None
_hzfunc("_calcFCDims") ;
_fc_item* pItem ; // Current flowchart item _fc_item* pNext ; // Current flowchart item const char* i ; // Text iterator uint32_t maxH ; // Max height found in a horizontal series uint32_t maxW ; // Max width found in a vertical series uint32_t nRow ; // Must be in same row uint32_t nCol ; // Must be in same column uint32_t start ; // Item iterator uint32_t end ; // Count of items uint32_t n ; // Item iterator uint32_t count ; // Number in either a horizontal or vertical series uint32_t h_sofar ; // Height so far uint32_t h_next ; // Height of next row uint32_t h_mid ; // Height midpoint uint32_t w_mid ; // Height midpoint uint32_t lenLine ; // Length of this line uint32_t lenMax ; // Longest line encountered
end = a.m_arrItems.Count() - 1 ;
/* ** Calculate actual height and width based on text content. */
for (n = 0 ; n <= end ; n++) { pItem = a.m_arrItems[n] ;
lenMax = 10 ; pItem->m_nLines = 1 ; if (pItem->m_Text) { for (lenLine = 0, i = *pItem->m_Text ; *i ; i++) { if (*i == CHAR_NL) { pItem->m_nLines++ ; if (lenLine > lenMax) lenMax = lenLine ; lenLine = 0 ; continue ; } lenLine++ ; }
if (lenLine > lenMax) lenMax = lenLine ; } pItem->m_actH = 16 * (pItem->m_nLines+1) ; pItem->m_actW = 8 * lenMax ;
if (pItem->m_eShape == HDSGRAPH_HEXAGON) pItem->m_actW += (pItem->m_actH/2) ; }
/* ** Set height, top and bottom */
nCol = nRow = 0 ; for (start = 1 ; start <= end ; start++) { pItem = a.m_arrItems[start] ;
if (pItem->m_Col > nCol) pItem->m_Row = nRow ; else pItem->m_Row = ++nRow ;
nCol = pItem->m_Col ; }
nCol = nRow = 0 ; for (start = 0 ; start <= end ; start++) { pItem = a.m_arrItems[start] ; if (pItem->m_effW == 0) { // Set the column - and then scan all items at the column or above, terminating either at the end or upon ecountering an item of lower colum pItem->m_effW = pItem->m_actW ; nCol = pItem->m_Col ; maxW = pItem->m_actW ; count = 0 ;
for (n = start + 1 ; n < end ; n++) { pNext = a.m_arrItems[n] ; if (pNext->m_Col < nCol) break ; if (pNext->m_Col > nCol) continue ;
if (pNext->m_actW > maxW) maxW = pNext->m_actW ; pNext->m_effW = pNext->m_actW ; count++ ; }
if (count) { // Revisit the items and set the m_effW to maxW for (n = start ; n < end ; n++) { pNext = a.m_arrItems[n] ; if (pNext->m_Col < nCol) break ; if (pNext->m_Col > nCol) continue ;
pItem->m_effW = maxW ; } } } }
nCol = nRow = 0 ; for (start = 0 ; start <= end ; start++) { pItem = a.m_arrItems[start] ;
if (pItem->m_effH == 0) { // If we are on a horizontal series, establish max height for the series maxH = pItem->m_actH ; count = 0 ; nRow = pItem->m_Row ;
for (n = start + 1 ; n < end ; n++) { pNext = a.m_arrItems[n] ;
if (pNext->m_Row > nRow) break ;
if (pNext->m_actH > maxH) maxH = pNext->m_actH ; count++ ; }
if (!count) pItem->m_effH = pItem->m_actH ; else { // Horizontal series established. Set effective height for whole series for (n = start ; count ; count--, n++) { pItem = a.m_arrItems[n] ; pItem->m_effH = maxH ; } } } }
h_sofar = 30 ; h_next = 0 ; nRow = 0 ; for (start = 0 ; start <= end ; start++) { pItem = a.m_arrItems[start] ;
if (nRow < pItem->m_Row) { nRow++ ; h_sofar += h_next ; h_sofar += 30 ; }
h_mid = h_sofar + (pItem->m_effH/2) ; pItem->m_Top = h_mid - (pItem->m_actH/2) ; pItem->m_Bot = h_mid + (pItem->m_actH/2) ; h_next = pItem->m_effH ; }
/* ** Set width, left and right */
uint16_t cols[16] ;
memset(cols, 0, 32) ;
for (nCol = start = 0 ; start <= end ; start++) { pItem = a.m_arrItems[start] ; nCol = pItem->m_Col ;
if (nCol > 0) cols[nCol] = cols[nCol-1] + pItem->m_effW ; else cols[nCol] = pItem->m_effW ;
w_mid = ((nCol+1)*30) + cols[nCol] - (pItem->m_effW/2) ; pItem->m_Lft = w_mid - (pItem->m_actW/2) ; pItem->m_Rht = w_mid + (pItem->m_actW/2) ; }
// Set height and width of flowchart maxX = maxY = 0 ; for (start = 0 ; start <= end ; start++) { pItem = a.m_arrItems[start] ; threadLog("doing item %d of %d: %s Row %02d Col %02d L %d W %03d/%03d H %03d/%03d top %03d bot %03d lft %03d rht %03d %s\n", start, end, pItem->m_eShape == HDSGRAPH_STADIUM ? "STA" : pItem->m_eShape == HDSGRAPH_HEXAGON ? "HEX" : pItem->m_eShape == HDSGRAPH_RECT ? "REC" : "NUL", pItem->m_Row, pItem->m_Col, pItem->m_nLevel, pItem->m_actW, pItem->m_effW, pItem->m_actH, pItem->m_effH, pItem->m_Top, pItem->m_Bot, pItem->m_Lft, pItem->m_Rht, *pItem->m_Text) ;
if (pItem->m_Bot > maxY) maxY = pItem->m_Bot ; if (pItem->m_Rht > maxX) maxX = pItem->m_Rht ; }
maxY += 60 ; maxX += 60 ; }
hdsVE* hdsApp::_readFlowchart (hzXmlNode* pN) { // Read in flowchart components. // // Argument: pN The current XML node expected to be a <xdiagram> tag // // Returns: Pointer to diagram visual entity
_hzfunc("hdsApp::_readFlowchart") ;
_fc_arg base ; // All flowchart graphics, connectors and other info _fc_item* pItem ; // Current flowchart item _fc_item* pCtrl ; // Current control flowchart item hdsText TX ; // Diagram Text component hzAttrset ai ; // Attribute iterator hzNumPair np ; // Row height entry hdsGraphic fcG ; // Flowchart start stadium hdsConnector* pConn ; // Flowchart connector hdsFlowchart* pFlow ; // This flowchart hdsGraphic* pG ; // Current flowchart component hdsVE* thisVE ; // Visible entity/active entity hzString cfg ; // Config source file uint32_t n ; // Graphic iterator uint32_t Xmax ; // Max implied width uint32_t Ymax ; // Max implied width hzEcode rc = E_OK ; // Return code
// Check node if (!pN) Fatal("No node supplied\n") ; if (!pN->NameEQ("xflowchart")) Fatal("File %s Line %d: Incorrect node (%s) supplied. Must be <xflowchart>\n", *pN->Filename(), pN->Line(), pN->txtName()) ;
// Create flowchart visible entity thisVE = pFlow = new hdsFlowchart(this) ; cfg = pN->Filename() ; thisVE->m_Line = pN->Line() ; thisVE->m_Indent = pN->Level() ;
pFlow->m_BgColor = 0xc0c0ff ; pFlow->m_ColorLine = 0 ; pFlow->m_VSpace = pFlow->m_HSpace = 50 ; pFlow->m_Font = "10px Arial" ;
if (!pN->GetFirstChild()) return pFlow ;
// Get diagram params for (ai = pN ; ai.Valid() ; ai.Advance()) { if (ai.NameEQ("id")) pFlow->m_Id = ai.Value() ; else if (ai.NameEQ("header")) pFlow->m_Header = ai.Value() ; else if (ai.NameEQ("footer")) pFlow->m_Footer = ai.Value() ; else if (ai.NameEQ("bgcolor")) IsHexnum(pFlow->m_BgColor, ai.Value()) ; else if (ai.NameEQ("fgcolor")) IsHexnum(pFlow->m_FgColor, ai.Value()) ; else if (ai.NameEQ("linecolor")) IsHexnum(pFlow->m_ColorLine, ai.Value()) ; else if (ai.NameEQ("termcolor")) IsHexnum(pFlow->m_ColorTerm, ai.Value()) ; else if (ai.NameEQ("testcolor")) IsHexnum(pFlow->m_ColorTest, ai.Value()) ; else if (ai.NameEQ("proccolor")) IsHexnum(pFlow->m_ColorProc, ai.Value()) ; else if (ai.NameEQ("vspace")) pFlow->m_VSpace = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("hspace")) pFlow->m_HSpace = ai.Value() ? atoi(ai.Value()) : 0 ; else if (ai.NameEQ("font")) pFlow->m_Font = ai.Value() ; else { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xflowchart> Bad param (%s=%s)\n", *cfg, pN->Line(), ai.Name(), ai.Value()) ; } }
// Check values if (!pFlow->m_Id) { rc = E_SYNTAX ; m_pLog->Log("File %s Line %d: <xflowchart> No id\n", *cfg, pN->Line()) ; }
if (rc != E_OK) return pFlow ;
threadLog("Doing flowchart %s\n", *pFlow->m_Id) ;
// Init flowchart base params base.pLog = m_pLog ; base.file = pN->Filename() ; base.m_bErr = false ;
// Add the START stadium pItem = new _fc_item() ; base.m_arrItems.Add(pItem) ; pItem->m_eShape = HDSGRAPH_STADIUM ; pItem->m_eSType = STMT_LABEL ; pItem->m_Xid = 0 ; pItem->m_Col = pItem->m_nLevel = 0 ; pItem->m_Row = 0 ; pItem->m_Text = "START" ;
// Recursive read of flowchart items _createFCItems(base, pN) ; _readFCItem(base, pN, 0, 0, 0) ;
// Reject flowcharts with less than 3 items if (base.m_arrItems.Count() < 3) return pFlow ;
// Calculate dimensions of flowchart items and whole flowchart _calcFCDims(base, Xmax, Ymax) ;
// Count up number of graphics and connectors and assign graphic ids to the items pFlow->m_nShapes = base.m_arrItems.Count() ; pFlow->m_pShapes = new hdsGraphic[pFlow->m_nShapes] ;
threadLog("Copying %d shapes\n", base.m_arrItems.Count()) ;
for (n = 0 ; n < base.m_arrItems.Count() ; n++) { pItem = base.m_arrItems[n] ; if (pItem->m_Xid != n) threadLog("Warning - case A item id %d, n %d\n", pItem->m_Xid, n) ;
pG = pFlow->m_pShapes + n ;
pG->m_eShape = pItem->m_eShape ; pG->m_Text = pItem->m_Text ; pG->m_Top = pItem->m_Top ; pG->m_Bot = pItem->m_Bot ; pG->m_Lft = pItem->m_Lft ; pG->m_Rht = pItem->m_Rht ; pG->m_Width = pG->m_Rht - pG->m_Lft ; pG->m_Height = pG->m_Bot - pG->m_Top ; pG->m_Id = n ; pG->m_ColorLine = pFlow->m_ColorLine ; pG->m_Font = pFlow->m_Font ;
switch (pG->m_eShape) { case HDSGRAPH_STADIUM: pG->m_ColorFill = pFlow->m_ColorTerm ; break ; case HDSGRAPH_RECT: pG->m_ColorFill = pFlow->m_ColorProc ; break ; case HDSGRAPH_HEXAGON: pG->m_ColorFill = pFlow->m_ColorTest ; break ; default: threadLog("NULL GRAPHIC (%d)\n", n) ; pG->m_eShape = HDSGRAPH_HEXAGON ; pG->m_ColorLine = 0xff0000 ; break ; } } threadLog("DONE Copying shapes\n") ;
// Assign connectors pFlow->m_nConnects = 0 ; for (n = 0 ; n < base.m_arrItems.Count() ; n++) { pItem = base.m_arrItems[n] ; if (pItem->m_Xid != n) threadLog("Warning - case B item id %d, n %d\n", pItem->m_Xid, n) ;
if (pItem->m_LinkCtrl >= base.m_arrItems.Count()) threadLog("ERROR: Link %u has ctrl of %u\n", pItem->m_Xid, pItem->m_LinkCtrl) ; if (pItem->m_LinkDown >= base.m_arrItems.Count()) threadLog("ERROR: Link %u has down of %u\n", pItem->m_Xid, pItem->m_LinkDown) ; if (pItem->m_LinkTrue >= base.m_arrItems.Count()) threadLog("ERROR: Link %u has true of %u\n", pItem->m_Xid, pItem->m_LinkTrue) ;
if (pItem->m_LinkDown || pItem->m_LinkCtrl) pFlow->m_nConnects++ ; if (pItem->m_LinkTrue) pFlow->m_nConnects++ ; } pFlow->m_pConn = new hdsConnector[pFlow->m_nConnects] ;
for (n = 0 ; n < base.m_arrItems.Count() ; n++) { pItem = base.m_arrItems[n] ; if (!pItem) threadLog("No item %d\n", n) ;
if (pItem->m_Xid != n) threadLog("Warning - case C item id %d, n %d\n", pItem->m_Xid, n) ;
pConn = pFlow->m_pConn ; pConn->m_Origin = pItem->m_Xid ;
if (!pItem->m_LinkDown) { if (pItem->m_LinkCtrl) { pCtrl = base.m_arrItems[pItem->m_LinkCtrl] ; if (pCtrl) pItem->m_LinkDown = pCtrl->m_LinkDown ; else threadLog("Error - NULL CONTROL %d\n", pItem->m_Xid) ; } }
if (pItem->m_LinkDown) { pConn->m_Target = pItem->m_LinkDown ; pConn++ ; } else { threadLog("ERROR item %d no link down\n", pItem->m_Xid) ; }
if (pItem->m_LinkTrue) { if (pItem->m_LinkTrue >= base.m_arrItems.Count()) threadLog("Error - link true exceeds range (%d)\n", pItem->m_Xid) ; else { pConn->m_Origin = pItem->m_Xid ; pConn->m_Target = pItem->m_LinkTrue ; pConn++ ; } } else { if (pItem->m_eSType == STMT_BRANCH_IF) threadLog("ERROR IF without a link TRUE\n") ; if (pItem->m_eSType == STMT_BRANCH_ELSEIF) threadLog("ERROR ELSEIF without a link TRUE\n") ; } }
pFlow->m_Width = Xmax ; pFlow->m_Height = Ymax ;
m_pLog->Log("Declared xflowchart parmas X %d Y %d\n", Xmax, Ymax) ; return thisVE ; }