diff options
| author | Felix Morgner <felix.morgner@gmail.com> | 2026-08-24 11:16:07 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@gmail.com> | 2026-08-24 11:16:07 +0200 |
| commit | c068f22329d5cc722622a2183bbb22eef2093df7 (patch) | |
| tree | 12d56c1aede67988a55e241364606bfbb4dba933 /tool_src/QvLib/QvNode.cpp | |
| download | openparsec-c068f22329d5cc722622a2183bbb22eef2093df7.tar.xz openparsec-c068f22329d5cc722622a2183bbb22eef2093df7.zip | |
Diffstat (limited to 'tool_src/QvLib/QvNode.cpp')
| -rw-r--r-- | tool_src/QvLib/QvNode.cpp | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/tool_src/QvLib/QvNode.cpp b/tool_src/QvLib/QvNode.cpp new file mode 100644 index 0000000..40fad6b --- /dev/null +++ b/tool_src/QvLib/QvNode.cpp @@ -0,0 +1,381 @@ +#include <ctype.h> +#include <QvDB.h> +#include <QvInput.h> +#include <QvDebugError.h> +#include <QvReadError.h> +#include <QvField.h> +#include <QvFieldData.h> +#include <QvNodes.h> +#include <QvUnknownNode.h> + +// The global name dictionary +QvDict *QvNode::nameDict = NULL; + +// Syntax for writing instances to files +#define OPEN_BRACE '{' +#define CLOSE_BRACE '}' +#define DEFINITION_KEYWORD "DEF" +#define REFERENCE_KEYWORD "USE" +#define NULL_KEYWORD "NULL" + +void +QvNode::init() +{ + if (nameDict == NULL) + nameDict = new QvDict; +} + +QvNode::QvNode() +{ + objName = new QvName(""); +} + +QvNode::~QvNode() +{ + if (! !(*objName)) + removeName(this, objName->getString()); + delete objName; +} + +const QvName & +QvNode::getName() const +{ + return *objName; +} + +void +QvNode::setName(const QvName &newName) +{ + if (! !(*objName)) { + removeName(this, objName->getString()); + } + delete objName; + + const char *str = newName.getString(); + QvBool isBad = 0; + + if (newName.getLength() > 0 && + !QvName::isNodeNameStartChar(str[0])) isBad = TRUE; + + int i; + for (i = 1; i < newName.getLength() && !isBad; i++) { + isBad = !QvName::isNodeNameChar(str[i]); + } + + if (isBad) { + QvString goodString; + + if (!QvName::isNodeNameStartChar(str[0])) { + goodString += "_"; + } + for (i = 0; i < newName.getLength(); i++) { + char temp[2]; + temp[0] = str[i]; temp[1] = '\0'; + if (!QvName::isNodeNameChar(str[i])) + goodString += "_"; + else + goodString += temp; + } +#ifdef DEBUG + QvDebugError::post("QvNode::setName", "Bad characters in" + " name '%s'. Replacing with name '%s'", + str, goodString.getString()); +#endif + objName = new QvName(goodString); + } + else { + objName = new QvName(newName); + } + if (! !(*objName)) { + addName(this, objName->getString()); + } +} + +void +QvNode::addName(QvNode *b, const char *name) +{ + QvPList *list; + void *t; + if (!nameDict->find((u_long)name, t)) { + list = new QvPList; + nameDict->enter((u_long)name, list); + } + else { + list = (QvPList *)t; + } + + list->append(b); +} + +void +QvNode::removeName(QvNode *b, const char *name) +{ + QvPList *list; + QvBool found; + void *t; + int i; + + found = nameDict->find((u_long) name, t); + + if (found) { + list = (QvPList *) t; + i = list->find(b); + + if (i < 0) + found = FALSE; + + else + list->remove(i); + } + + if (! found) + QvDebugError::post("QvNode::removeName", + "Name \"%s\" (node %x) is not in dictionary", + name, b); +} + +QvBool +QvNode::read(QvInput *in, QvNode *&node) +{ + QvBool ret; + QvName name; + + if (! in->read(name, TRUE)) { + node = NULL; + ret = in->headerOk; + } + + else if (! name || name == NULL_KEYWORD) { + node = NULL; + ret = TRUE; + } + + else if (name == REFERENCE_KEYWORD) { + node = readReference(in); + ret = (node != NULL); + } + + else + ret = readNode(in, name, node); + + return ret; +} + +QvBool +QvNode::readInstance(QvInput *in) +{ + QvName typeString; + QvFieldData *fieldData = getFieldData(); + + if (in->read(typeString, TRUE)) { + if (typeString == "fields") { + if (! fieldData->readFieldTypes(in, this)) { + QvReadError::post(in, "Bad field specifications for node"); + return FALSE; + } + } + else + in->putBack(typeString.getString()); + } + + if (! fieldData->read(in, this)) + return FALSE; + + return TRUE; +} + +QvNode * +QvNode::readReference(QvInput *in) +{ + QvName refName; + QvNode *node; + + if (! in->read(refName, FALSE)) { + QvReadError::post(in, "Premature end of file after " + REFERENCE_KEYWORD); + node = NULL; + } + + else if ((node = in->findReference(refName)) == NULL) + QvReadError::post(in, "Unknown reference \"%s\"", + refName.getString()); + + return node; +} + +QvBool +QvNode::readNode(QvInput *in, QvName &className, QvNode *&node) +{ + QvBool gotChar; + QvName refName; + char c; + QvBool ret = TRUE, flush = FALSE; + + node = NULL; + + if (className == DEFINITION_KEYWORD) { + if (! in->read(refName, FALSE) || ! in->read(className, TRUE)) { + QvReadError::post(in, "Premature end of file after " + DEFINITION_KEYWORD); + ret = FALSE; + } + + if (! refName) { + QvReadError::post(in, "No name given after ", DEFINITION_KEYWORD); + ret = FALSE; + } + + if (! className) { + QvReadError::post(in, "Invalid definition of %s", + refName.getString()); + ret = FALSE; + } + } + + if (ret) { + + if (! (gotChar = in->read(c)) || c != OPEN_BRACE) { + if (gotChar) + QvReadError::post(in, "Expected '%c'; got '%c'", + OPEN_BRACE, c); + else + QvReadError::post(in, "Expected '%c'; got EOF", OPEN_BRACE); + ret = FALSE; + } + + else { + ret = readNodeInstance(in, className, refName, node); + + if (! ret) + flush = TRUE; + + else if (! (gotChar = in->read(c)) || c != CLOSE_BRACE) { + if (gotChar) + QvReadError::post(in, "Expected '%c'; got '%c'", + CLOSE_BRACE, c); + else + QvReadError::post(in, "Expected '%c'; got EOF", + CLOSE_BRACE); + ret = FALSE; + } + } + } + + if (! ret && flush) + flushInput(in); + + return ret; +} + +QvBool +QvNode::readNodeInstance(QvInput *in, const QvName &className, + const QvName &refName, QvNode *&node) +{ + node = createInstance(in, className); + if (node == NULL) + return FALSE; + + if (! (! refName)) + in->addReference(refName, node); + + return node->readInstance(in); +} + +QvNode * +QvNode::createInstance(QvInput *in, const QvName &className) +{ + QvNode *instance; + QvString unknownString; + + instance = createInstanceFromName(className); + + if (instance == NULL) { + + if (! in->read(unknownString) || unknownString != "fields") { + QvReadError::post(in, "Unknown class \"%s\"", + className.getString()); + return NULL; + } + + else if (unknownString == "fields") { + QvUnknownNode *tmpNode = new QvUnknownNode; + tmpNode->setClassName(className.getString()); + instance = tmpNode; + in->putBack(unknownString.getString()); + } + } + + return instance; +} + +QvNode * +QvNode::createInstanceFromName(const QvName &className) +{ +#define TRY_CLASS(name, class) \ + else if (className == name) \ + inst = new class + + QvNode *inst = NULL; + + if (0) ; // So "else" works in first TRY_CLASS + TRY_CLASS("Cone", QvCone); + TRY_CLASS("Coordinate3", QvCoordinate3); + TRY_CLASS("Cube", QvCube); + TRY_CLASS("Cylinder", QvCylinder); + TRY_CLASS("DirectionalLight", QvDirectionalLight); + TRY_CLASS("Group", QvGroup); + TRY_CLASS("IndexedFaceSet", QvIndexedFaceSet); + TRY_CLASS("IndexedLineSet", QvIndexedLineSet); + TRY_CLASS("Info", QvInfo); + TRY_CLASS("LevelOfDetail", QvLevelOfDetail); + TRY_CLASS("Material", QvMaterial); + TRY_CLASS("MaterialBinding", QvMaterialBinding); + TRY_CLASS("MatrixTransform", QvMatrixTransform); + TRY_CLASS("Normal", QvNormal); + TRY_CLASS("NormalBinding", QvNormalBinding); + TRY_CLASS("OrthographicCamera", QvOrthographicCamera); + TRY_CLASS("PerspectiveCamera", QvPerspectiveCamera); + TRY_CLASS("PointLight", QvPointLight); + TRY_CLASS("PointSet", QvPointSet); + TRY_CLASS("Rotation", QvRotation); + TRY_CLASS("Scale", QvScale); + TRY_CLASS("Separator", QvSeparator); + TRY_CLASS("ShapeHints", QvShapeHints); + TRY_CLASS("Sphere", QvSphere); + TRY_CLASS("SpotLight", QvSpotLight); + TRY_CLASS("Switch", QvSwitch); + TRY_CLASS("Texture2", QvTexture2); + TRY_CLASS("Texture2Transform", QvTexture2Transform); + TRY_CLASS("TextureCoordinate2", QvTextureCoordinate2); + TRY_CLASS("Transform", QvTransform); + TRY_CLASS("TransformSeparator", QvTransformSeparator); + TRY_CLASS("Translation", QvTranslation); + TRY_CLASS("WWWAnchor", QvWWWAnchor); + TRY_CLASS("WWWInline", QvWWWInline); + + return inst; + +#undef TRY_CLASS +} + +void +QvNode::flushInput(QvInput *in) +{ + int nestLevel = 1; + char c; + + while (nestLevel > 0 && in->get(c)) { + + if (c == CLOSE_BRACE) + nestLevel--; + + else if (c == OPEN_BRACE) + nestLevel++; + } +} + +#undef OPEN_BRACE +#undef CLOSE_BRACE +#undef DEFINITION_KEYWORD +#undef REFERENCE_KEYWORD +#undef NULL_KEYWORD |
